Skip to content

Instantly share code, notes, and snippets.

@vttc08
Last active June 12, 2025 04:48
Show Gist options
  • Save vttc08/3b866ff4f71ede8d9bb409e58376402e to your computer and use it in GitHub Desktop.
Save vttc08/3b866ff4f71ede8d9bb409e58376402e to your computer and use it in GitHub Desktop.
Apache guacamole configuration
# Credit https://github.com/boschkundendienst/guacamole-docker-compose
services:
# guacd
guacd:
container_name: guacd
image: guacamole/guacd
restart: unless-stopped
environment:
TZ: America/Vancouver
networks:
- guacamole_network
volumes:
- guacamole_recording:/record:rw
# postgres
postgres:
container_name: guacdb
env_file:
- ~/docker/guacamole/.env
environment:
PGDATA: /var/lib/postgresql/data/guacamole
POSTGRES_DB: guacamole_db
POSTGRES_USER: guacamole_user
TZ: America/Vancouver
# POSTGRES_PASSWORD from .env file
networks:
- guacamole_network
image: postgres:15.2-alpine
restart: unless-stopped
volumes:
- ~/docker/guacamole/init:/docker-entrypoint-initdb.d:z
- guacdb_postgres:/var/lib/postgresql/data:Z
# guacamole
guacamole:
container_name: guacamole
group_add:
- "1000"
depends_on:
- guacd
- postgres
env_file:
- ~/docker/guacamole/.env
- ~/docker/authentication/auth.env
networks:
- guacamole_network
- public
environment:
GUACD_HOSTNAME: guacd
POSTGRES_DATABASE: guacamole_db
POSTGRES_HOSTNAME: guacdb
# POSTGERES_PASSWORD from .env file
POSTGRES_USER: guacamole_user
RECORDING_SEARCH_PATH: /record
WEBAPP_CONTEXT: /remote
EXTENSION_PRIORITY: '*, openid'
OPENID_AUTHORIZATION_ENDPOINT: 'https://auth.${DOMAIN_NAME}/api/oidc/authorization?state=${GUACAMOLE_STATE}'
OPENID_JWKS_ENDPOINT: 'https://auth.${DOMAIN_NAME}/jwks.json'
OPENID_ISSUER: 'https://auth.${DOMAIN_NAME}'
OPENID_CLIENT_ID: 'guacamole'
OPENID_REDIRECT_URI: 'https://${DOMAIN_NAME}/remote'
OPENID_USERNAME_CLAIM_TYPE: preferred_username
OPENID_GROUPS_CLAIM_TYPE: groups
OPENID_SCOPE: openid profile groups email
TZ: America/Vancouver
image: guacamole/guacamole
volumes:
- guacamole_recording:/record:rw
ports:
- 8022:8080/tcp
restart: unless-stopped
volumes:
guacdb_postgres:
name: guacdb_postgres
driver: local
guacamole_recording:
name: guacamole_recording
driver: local
networks:
guacamole_network:
name: guacamole_network
driver: bridge
public:
name: public
external: true
# Import the SQL files
python3 generate_sql.py
docker exec -i guacdb psql -U guacamole_user -d guacamole_db < guac_sharing_profiles_dynamic.sql
docker exec -i guacdb psql -U guacamole_user -d guacamole_db < guac_sync_dynamic.sql
# This file will generate SQL file based on SSH config which can be written to guacamole postgres
#!/usr/bin/env python3
import os
from pathlib import Path
ssh_config_path = Path.home() / ".ssh" / "config"
if not ssh_config_path.exists():
raise FileNotFoundError(f"SSH config not found at {ssh_config_path}")
with open(ssh_config_path) as f:
ssh_config = f.read()
entries = []
entry = {}
home = str(Path.home())
for line in ssh_config.strip().splitlines():
line = line.strip()
if line.startswith("Host "):
if entry:
entries.append(entry)
entry = {}
entry["connection_name"] = line.split()[1]
elif line.startswith("HostName"):
entry["hostname"] = line.split()[1]
elif line.startswith("User"):
entry["username"] = line.split()[1]
elif line.startswith("IdentityFile"):
path = line.split()[1].replace("~", home)
entry["identityfile"] = path
if entry:
entries.append(entry)
default_params = {
"timezone": "America/Vancouver",
"enable-sftp": "true",
"create-typescript-path": "true",
"typescript-path": "${HISTORY_PATH}/${HISTORY_UUID}",
"create-recording-path": "true",
"recording-path": "${HISTORY_PATH}/${HISTORY_UUID}",
"terminal-type": "xterm-256color",
"command": "export GUAC=1 && bash",
"port": "22"
}
sql_statements = []
for entry in entries:
name = entry.get("connection_name")
host = entry.get("hostname")
user = entry.get("username")
key_path = entry.get("identityfile")
if not all([name, host, user, key_path]):
continue
if not os.path.exists(key_path):
print(f"[!] Skipping {name} — key file missing: {key_path}")
continue
with open(key_path) as f:
private_key = f.read()
merged = default_params.copy()
merged["hostname"] = host
merged["username"] = user
merged["private-key"] = private_key
merged["sftp-root-directory"] = f"/home/{user}"
sql_statements.append(f"-- {name} ({host})")
sql_statements.append(f"""
DO $$
DECLARE cid INT;
BEGIN
SELECT connection_id INTO cid FROM guacamole_connection WHERE connection_name = '{name}';
IF cid IS NULL THEN
INSERT INTO guacamole_connection (connection_name, protocol)
VALUES ('{name}', 'ssh') RETURNING connection_id INTO cid;
ELSE
DELETE FROM guacamole_connection_parameter WHERE connection_id = cid;
END IF;
""")
for param, value in merged.items():
escaped = value.replace("'", "''")
sql_statements.append(
f" INSERT INTO guacamole_connection_parameter (connection_id, parameter_name, parameter_value) "
f"VALUES (cid, '{param}', '{escaped}');"
)
sql_statements.append("END $$;\n")
# Save output
output_file = "guac_sync_dynamic.sql"
with open(output_file, "w") as f:
f.write("\n".join(sql_statements))
print(f"✅ SQL written to {output_file}")
-- this SQL script apply read only and default sharing policy on each connection
DO $$
DECLARE
conn_id INT;
spid INT := 1000;
BEGIN
FOR conn_id IN SELECT DISTINCT connection_id FROM guacamole_connection LOOP
-- sharing
INSERT INTO guacamole_sharing_profile (sharing_profile_id, sharing_profile_name, primary_connection_id)
VALUES (spid, 'sharing', conn_id);
INSERT INTO guacamole_sharing_profile_parameter (sharing_profile_id, parameter_name, parameter_value)
VALUES (spid, 'read-only', 'false');
spid := spid + 1;
-- readonly sharing
INSERT INTO guacamole_sharing_profile (sharing_profile_id, sharing_profile_name, primary_connection_id)
VALUES (spid, 'readonly sharing', conn_id);
INSERT INTO guacamole_sharing_profile_parameter (sharing_profile_id, parameter_name, parameter_value)
VALUES (spid, 'read-only', 'true');
spid := spid + 1;
END LOOP;
END
$$ LANGUAGE plpgsql;
#!/bin/sh
#
# check if docker is running
if ! (docker ps >/dev/null 2>&1)
then
echo "docker daemon not running, will exit here!"
exit
fi
echo "Preparing folder init and creating ./init/initdb.sql"
mkdir ./init >/dev/null 2>&1
chmod -R +x ./init
docker run --rm guacamole/guacamole /opt/guacamole/bin/initdb.sh --postgresql > ./init/initdb.sql
echo "done"
echo "Preparing folder record and set permissions"
mkdir ./record >/dev/null 2>&1
chmod -R 777 ./record
docker run --rm -v guacamole_recording:/path busybox sh -c 'touch /path && chmod a+w /path'
echo "done"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment