6.5 KiB
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:
- explicit agent desired policy
- policy of a farm explicitly listed in the agent's
farm_ids, in configured order - policy of a centrally matching farm (
agent_idsorrequired_tags), deterministic farm-ID order - global policy
- 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 0–100 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.