headplane/GUACAMOLE_REMOTE_ACCESS_DESIGN.md
Ryan Malloy 1ced46e680 🍴 ENTERPRISE SECURITY FORK: Complete OIDC overhaul + architecture realignment
This commit marks the creation of the enterprise security fork, fundamentally
realigning Headplane's architecture toward production VPN infrastructure requirements.

## 🚀 OIDC AUTHENTICATION REVOLUTION

### Convention Over Configuration Role Mapping
- Smart pattern recognition for common identity provider groups
- Case-insensitive matching works with any capitalization
- Role hierarchy ensures highest privilege wins
- Zero-config setup for 90% of identity providers

### Environment Variable Power
- Custom role mapping via HEADPLANE_*_GROUPS variables
- Override system with graceful fallbacks to conventions
- Enterprise-friendly configuration management
- Easy deployment customization without code changes

### Configuration Self-Healing
- Auto-scope detection adds "groups" scope automatically
- Auto-redirect generation from PUBLIC_URL/HEADPLANE_URL
- Provider-specific optimizations (Google, Azure AD, Keycloak, Okta)
- Helpful guidance and environment variable suggestions

### Production-Ready Quality
- 32/32 comprehensive tests passing
- Real-world provider scenario validation
- Complete TypeScript type safety
- Extensive error handling and logging

## 🏗️ ARCHITECTURAL VISION

### Security-First Philosophy
- Eliminated 38MB WASM SSH console (security nightmare)
- Designed guacamole + Python ASGI remote access architecture
- Server-side connections only, no client-side crypto
- Audit-friendly technologies that security teams understand

### Enterprise Integration Focus
- OIDC role mapping integrates with remote access permissions
- Comprehensive audit trails and session management
- Standards-based protocols over experimental approaches
- Maintainable, deployable, scalable solutions

## 📁 CORE CHANGES

### Implementation Files
- app/server/web/roles.ts - Intelligent role mapping engine
- app/utils/oidc.ts - Smart group extraction from claims
- app/server/config/oidc-enhancer.ts - Configuration self-healing
- app/routes/auth/oidc-callback.ts - Enhanced logging & error handling
- config.example.yaml - Simplified configuration examples

### Database & Testing
- drizzle/0003_add_groups_column.sql - Groups storage migration
- tests/oidc-improvements.test.js - Comprehensive test suite

### Documentation & Architecture
- OIDC_IMPROVEMENTS_SUMMARY.md - Complete implementation guide
- GUACAMOLE_REMOTE_ACCESS_DESIGN.md - Security-first remote access architecture
- WASM_SSH_REMOVAL.md - Justification for security improvements
- docs/OIDC-Authentication.md - User configuration guide

## 🎯 FORK JUSTIFICATION

The upstream project's commitment to a 38MB client-side WASM SSH console
reveals irreconcilable differences in architectural philosophy:

**Upstream Priority**: Technical novelty, feature completeness, "cool factor"
**Enterprise Fork Priority**: Security, auditability, production readiness

This fork targets organizations running production VPN infrastructure who need:
- Security-first development practices
- Enterprise identity system integration
- Audit trails and compliance tooling
- Maintainable, proven technologies

## 🚀 FORWARD VISION

This enterprise security fork establishes the foundation for:
- Advanced role-based access control
- Comprehensive audit and compliance features
- Multi-tenancy and organizational management
- API-first infrastructure as code support
- Integration with enterprise monitoring and SIEM systems

---

**Breaking Change**: This commit removes the WASM SSH console and establishes
a new security-focused architectural direction incompatible with upstream.

Organizations prioritizing VPN infrastructure security will find this fork
provides the enterprise-grade features and security posture they require.
2025-09-17 02:23:56 -06:00

12 KiB

🏗️ Guacamole + Python ASGI Remote Access Architecture

Overview

This document outlines the proposed architecture for replacing the problematic 38MB WASM SSH console with a professional, secure, and maintainable remote access solution using Apache Guacamole, Python ASGI backend, and a custom SPA frontend.

🎯 Architecture Goals

Security First

  • 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

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

# 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

# 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

// 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:

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:

# 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

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.