Apache Guacamole Remote Access Gateway
Apache Guacamole is a clientless remote desktop gateway that provides browser-based access to RDP, VNC, and SSH connections without requiring any client software. This guide covers deploying Guacamole with Docker, setting up connections, configuring LDAP authentication, and enabling two-factor security.
Prerequisites
- Linux server (Ubuntu 22.04/Debian 12 or CentOS/Rocky 9) with at least 2 GB RAM
- Docker Engine and Docker Compose v2
- A domain name with a valid TLS certificate
- Ports 80 and 443 open in firewall
- Network access from the Guacamole server to target machines (RDP/3389, SSH/22, VNC/5900)
Deploy Guacamole with Docker Compose
Guacamole consists of two components:
guacd- the daemon that handles remote protocol connectionsguacamole- the web application and REST API
mkdir -p /opt/guacamole/{mysql,init}
cd /opt/guacamole
# Generate the MySQL initialization script from the Guacamole image
docker run --rm guacamole/guacamole /opt/guacamole/bin/initdb.sh --mysql > init/initdb.sql
cat > docker-compose.yml << 'EOF'
version: "3.8"
services:
# Guacamole backend daemon
guacd:
image: guacamole/guacd:latest
restart: unless-stopped
volumes:
- guacd_data:/tmp/guacd
# MySQL database for user/connection storage
mysql:
image: mysql:8.0
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: StrongRootPassword123
MYSQL_DATABASE: guacamole_db
MYSQL_USER: guacamole_user
MYSQL_PASSWORD: StrongGuacPassword456
volumes:
- ./mysql:/var/lib/mysql
- ./init:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 30s
timeout: 10s
retries: 3
# Guacamole web application
guacamole:
image: guacamole/guacamole:latest
restart: unless-stopped
depends_on:
mysql:
condition: service_healthy
environment:
GUACD_HOSTNAME: guacd
GUACD_PORT: 4822
MYSQL_HOSTNAME: mysql
MYSQL_PORT: 3306
MYSQL_DATABASE: guacamole_db
MYSQL_USER: guacamole_user
MYSQL_PASSWORD: StrongGuacPassword456
# Optional: set a custom path
# WEBAPP_CONTEXT: ROOT
ports:
- "8080:8080"
volumes:
guacd_data:
EOF
docker compose up -d
# Check container health
docker compose ps
docker compose logs guacamole --tail=30
Initial Configuration and Admin Setup
- Access the Guacamole interface at
http://your-server:8080/guacamole - Log in with default credentials: admin / guacadmin
- Immediately change the admin password:
- Click the username (top right) > Settings > Preferences
- Change the password
# Verify the web interface is running
curl -s http://localhost:8080/guacamole/api/tokens | python3 -m json.tool
Configure RDP Connections
Add a Windows Remote Desktop connection through the admin interface:
- Go to Settings > Connections > New Connection
- Select Protocol: RDP
- Configure:
Name: Windows Server
Location: ROOT
Protocol: RDP
Network:
Hostname: 192.168.1.50
Port: 3389
Authentication:
Username: Administrator
Password: (leave blank to prompt user)
Domain: EXAMPLE
Display:
Color depth: True color (32-bit)
Width: 1920
Height: 1080
DPI: 96
Performance:
Enable wallpaper: false
Enable font smoothing: true
Enable full window drag: false
Security:
Security mode: Any
Ignore cert: true (for self-signed certs)
For NLA (Network Level Authentication):
Security mode: NLA
Ignore cert: false # Use proper cert in production
Configure SSH Connections
Add an SSH connection for Linux servers:
- Settings > Connections > New Connection > Protocol: SSH
Name: Linux Server
Protocol: SSH
Network:
Hostname: 192.168.1.100
Port: 22
Authentication:
Username: admin
Private key: (paste SSH private key)
Passphrase: (if key is encrypted)
Terminal:
Font size: 12
Color scheme: Gray black
Backspace key: Delete
For key-based SSH, prepare the key:
# Generate a dedicated SSH key for Guacamole
ssh-keygen -t ed25519 -f /tmp/guacamole_key -N ""
# Add public key to target server
ssh-copy-id -i /tmp/guacamole_key.pub [email protected]
# Paste the private key content into the Guacamole connection settings
cat /tmp/guacamole_key
Configure VNC Connections
- Settings > Connections > New Connection > Protocol: VNC
Name: Desktop 01
Protocol: VNC
Network:
Hostname: 192.168.1.200
Port: 5901 (5900 + display number)
Authentication:
Password: (VNC password)
Display:
Color depth: True color (32-bit)
Cursor: Remote (server-side)
For VNC with SSH tunneling (recommended for security):
Hostname: 192.168.1.200
Port: 5901
SSH Tunnel:
SSH Host: 192.168.1.200
SSH Port: 22
SSH Username: admin
SSH Private Key: (paste key)
LDAP Authentication
Configure Guacamole to authenticate against LDAP/Active Directory:
# Update docker-compose.yml - add LDAP environment variables to guacamole service
sudo nano /opt/guacamole/docker-compose.yml
Add to the guacamole service environment:
environment:
# ... existing settings ...
# LDAP Configuration
LDAP_HOSTNAME: ldap.example.com
LDAP_PORT: 389
LDAP_ENCRYPTION_METHOD: starttls # or "ssl" for LDAPS
LDAP_USER_BASE_DN: "ou=Users,dc=example,dc=com"
LDAP_USERNAME_ATTRIBUTE: uid
LDAP_SEARCH_BIND_DN: "cn=guacamole-reader,ou=ServiceAccounts,dc=example,dc=com"
LDAP_SEARCH_BIND_PASSWORD: ServiceAccountPassword
LDAP_GROUP_BASE_DN: "ou=Groups,dc=example,dc=com"
LDAP_GROUP_SEARCH_FILTER: "(objectClass=groupOfNames)"
LDAP_MEMBER_ATTRIBUTE: member
docker compose up -d guacamole
# Test LDAP login
# Log in with an LDAP user - Guacamole will create the user automatically on first login
For Active Directory:
LDAP_HOSTNAME: dc.example.com
LDAP_PORT: 389
LDAP_USER_BASE_DN: "dc=example,dc=com"
LDAP_USERNAME_ATTRIBUTE: sAMAccountName # Windows login name
LDAP_SEARCH_BIND_DN: "cn=guac-reader,cn=Users,dc=example,dc=com"
LDAP_SEARCH_BIND_PASSWORD: Password
LDAP_GROUP_BASE_DN: "cn=Users,dc=example,dc=com"
Two-Factor Authentication
Enable TOTP (Time-based One-Time Password) for MFA:
# Add to guacamole service environment in docker-compose.yml
TOTP_ENABLED: "true"
TOTP_ISSUER: "Guacamole" # Displayed in authenticator app
TOTP_DIGITS: 6
TOTP_PERIOD: 30
docker compose up -d guacamole
Users will be prompted to enroll their TOTP device on next login by scanning a QR code with Google Authenticator or Authy.
For Duo Security MFA:
DUO_API_HOSTNAME: api-XXXXXXXX.duosecurity.com
DUO_INTEGRATION_KEY: DIXXXXXXXXXXXXXXXXXX
DUO_SECRET_KEY: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
DUO_APPLICATION_KEY: (generate with: python3 -c "import hashlib, os; print(hashlib.sha1(os.urandom(32)).hexdigest())")
Reverse Proxy with Nginx
Put Guacamole behind Nginx with HTTPS:
sudo tee /etc/nginx/sites-available/guacamole << 'EOF'
server {
listen 80;
server_name guac.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name guac.example.com;
ssl_certificate /etc/letsencrypt/live/guac.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/guac.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
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;
# Required for WebSocket (Guacamole uses WebSockets)
proxy_read_timeout 3600;
proxy_send_timeout 3600;
# Remove the /guacamole prefix if Guacamole is at the root
proxy_pass_header Server;
}
}
EOF
sudo ln -s /etc/nginx/sites-available/guacamole /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Remove Guacamole's port from Docker Compose (only expose via Nginx):
guacamole:
# Change from:
ports:
- "8080:8080"
# To:
ports:
- "127.0.0.1:8080:8080"
Troubleshooting
"Cannot connect to Guacamole server" in browser:
# Check all containers are running
docker compose ps
docker compose logs guacamole --tail=50
# Verify guacd is running
docker compose logs guacd --tail=20
# Check database connection
docker compose exec guacamole cat /opt/guacamole/bin/start.sh
RDP connections fail with "Unable to connect":
# Verify the target is reachable from the Guacamole container
docker compose exec guacamole ping -c3 192.168.1.50
docker compose exec guacamole nc -zv 192.168.1.50 3389
# Check RDP is enabled on the target Windows server:
# Control Panel > System > Remote Settings > Allow remote connections
SSH authentication failing:
# Test SSH directly from server
ssh -i /tmp/guacamole_key [email protected]
# Verify key format - Guacamole requires OpenSSH private key format
# Convert from PuTTY format if needed:
puttygen guacamole.ppk -O private-openssh -o guacamole_key
LDAP users can't log in:
# Test LDAP bind manually
ldapsearch -H ldap://ldap.example.com -D "cn=guacamole-reader,ou=ServiceAccounts,dc=example,dc=com" \
-w ServiceAccountPassword -b "ou=Users,dc=example,dc=com" "(uid=testuser)"
# Check Guacamole logs for LDAP errors
docker compose logs guacamole 2>&1 | grep -i ldap
Conclusion
Apache Guacamole provides a secure, browser-based remote access gateway that eliminates the need for VPN clients or native RDP/SSH applications on end-user devices. Docker Compose deployment simplifies installation and updates, while LDAP integration enables centralized user management and TOTP adds an essential MFA layer for any internet-exposed gateway. Ensure Guacamole is always placed behind a TLS-terminating reverse proxy and restrict access using firewall rules to limit the attack surface.


