# πŸ—οΈ Guacamole + Python ASGI Remote Access Architecture ## Overview This document outlines the proposed architecture for replacing the problematic 38MB WASM SSH console with an awesome, secure, and maintainable remote access solution using Apache Guacamole, Python ASGI backend, and a custom SPA frontend. **Heady Remote Access** - Because VPN management should be strategic, not scary! 🀠 ## 🎯 Architecture Goals ### Awesome Security - **Server-side connections**: All SSH/RDP/VNC handled on trusted infrastructure - **No client-side crypto**: Private keys never leave the server - **Role-based access control**: Integrate with existing OIDC role mapping - **Audit trails**: Complete session logging and recording capabilities - **Standard protocols**: Use proven, auditable technologies ### Awesome Performance & UX - **Lightweight frontend**: <1MB custom SPA vs 38MB WASM blob - **Real-time communication**: WebSocket-based terminal streaming - **Responsive design**: Mobile-friendly remote access interface - **Fast deployment**: Standard containerization, no Go build complexity ## 🏒 System Architecture ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Custom SPA │◄──►│ Python ASGI │◄──►│ Guacd Daemon β”‚ β”‚ (TypeScript) β”‚ β”‚ Backend β”‚ β”‚ (C/Docker) β”‚ β”‚ β”‚ β”‚ (FastAPI/Sanic) β”‚ β”‚ β”‚ β”‚ β€’ Terminal UI β”‚ β”‚ β€’ WebSocket β”‚ β”‚ β€’ SSH Client β”‚ β”‚ β€’ Session Mgmt β”‚ β”‚ β€’ Auth Checks β”‚ β”‚ β€’ RDP Client β”‚ β”‚ β€’ Role Display β”‚ β”‚ β€’ Protocol Bridgeβ”‚ β”‚ β€’ VNC Client β”‚ β”‚ β€’ File Transfer β”‚ β”‚ β€’ Session Logs β”‚ β”‚ β€’ Protocol Mux β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ └──────────────►│ Headplane β”‚β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ Main App β”‚ β”‚ β€’ OIDC Roles β”‚ β”‚ β€’ User Sessions β”‚ β”‚ β€’ Node Discovery β”‚ β”‚ β€’ Audit Logs β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ## πŸ”§ Component Design ### 1. Apache Guacamole Daemon (guacd) **Role**: Protocol handling and connection management ```dockerfile # Lightweight guacd container FROM guacamole/guacd:latest EXPOSE 4822 # Custom configuration for Headplane integration COPY guacd.conf /etc/guacamole/ ``` **Configuration**: - **SSH connections**: Direct to Tailscale nodes - **Connection pooling**: Efficient resource usage - **Protocol support**: SSH, RDP, VNC as needed - **Security**: Connection isolation and timeout handling ### 2. Python ASGI Backend **Role**: WebSocket bridge and authentication/authorization ```python # Core FastAPI application from fastapi import FastAPI, WebSocket, Depends from fastapi.security import HTTPBearer import asyncio import websockets app = FastAPI() class GuacamoleProxy: def __init__(self): self.guacd_host = "guacd" self.guacd_port = 4822 async def create_connection(self, protocol: str, params: dict): """Create guacamole connection with protocol-specific params""" pass async def stream_session(self, websocket: WebSocket, connection_id: str): """Proxy guacamole protocol over WebSocket""" pass @app.websocket("/terminal/{node_name}") async def terminal_endpoint( websocket: WebSocket, node_name: str, user: User = Depends(get_current_user) ): # Check role-based permissions permissions = get_terminal_permissions(user.groups) if not permissions['ssh']: await websocket.close(4003, "Insufficient permissions") return # Create guacamole connection proxy = GuacamoleProxy() connection = await proxy.create_connection("ssh", { "hostname": node_name, "username": user.preferred_username, "port": "22" }) # Stream session await proxy.stream_session(websocket, connection.id) ``` **Key Features**: - **Authentication**: Validate user sessions from Headplane - **Authorization**: Role-based access control using OIDC groups - **WebSocket proxy**: Bridge between SPA and guacd protocol - **Session management**: Track active connections and resources - **Audit logging**: Record all connection attempts and activities ### 3. Custom SPA Frontend **Role**: User interface and terminal rendering ```typescript // Terminal component using xterm.js import { Terminal } from 'xterm'; import { FitAddon } from 'xterm-addon-fit'; import { WebLinksAddon } from 'xterm-addon-web-links'; class GuacamoleTerminal { private terminal: Terminal; private socket: WebSocket; constructor(private nodeHostname: string) { this.terminal = new Terminal({ theme: { background: '#1a1a1a' }, fontSize: 14, fontFamily: 'JetBrains Mono, monospace' }); this.terminal.loadAddon(new FitAddon()); this.terminal.loadAddon(new WebLinksAddon()); } async connect() { const wsUrl = `wss://${location.host}/api/terminal/${this.nodeHostname}`; this.socket = new WebSocket(wsUrl); this.socket.onmessage = (event) => { this.terminal.write(event.data); }; this.terminal.onData((data) => { this.socket.send(data); }); } } ``` **UI Features**: - **Modern terminal**: xterm.js with full VT100 compatibility - **Responsive design**: Works on desktop, tablet, mobile - **File transfer**: Drag-and-drop file uploads via SFTP - **Session tabs**: Multiple concurrent connections - **Role indicators**: Clear display of user permissions ## πŸ” OIDC Role Integration ### Permission Matrix Integration with existing OIDC role mapping system: ```python def get_terminal_permissions(user_groups: list[str]) -> dict: """Get terminal access permissions based on OIDC groups""" role = map_oidc_groups_to_role(user_groups) # Use existing mapping permissions = { 'owner': { 'ssh': True, 'rdp': True, 'vnc': True, 'file_transfer': True, 'session_recording': False, # Owners don't need to be recorded 'connection_sharing': True, 'admin_nodes': True }, 'admin': { 'ssh': True, 'rdp': True, 'vnc': True, 'file_transfer': True, 'session_recording': True, # Record admin sessions 'connection_sharing': True, 'admin_nodes': True }, 'network_admin': { 'ssh': True, 'rdp': False, 'vnc': False, 'file_transfer': True, 'session_recording': True, 'connection_sharing': False, 'admin_nodes': False # Only access to regular nodes }, 'it_admin': { 'ssh': True, 'rdp': True, # IT needs RDP for Windows support 'vnc': False, 'file_transfer': True, 'session_recording': True, 'connection_sharing': False, 'admin_nodes': False }, 'auditor': { 'ssh': False, # Read-only access 'rdp': False, 'vnc': False, 'file_transfer': False, 'session_recording': False, 'connection_sharing': False, 'admin_nodes': False, 'view_sessions': True, # Can view active sessions 'replay_sessions': True # Can replay recorded sessions }, 'member': { 'ssh': False, 'rdp': False, 'vnc': False, 'file_transfer': False, 'session_recording': False, 'connection_sharing': False, 'admin_nodes': False } } return permissions.get(role, permissions['member']) ``` ### Environment Variable Configuration Extend existing environment variable system for remote access: ```bash # Existing OIDC role mapping HEADPLANE_ADMIN_GROUPS="vp,director,manager" HEADPLANE_NETWORK_ADMIN_GROUPS="devops,network,sre" # New remote access permissions HEADPLANE_SSH_ALLOWED_GROUPS="admin,network_admin,it_admin" HEADPLANE_RDP_ALLOWED_GROUPS="admin,it_admin" HEADPLANE_SESSION_RECORDING_REQUIRED="admin,network_admin,it_admin" HEADPLANE_FILE_TRANSFER_ALLOWED="admin,network_admin,it_admin" ``` ## πŸš€ Deployment Strategy ### Docker Compose Setup ```yaml services: headplane: # Existing Headplane service environment: - HEADPLANE_REMOTE_ACCESS_ENABLED=true - HEADPLANE_GUACD_HOST=guacd guacd: image: guacamole/guacd:latest container_name: headplane-guacd restart: unless-stopped volumes: - ./guacd.conf:/etc/guacamole/guacd.conf:ro expose: - "4822" remote-access-backend: build: ./remote-access container_name: headplane-remote-access restart: unless-stopped environment: - GUACD_HOST=guacd - GUACD_PORT=4822 - HEADPLANE_API_URL=http://headplane:3000 expose: - "8000" depends_on: - guacd - headplane ``` ### Integration Points - **Authentication**: Validate JWT tokens from Headplane - **Node discovery**: Query Headplane API for available Tailscale nodes - **Audit integration**: Send session logs to Headplane audit system - **Role sync**: Real-time role updates from OIDC changes ## πŸ“Š Benefits Over WASM Approach | **Metric** | **38MB WASM SSH** | **Guacamole Architecture** | |------------|-------------------|-----------------------------| | **Bundle Size** | 38MB | <1MB SPA | | **Security Model** | Client-side crypto | Server-side only | | **Build Complexity** | Go toolchain + deps | Standard containers | | **Protocol Support** | SSH only | SSH + RDP + VNC | | **Audit Capability** | Limited | Full session recording | | **Mobile Support** | Poor | Responsive design | | **Enterprise Ready** | No | Yes | | **Maintenance** | High complexity | Standard stack | ## 🎯 Implementation Phases ### Phase 1: Core Infrastructure - [ ] Deploy guacd container - [ ] Build Python ASGI WebSocket proxy - [ ] Create basic terminal SPA interface - [ ] Integrate with existing OIDC authentication ### Phase 2: Role Integration - [ ] Implement permission matrix with OIDC roles - [ ] Add environment variable configuration - [ ] Create role-based UI elements - [ ] Add audit logging for connections ### Phase 3: Advanced Features - [ ] Session recording and playback - [ ] File transfer capabilities - [ ] Multi-protocol support (RDP/VNC) - [ ] Connection sharing for collaboration ### Phase 4: Enterprise Features - [ ] Session timeout policies - [ ] Connection quotas per role - [ ] Advanced audit reporting - [ ] Integration with external SIEM systems ## πŸ”’ Security Considerations ### Network Security - **TLS encryption**: All WebSocket connections over WSS - **Network isolation**: guacd in separate container network - **Firewall rules**: Restrict guacd access to ASGI backend only - **Connection limits**: Per-user and per-role connection quotas ### Authentication & Authorization - **JWT validation**: Verify tokens against Headplane API - **Role-based access**: Fine-grained permissions per protocol - **Session management**: Automatic timeout and cleanup - **Audit trails**: Complete logging of all access attempts ### Data Protection - **No credential storage**: Credentials never stored in frontend - **Session isolation**: Each connection in separate context - **Memory protection**: Clear sensitive data from memory - **Log sanitization**: Remove sensitive data from audit logs ## πŸŽ‰ Conclusion This guacamole-based architecture provides a secure, scalable, and maintainable solution for remote access that: 1. **Eliminates security risks** of client-side crypto and massive WASM downloads 2. **Integrates seamlessly** with existing OIDC role mapping improvements 3. **Provides enterprise-grade** audit, recording, and management capabilities 4. **Uses proven technologies** that security teams understand and trust 5. **Scales efficiently** with standard container orchestration The architecture leverages our OIDC improvements to provide fine-grained access control while maintaining the security posture required for VPN infrastructure management.