Apache Guacamole: Gateway de Acceso Remoto sin Cliente

Apache Guacamole es una puerta de enlace de escritorio remoto sin cliente que permite acceder a escritorios y servidores mediante RDP, VNC y SSH directamente desde el navegador web, sin necesidad de instalar ninguna aplicación en el dispositivo cliente. Al centralizar el acceso remoto en un único punto con autenticación y auditoría, es la solución ideal para entornos corporativos que necesitan control de acceso a infraestructuras sin exponer los puertos RDP/VNC directamente a Internet. Esta guía cubre el despliegue de Guacamole con Docker.

Requisitos Previos

  • Servidor Linux con Docker 24+ y Docker Compose v2
  • Mínimo 2 GB de RAM (4 GB recomendado para múltiples conexiones simultáneas)
  • Dominio con certificado TLS (para producción)
  • Puerto 443 accesible para los usuarios del gateway
  • Acceso de red desde el servidor Guacamole a los hosts de destino (RDP/VNC/SSH)

Instalación con Docker Compose

# Crear directorio de trabajo
mkdir -p /opt/guacamole/{init,config}
cd /opt/guacamole

# Descargar el script de inicialización de la base de datos
docker run --rm guacamole/guacamole /opt/guacamole/bin/initdb.sh --postgresql \
  > init/initdb.sql

# Crear el archivo docker-compose.yml
cat > docker-compose.yml << 'EOF'
version: "3.9"

services:
  # Base de datos PostgreSQL para Guacamole
  postgres:
    image: postgres:15-alpine
    restart: unless-stopped
    container_name: guacamole-db
    environment:
      POSTGRES_DB: guacamole_db
      POSTGRES_USER: guacamole_user
      POSTGRES_PASSWORD: password_seguro_db_aqui
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init/initdb.sql:/docker-entrypoint-initdb.d/initdb.sql:ro
    networks:
      - guacamole-net
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U guacamole_user -d guacamole_db"]
      interval: 10s
      timeout: 5s
      retries: 5

  # Daemon de Guacamole (procesamiento de protocolos RDP/VNC/SSH)
  guacd:
    image: guacamole/guacd:1.5.4
    restart: unless-stopped
    container_name: guacamole-guacd
    networks:
      - guacamole-net

  # Aplicación web de Guacamole
  guacamole:
    image: guacamole/guacamole:1.5.4
    restart: unless-stopped
    container_name: guacamole-app
    depends_on:
      postgres:
        condition: service_healthy
      guacd:
        condition: service_started
    environment:
      # Conexión al daemon guacd
      GUACD_HOSTNAME: guacd
      GUACD_PORT: 4822
      
      # Conexión a la base de datos PostgreSQL
      POSTGRESQL_HOSTNAME: postgres
      POSTGRESQL_PORT: 5432
      POSTGRESQL_DATABASE: guacamole_db
      POSTGRESQL_USER: guacamole_user
      POSTGRESQL_PASSWORD: password_seguro_db_aqui
      
      # Seguridad de la sesión
      GUACAMOLE_HOME: /etc/guacamole
    volumes:
      - ./config:/etc/guacamole
    ports:
      - "8080:8080"
    networks:
      - guacamole-net

volumes:
  postgres_data:

networks:
  guacamole-net:
    driver: bridge
EOF

# Iniciar los servicios
docker compose up -d

# Verificar que todos los contenedores están en ejecución
docker compose ps
docker compose logs guacamole

Configuración de la Base de Datos

Guacamole guarda usuarios, conexiones y grupos en la base de datos:

# Verificar que la base de datos se inicializó correctamente
docker compose exec postgres psql -U guacamole_user -d guacamole_db \
  -c "\dt" 2>/dev/null
# Debe mostrar las tablas: guacamole_connection, guacamole_user, etc.

# Las credenciales por defecto del administrador son:
# Usuario: guacadmin
# Contraseña: guacadmin
# IMPORTANTE: Cambiar la contraseña inmediatamente después del primer login

# Cambiar la contraseña del administrador directamente en la base de datos
# (alternativamente, cambiarla desde la interfaz web)
docker compose exec postgres psql -U guacamole_user -d guacamole_db << 'SQL'
UPDATE guacamole_user
SET password_hash = encode(digest(CONCAT('tu_nueva_contraseña', encode(password_salt, 'hex')), 'sha256'), 'hex'),
    password_date = NOW()
WHERE entity_id = (SELECT entity_id FROM guacamole_entity WHERE name = 'guacadmin');
SQL

# La interfaz web está disponible en:
# http://IP-del-servidor:8080/guacamole

Configuración de Conexiones

Guacamole gestiona conexiones desde la interfaz web en Settings > Connections:

# Las conexiones también se pueden insertar directamente en la base de datos
# Útil para automatización o importación masiva

docker compose exec postgres psql -U guacamole_user -d guacamole_db << 'SQL'
-- Crear una nueva conexión SSH al servidor de producción
INSERT INTO guacamole_connection (connection_name, protocol, max_connections)
VALUES ('Servidor-Produccion-SSH', 'ssh', 5);

-- Obtener el ID de la conexión creada
SELECT connection_id FROM guacamole_connection 
WHERE connection_name = 'Servidor-Produccion-SSH';

-- Añadir parámetros de la conexión SSH
INSERT INTO guacamole_connection_parameter (connection_id, parameter_name, parameter_value)
VALUES
    (1, 'hostname', '10.0.1.10'),
    (1, 'port', '22'),
    (1, 'username', 'operador'),
    -- NO almacenar contraseñas en texto claro; usar clave privada
    -- (1, 'password', 'contraseña'),
    -- Alternativa: autenticación por clave privada
    (1, 'private-key', '-----BEGIN OPENSSH PRIVATE KEY-----\n...'),
    (1, 'color-scheme', 'green-black'),
    (1, 'font-size', '14');

-- Crear una conexión RDP a un servidor Windows
INSERT INTO guacamole_connection (connection_name, protocol)
VALUES ('Servidor-Windows-RDP', 'rdp');

INSERT INTO guacamole_connection_parameter (connection_id, parameter_name, parameter_value)
VALUES
    (2, 'hostname', '10.0.2.20'),
    (2, 'port', '3389'),
    (2, 'username', 'Administrador'),
    (2, 'domain', 'EMPRESA'),
    (2, 'security', 'nla'),          -- NLA es más seguro que RDP básico
    (2, 'ignore-cert', 'true'),      -- Solo en pruebas; usar certificado válido en prod
    (2, 'resize-method', 'reconnect'),
    (2, 'enable-font-smoothing', 'true');
SQL

Autenticación LDAP

Configura Guacamole para autenticar usuarios contra LDAP/Active Directory:

# Crear el archivo de configuración de Guacamole con LDAP
mkdir -p /opt/guacamole/config
cat > /opt/guacamole/config/guacamole.properties << 'EOF'
# Configuración LDAP para autenticación corporativa
ldap-hostname: ldap.empresa.local
ldap-port: 389
ldap-username-attribute: sAMAccountName  # Para Active Directory
# ldap-username-attribute: uid  # Para LDAP genérico
ldap-user-base-dn: ou=usuarios,dc=empresa,dc=local
ldap-search-bind-dn: cn=guacamole-reader,ou=service-accounts,dc=empresa,dc=local
ldap-search-bind-password: password_cuenta_servicio

# Mapeo de grupos LDAP a permisos de Guacamole
ldap-group-base-dn: ou=grupos,dc=empresa,dc=local
ldap-group-search-filter: (objectClass=groupOfNames)

# Los usuarios en el grupo "guacamole-admins" de LDAP serán administradores
ldap-user-attributes: cn,mail,givenName,sn
EOF

# Actualizar docker-compose.yml para montar la configuración
# Añadir en el servicio guacamole:
# volumes:
#   - ./config:/etc/guacamole:ro

docker compose restart guacamole
docker compose logs guacamole | tail -20

Proxy Inverso con Nginx

# Instalar Nginx y Certbot
apt-get install -y nginx certbot python3-certbot-nginx

# Obtener certificado TLS
certbot certonly --nginx -d remoto.tudominio.com \
  --email [email protected] --agree-tos --non-interactive

# Configuración de Nginx para Guacamole
cat > /etc/nginx/sites-available/guacamole << 'EOF'
server {
    listen 80;
    server_name remoto.tudominio.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name remoto.tudominio.com;

    ssl_certificate /etc/letsencrypt/live/remoto.tudominio.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/remoto.tudominio.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;

    # Cabeceras de seguridad
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;

    # WebSocket para Guacamole (requiere soporte de WebSocket)
    location /guacamole/websocket-tunnel {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_buffering off;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }

    # Resto de la aplicación Guacamole
    location /guacamole {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering off;
        client_max_body_size 1024m;
    }

    # Redirigir / a /guacamole para comodidad
    location = / {
        return 301 https://$host/guacamole/;
    }
}
EOF

ln -s /etc/nginx/sites-available/guacamole /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

Autenticación de Dos Factores

# Habilitar TOTP (Time-based One-Time Password) en Guacamole
# Añadir la extensión TOTP a la imagen de Guacamole o descargarla

# La extensión TOTP ya viene incluida en la imagen oficial de Guacamole
# Activarla en docker-compose.yml añadiendo al servicio guacamole:
# environment:
#   TOTP_ENABLED: "true"  # No disponible como var de entorno directa
# 
# En su lugar, configurar en guacamole.properties:
cat >> /opt/guacamole/config/guacamole.properties << 'EOF'

# Habilitar TOTP para doble factor de autenticación
# (requiere la extensión totp-*.jar en el directorio de extensiones)
totp-issuer: Empresa
totp-period: 30
totp-digits: 6
EOF

# Los usuarios deberán configurar su app de autenticación (Google Authenticator,
# Authy, etc.) en su primer login tras activar TOTP
docker compose restart guacamole

Solución de Problemas

# Ver logs de todos los contenedores
docker compose logs -f

# Ver logs del daemon guacd (errores de conexión RDP/VNC/SSH)
docker compose logs guacd

# Probar la conectividad desde el servidor Guacamole al host de destino
docker compose exec guacd nc -zv 10.0.1.10 22    # Prueba SSH
docker compose exec guacd nc -zv 10.0.2.20 3389  # Prueba RDP

# La interfaz web no carga
# Verificar que el contenedor guacamole está en ejecución
docker compose ps

# Error "Cannot connect to Guacamole server"
# El servidor web no puede conectar con guacd
docker compose logs guacamole | grep "guacd"

# Error de autenticación en conexiones RDP
# - Verificar usuario/contraseña/dominio en los parámetros de conexión
# - Comprobar que NLA está configurado correctamente
# - Verificar que el firewall permite el tráfico al puerto 3389

# Sesión desconectada prematuramente (timeout)
# Aumentar los timeouts en la configuración de Nginx:
# proxy_read_timeout 3600s;  (ya configurado arriba)

# Ver las conexiones activas en la base de datos
docker compose exec postgres psql -U guacamole_user -d guacamole_db \
  -c "SELECT * FROM guacamole_connection_history ORDER BY start_date DESC LIMIT 10;"

Conclusión

Apache Guacamole transforma el acceso remoto corporativo en un servicio web centralizado y auditable que elimina la necesidad de clientes VPN o software RDP en los dispositivos de los usuarios, simplificando la gestión y mejorando la seguridad al no exponer directamente los puertos de escritorio remoto. La integración con LDAP/AD y la autenticación de dos factores, combinadas con el registro completo de sesiones, convierten a Guacamole en la solución más completa para el acceso remoto controlado a infraestructuras críticas.