{ email {$ACME_EMAIL} admin off # Reduce protocol/parser attack surface without interfering with Guacamole # WebSockets. HTTP/3 can be enabled later if there is a concrete need. servers { protocols h1 h2 strict_sni_host on max_header_size 64KB timeouts { read_header 10s } } } (edge_security_headers) { header { -Server Strict-Transport-Security "max-age=31536000" X-Content-Type-Options "nosniff" Referrer-Policy "strict-origin-when-cross-origin" Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=()" } } (edgeguard_check) { # EdgeGuard only listens on 127.0.0.1. These headers are overwritten by # Caddy and therefore cannot be forged by an Internet client. forward_auth 127.0.0.1:9081 { uri /check header_up X-Edge-Client-IP {client_ip} header_up X-Edge-Original-Host {host} header_up X-Edge-Original-Method {method} header_up X-Edge-Original-URI {uri} } } # Guacamole public endpoint + SessionGuard Access Auth {$GUAC_HOST} { encode zstd gzip import edge_security_headers route { # Security pre-check occurs before OIDC/Auth and before any backend. import edgeguard_check # These endpoints belong to SessionGuard, but intentionally live on # the Guacamole hostname so the Access-Auth cookie remains host-bound. handle_path /_sessionguard/* { reverse_proxy {$SESSIONGUARD_IP}:8080 } handle { route { # Never trust identity headers supplied by an Internet client. request_header -X-Guacamole-User request_header -X-SessionGuard-User request_header -X-SessionGuard-Email request_header -X-SessionGuard-Groups request_header -X-Forwarded-User request_header -X-Authenticated-User # SessionGuard is the single OIDC/ForwardAuth authority. forward_auth {$SESSIONGUARD_IP}:8080 { uri /auth/verify copy_headers { X-Guacamole-User X-SessionGuard-User X-SessionGuard-Email X-SessionGuard-Groups } } # Sticky sessions are important because Guacamole keeps runtime # authentication/session state in the selected webapp process. reverse_proxy {$GUAC01_IP}:8080 {$GUAC02_IP}:8080 {$GUAC03_IP}:8080 { lb_policy cookie guac_node {$GUAC_LB_SECRET} lb_try_duration 5s lb_try_interval 250ms health_uri / health_interval 10s health_timeout 3s } } } } log { output file /var/log/caddy/guacamole-access.log { roll_size 100MiB roll_keep 5 roll_keep_for 168h } format json sampling { interval 1s first 200 thereafter 20 } } } # SessionGuard administration UI, Agent endpoint and APIs. {$SESSIONGUARD_HOST} { encode zstd gzip import edge_security_headers route { import edgeguard_check # These endpoints are required internally only. Broker requests from # Guacamole workers go directly over NetBird, never via public Caddy. respond /metrics 404 respond /api/v1/broker/* 404 reverse_proxy {$SESSIONGUARD_IP}:8080 } log { output file /var/log/caddy/sessionguard-access.log { roll_size 100MiB roll_keep 5 roll_keep_for 168h } format json sampling { interval 1s first 200 thereafter 20 } } }