Files
jbergner 7c69432097
Some checks failed
release-tag / release-image (push) Successful in 2m3s
release-main / release-images (push) Failing after 3m9s
RC-2 0.5.2
2026-08-24 05:46:42 +02:00
..
2026-08-24 05:18:43 +02:00
2026-08-24 05:46:42 +02:00
2026-08-24 05:18:43 +02:00
2026-08-24 05:46:42 +02:00
2026-08-24 05:18:43 +02:00
2026-08-24 05:18:43 +02:00
2026-08-24 05:46:42 +02:00
2026-08-24 05:46:42 +02:00

SessionGuard + 3x Guacamole + NetBird + public Caddy

Target design for about 200-250 concurrent Guacamole/RDS sessions:

Internet
   |
   v
PUBLIC01 (Caddy + NetBird)
   |  HTTPS termination / SessionGuard ForwardAuth / sticky LB
   |  NetBird/WireGuard
   +--> GUAC01 Docker subnet --> Guacamole01 --> guacd01 --> RDS farm
   +--> GUAC02 Docker subnet --> Guacamole02 --> guacd02 --> RDS farm
   +--> GUAC03 Docker subnet --> Guacamole03 --> guacd03 --> RDS farm
   +--> SESSIONGUARD01 Docker subnet --> SessionGuard Master
                                      --> shared Guacamole PostgreSQL

Important: Guacamole is configured once, not three times

All three workers connect to the same Guacamole PostgreSQL database at 10.202.0.20:5432.

Therefore these are shared automatically:

  • Guacamole users
  • connection definitions
  • connection groups
  • permissions
  • user preferences stored in PostgreSQL
  • the Guacamole connection IDs used by SessionGuard resources

Every worker must use the same:

  • SessionGuard-enabled Guacamole image/version
  • PostgreSQL credentials/database
  • SESSIONGUARD_BROKER_API_KEY
  • Header-Auth configuration

There is no PocketID/OIDC configuration on the individual Guacamole workers.

PocketID flow

PocketID is configured once through SessionGuard:

Browser -> https://ts.hilden.info
        -> public Caddy
        -> SessionGuard /auth/verify
        -> PocketID if no valid access session
        -> SessionGuard OIDC callback
        -> Caddy injects X-Guacamole-User
        -> selected Guacamole worker

Use one PocketID confidential client for Guacamole access:

  • Redirect URI: https://ts.hilden.info/_sessionguard/auth/oidc/callback
  • Back-channel logout callback: https://ts.hilden.info/_sessionguard/auth/backchannel-logout
  • Post logout redirect: https://ts.hilden.info/
  • Scopes: openid profile email groups

Use a second PocketID client for the SessionGuard administration UI:

  • Redirect URI: https://sessionguard.hilden.info/oidc/callback

DNS

Only the public VPS needs public DNS:

ts.hilden.info            -> PUBLIC01 public IP
sessionguard.hilden.info  -> PUBLIC01 public IP

auth.hilden.info remains wherever PocketID currently runs. It only needs to be moved behind PUBLIC01 if you intentionally want that architecture too.

The Guacamole workers and SessionGuard host need no public A records.

Docker subnet plan

The example intentionally uses unique, fixed subnets:

GUAC01           10.201.1.0/24   Guacamole 10.201.1.10
GUAC02           10.201.2.0/24   Guacamole 10.201.2.10
GUAC03           10.201.3.0/24   Guacamole 10.201.3.10
SESSIONGUARD01   10.202.0.0/24   Master    10.202.0.10
                                    SG DB     10.202.0.11 (NOT routed)
                                    Guac DB   10.202.0.20

Change them if they overlap any existing LAN, Docker, VPN or NetBird routes.

Deployment order

  1. Deploy SessionGuard stack.
  2. Deploy GUAC01/02/03 using the same guac-worker/docker-compose.yml but the matching .env values.
  3. Register all four on-prem hosts as NetBird peers.
  4. Configure the NetBird Networks described in NETBIRD-NETWORKS.md.
  5. Verify PUBLIC01 can reach the three Guacamole IPs and SessionGuard health.
  6. Deploy PUBLIC01 Caddy stack.
  7. Point public DNS for ts.hilden.info and sessionguard.hilden.info to PUBLIC01.
  8. Configure the PocketID callback URLs.
  9. Test / _sessionguard/auth/status (without the space) and then Guacamole.

Guacamole N+1 behavior

Caddy uses a signed guac_node load-balancer cookie. A browser stays on the same Guacamole webapp while that worker is healthy. If a worker fails, new requests can move to another worker, but an already active remote desktop tunnel on the failed worker must reconnect. The RDS session itself can survive and SessionGuard can broker the reconnect back to its existing RDS host.

For a 250-session peak, three workers imply about 83 sessions/worker normally and 125 sessions/worker with one worker down. Validate that target with your actual applications and display settings.

RDP target DNS

Your Guacamole connection currently uses:

${SESSIONGUARD_HOST}.stadt-hilden.de

Ensure the Guacamole containers can resolve the internal stadt-hilden.de zone. If NetBird DNS overrides resolver settings on the hosts, configure a NetBird DNS nameserver/match-domain for your AD/internal DNS zone.

Updating a worker

Because the three workers are stateless apart from the shared drive volume and shared PostgreSQL state, drain one worker at the Caddy/NetBird level, update it, then return it to service. Never create three independent Guacamole databases.

Public edge security (v0.5.2)

The public-vps/ stack now contains SessionGuard EdgeGuard in front of both public hosts. EdgeGuard provides CIDR blacklisting, NAT-friendly rate limiting, scanner detection, bounded per-IP state, persistent temporary bans and localhost-only metrics. The Caddy example disables its admin API, uses bounded/sampled access logs, pins Caddy 2.11.4, and exposes only the proxy service publicly. See EDGE-SECURITY.md.