Files
sessiongurad/docs/ARCHITECTURE.md
jbergner 7972ed7e38
All checks were successful
release-tag / release-image (push) Successful in 2m5s
release-main / release-images (push) Successful in 5m5s
Update mit Guacamole-Extension
2026-08-22 15:19:17 +02:00

6.5 KiB
Raw Blame History

SessionGuard 0.3 Architecture

Purpose

SessionGuard separates the functions commonly bundled into a Citrix deployment into components that can be independently replaced:

  • PocketID/OIDC: administrator identity and group claims.
  • Traefik / forward-auth: trusted ingress for Guacamole.
  • Apache Guacamole: browser gateway and RDP transport.
  • Windows RDS: Windows session runtime.
  • SessionGuard Master: broker, Director, policy control plane, history, alerting and command queue.
  • SessionGuard Agent: Windows/RDS integration, profile lifecycle, templates, telemetry and local fallback administration.

SessionGuard intentionally does not implement a new remote-display protocol.

Network model

Agents initiate all master communication:

Agent --HTTPS heartbeat--> Master
      <-- policy + commands--
      -- results + telemetry-->

The master does not open SMB/RPC/WMI/WinRM management sessions to RDS hosts. This keeps the Windows hosts behind normal firewall/NAT boundaries and makes master outages less disruptive.

Guacamole reaches only the broker API using a dedicated API key:

Guacamole extension --HTTP(S) + bearer key--> /api/v1/broker/tokens

That API key is separate from agent enrollment credentials and PocketID secrets.

Master persistence

Production mode uses PostgreSQL.

sessionguard_state

Single JSONB control-plane document containing relatively small mutable state:

  • agents and last snapshots
  • farms
  • published resources
  • leases
  • policy versions/control state
  • active alerts

sessionguard_audit

Append-only administrative and command audit events. Indexed by time and actor.

sessionguard_session_history

Append-only Director session history. Indexed by time, lower-cased username and agent ID.

History retention is bounded by history_limit. This avoids rewriting a permanently growing history document on every agent heartbeat.

JSON-file storage remains supported when database_url is empty. It is intended for development, migration and small single-node installations, not a HA master.

Single-active-master guard

v0.3 is intentionally single-active-master. A dedicated PostgreSQL connection holds a session-level advisory lock for the lifetime of the Master, and control-plane writes verify that lock is still held. A second Master against the same database refuses to start. This prevents accidental split-brain; it is not a claim of seamless HA/failover.

Agent state

Persistent agent state under data_dir contains:

  • enrolled agent ID/token
  • last accepted policy
  • pending cleanup/profile jobs
  • disconnected-session timers
  • command deduplication/results
  • profile status
  • bounded event history
  • bounded logon telemetry

The service can therefore continue cleanup/profile/session policy during a master outage using the last accepted policy.

Session event model

The Windows service subscribes to service session-change notifications and also polls WTS. Immediate notifications wake the worker; polling is the reconciliation mechanism.

The agent records:

  • session ID
  • user/domain/SID
  • state
  • client name/address where available
  • logon/connect/last-input timestamps
  • disconnected-since timestamp
  • idle seconds

The master compares consecutive snapshots and emits history events such as logon, reconnect, disconnect, state_change, logoff, and logon_ready.

Broker model

Broker selection is entirely master-side and never modifies the Guacamole database.

A request contains the authenticated username plus Guacamole connection ID/name or an explicit SessionGuard resource/farm. The master resolves the resource and farm, then follows the placement algorithm documented in BROKER.md.

The response contains tokens rather than Guacamole-specific mutable state. The extension injects them into the current user context immediately before the connection parameters are resolved.

Policy hierarchy

Policy precedence:

  1. explicit agent desired policy
  2. policy of a farm explicitly listed in the agent's farm_ids, in configured order
  3. policy of a centrally matching farm (agent_ids or required_tags), deterministic farm-ID order
  4. global policy
  5. agent's local policy if no master desired policy applies

Master policy changes are versioned. Rollback creates a new revision derived from the selected historical version, so history remains append-only rather than being rewritten.

Command model

Administrative actions are queued on the master and delivered through the next heartbeat:

UI -> Master command queue -> heartbeat response -> Agent -> WTS/Win32 -> result -> heartbeat -> audit

Commands have IDs, creation time, expiry time and actor. The agent remembers recently executed IDs to avoid duplicate execution if a heartbeat is retried.

Supported command families include:

  • message session
  • disconnect session
  • logoff session
  • terminate process
  • restart server after drain

Maintenance states

  • online: new and existing connections allowed.
  • drain: no new broker placements; existing sessions may reconnect.
  • maintenance: neither new placement nor reconnect is allowed.

With restart_when_drained, the master queues a restart only once the agent reports zero user sessions.

Health model

The agent calculates a 0100 health score from local checks. Current checks include:

  • CPU pressure
  • memory pressure
  • system-disk free space
  • local RDP listener reachability
  • profile-store reachability when profile sync is enabled

The broker excludes hosts below broker.min_health_score for new sessions. Alerts have independent thresholds.

Failure behavior

Master unavailable

  • Existing Windows sessions continue.
  • Agent keeps its last accepted policy.
  • Profile jobs and disconnect timers continue.
  • Local agent UI remains usable if its OIDC provider is reachable.
  • New Guacamole broker requests fail because authoritative farm placement is unavailable. Do not silently fall back to an arbitrary RDS host if duplicate-profile protection matters.

PostgreSQL unavailable

Master writes fail rather than pretending state was persisted. Existing master in-memory state may still answer reads, but operators should treat the master as degraded and restore DB connectivity.

Profile share unavailable

Backup/restore jobs retry within their configured semantics. A failed backup blocks cleanup. Restore stops retrying after the configured restore window rather than injecting files into an already-running desktop later.

Agent unavailable

It is removed from new broker placement after offline_after_seconds. Offline alerts use their own threshold.