Update mit Guacamole-Extension
release-tag / release-image (push) Successful in 2m5s
release-main / release-images (push) Successful in 5m5s

This commit is contained in:
2026-08-22 15:19:17 +02:00
parent 0f4a234f52
commit 7972ed7e38
45 changed files with 6448 additions and 402 deletions
+254 -95
View File
@@ -1,119 +1,278 @@
# SessionGuard
SessionGuard is a Go-based management layer for Windows Remote Desktop Session Hosts. It is intended for environments that use Guacamole or another access gateway and want a small subset of the operational features commonly provided by Citrix management/profile components.
SessionGuard is a Go-based **RDS control plane** for Windows Remote Desktop Session Hosts. It is designed to complement Apache Guacamole: Guacamole remains the HTML5/RDP gateway, while SessionGuard provides Citrix-like broker, Director, policy, profile-lifecycle and operations functions.
## Implemented MVP
**Current development version: 0.3.0 (Broker & Director production candidate)**
- Windows service agent
- RDS/WTS session inventory
- delayed profile deletion after a real session disappears
- safety exclusions, allowed profile roots, retries, and dry-run mode
- per-user template enforcement for files, directories, `.url` links and `.lnk` shortcuts
- server basics: hostname, Windows version/build, uptime, RAM
- local agent dashboard
- Linux/Docker master dashboard for all agents
- outbound agent-to-master heartbeats
- bootstrap enrollment followed by per-agent bearer credentials
- per-agent policies and "apply to all agents"
- Pocket ID / generic OIDC authentication for master and local agent UI
- last-known policy continues to work if the master is unavailable
> SessionGuard is not an ICA/HDX implementation and does not replace the Windows RDS runtime. It deliberately reuses standard RDP/WTS, Guacamole and PocketID/OIDC.
## Important safety note
## 0.3.0 at a glance
Profile deletion is destructive. Start with `dry_run: true`, verify exclusions and `allowed_profile_roots`, test on a non-production RDS host, and only then disable dry-run. SessionGuard calls the Windows user-profile deletion API; it does not recursively delete arbitrary profile paths itself.
### Smart broker and farms
## Build
- Logical farms with explicit agent membership and/or required tags.
- Published resources for full desktops and RemoteApps.
- Existing-session reconnect affinity before new load balancing.
- Persistent user leases to reduce duplicate sessions across hosts.
- Optional global single-session-per-user behavior.
- Health-aware placement using server health, sessions, CPU and memory pressure.
- `online`, `drain` and `maintenance` server modes.
- `restart_when_drained`: stop new allocations, wait for all user sessions to leave, then queue a controlled restart.
- Farm and global policy inheritance, plus per-agent override.
Requirements: Go 1.23+ and Internet access for the Go modules on the first build.
### Guacamole 1.6 bridge
`guacamole-extension/` contains a small Java extension using Guacamole's extension API. It does not authenticate users. It decorates the already authenticated Guacamole user context, asks the SessionGuard broker for connection tokens and injects:
- `${SESSIONGUARD_HOST}`
- `${SESSIONGUARD_AGENT_ID}`
- `${SESSIONGUARD_RESOURCE_ID}`
- `${SESSIONGUARD_REMOTE_APP}`
- `${SESSIONGUARD_REMOTE_APP_DIR}`
- `${SESSIONGUARD_REMOTE_APP_ARGS}`
This keeps PocketID / header authentication and the browser gateway separate from broker logic. See `docs/GUACAMOLE.md`.
### Director-style operations
- Consolidated server and session inventory.
- RDS logon/connect/last-input timestamps and calculated idle time.
- Per-session process inventory and remote process termination.
- CPU, memory and system-disk telemetry.
- RDP-listener and profile-store health checks.
- Composite server health score used by both the UI and broker.
- Session history for logon, reconnect, disconnect, logoff and SessionGuard `logon_ready` events.
- Logon telemetry including SessionGuard restore duration and observed time until the post-restore/template stage is ready.
- Active alerts for offline agents, CPU/RAM/disk/health, profile failures, disconnected-session count and slow observed logon.
- Optional generic JSON webhook notifications with notification throttling.
- Prometheus-compatible `/metrics` endpoint.
### Enterprise control plane
- PostgreSQL persistence for production with a database advisory lock preventing accidental active/active masters.
- Append-only audit and session-history tables with retention and indexes.
- JSONB control-plane state for agents, farms, resources, leases, policies and alerts.
- JSON-file persistence remains available as a small single-node fallback / migration path.
- PocketID / generic OIDC authentication.
- Group-to-role RBAC.
- Policy version history and rollback for global, farm and agent policies.
- Agent-initiated HTTPS heartbeats and command delivery only; no inbound management port from master to RDS hosts.
- Expiring, deduplicated command queue with result acknowledgement and audit trail.
### Windows agent and profile lifecycle
The v0.2 functionality remains:
- Windows service with native WTS session inventory and session-change wakeups.
- Send message, disconnect and logoff user sessions.
- Automatic logoff of disconnected sessions after a persistent configurable timeout.
- Backup of selected profile folders after logoff.
- Versioned transactional profile snapshots (`staging -> current`).
- Restore of the last successful snapshot on a newly observed logon.
- A failed backup blocks local profile deletion.
- Cleanup with grace period, exclusions, allowed profile roots, final WTS recheck and `DeleteProfileW`.
- Structured templates for files, folders, `.url` and `.lnk` shortcuts.
- Local PocketID/OIDC-protected fallback UI on every agent.
See `docs/PROFILE-LIFECYCLE.md` for the deliberately limited scope of profile synchronization.
## Target architecture
```text
PocketID
|
Traefik / trusted header-auth boundary
|
Apache Guacamole 1.6
+ sessionguard-guacamole extension
|
| broker token request
v
SessionGuard Master ---------------- PostgreSQL
|
| outbound agent heartbeats carry policy/commands/results
|
+-------------+-------------+
| | |
RDS01 RDS02 RDS03
Agent Agent Agent
```
## Quick start: master
Copy the example configuration and environment file:
```bash
cp configs/master.example.json deploy/master.json
cp deploy/.env.example deploy/.env
```
Set at least:
- `SESSIONGUARD_DB_PASSWORD`
- `SESSIONGUARD_ENROLLMENT_TOKEN`
- `SESSIONGUARD_BROKER_API_KEY`
- `SESSIONGUARD_OIDC_CLIENT_SECRET`
- PocketID issuer/client/redirect values in `master.json`
- `TRAEFIK_NETWORK`
Then:
```bash
cd deploy
docker compose up -d --build
```
For production, expose the master only through your existing TLS reverse proxy. The bundled compose publishes port 8080 only on `127.0.0.1` as an additional safety measure.
## Quick start: Windows agent
Build on a machine with Go module access:
```powershell
.\scripts\build.ps1
```
Or:
Copy `configs/agent.example.json`, configure PocketID, the master URL and your initial policy, then install from an elevated PowerShell:
```bash
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -o sessionguard-agent.exe ./cmd/agent
go build -o sessionguard-master ./cmd/master
```powershell
.\scripts\install-agent.ps1 -BinaryPath .\dist\sessionguard-agent.exe -ConfigPath .\configs\agent.json
```
## Master deployment
For SMB profile stores, prefer a gMSA and grant it the required share/NTFS permissions. Do not store SMB passwords in the agent JSON.
1. Copy `configs/master.example.json` to `deploy/master.json` and edit it.
2. In Pocket ID create an OIDC client whose callback URL is `https://sessionguard.example.org/oidc/callback`.
3. Restrict the Pocket ID client to the intended admin group and configure the same group in `admin_groups`.
4. Put a TLS reverse proxy in front of the master.
5. Start with `docker compose -f deploy/docker-compose.yml up -d --build`.
## Guacamole integration
The container binds the example host port only to `127.0.0.1`; publish it through your reverse proxy rather than exposing plain HTTP.
Build a Guacamole image containing the extension using `deploy/guacamole/Dockerfile.guacamole`. Add these environment variables to Guacamole:
## Agent deployment
1. Copy `configs/agent.example.json` to `configs/agent.json` and edit the master URL, enrollment token and OIDC settings.
2. Create a Pocket ID OIDC client for the agent's management URL, for example `https://ts01-mgmt.example.org/oidc/callback`.
3. Build the Windows agent.
4. Run `scripts/install-agent.ps1` from an elevated PowerShell prompt.
5. Keep `dry_run: true` until profile cleanup has been observed successfully.
The service is installed as LocalSystem by default. If a template `source` points to a UNC share, grant read access to the server computer account (`DOMAIN\SERVER$`) or change the service identity to an appropriate gMSA/service account. Do not put share passwords in the SessionGuard JSON configuration.
## Example templates
```json
[
{
"id": "support",
"kind": "url",
"target": "Desktop\\Support.url",
"url": "https://support.example.org",
"overwrite": true
},
{
"id": "erp",
"kind": "shortcut",
"target": "Desktop\\ERP.lnk",
"overwrite": true,
"shortcut": {
"target": "C:\\Program Files\\ERP\\erp.exe",
"arguments": "--terminal"
}
},
{
"id": "defaults",
"kind": "file",
"target": "AppData\\Roaming\\Example\\defaults.json",
"source": "\\\\fileserver\\templates\\defaults.json",
"overwrite": true
}
]
```yaml
SESSIONGUARD_MASTER_URL: http://sessionguard-master:8080
SESSIONGUARD_BROKER_API_KEY: ${SESSIONGUARD_BROKER_API_KEY}
SESSIONGUARD_BROKER_TIMEOUT_MS: "2500"
```
All `target` values are relative to the user's profile. Attempts to escape the profile root are rejected.
## Local management during a master outage
The Windows agent continues cleanup and template work using its persisted policy. Its local web UI remains available independently of the master, provided Pocket ID is reachable. A locally saved emergency policy remains in effect until the master reconnects; if the master already has a different desired policy for that agent, the master policy is then reapplied.
## Pocket ID notes
SessionGuard requests the `openid`, `profile`, `email` and `groups` scopes. Use `admin_groups` as a second authorization check in addition to Pocket ID's client-side allowed-group restriction. The master and agent should be served over HTTPS and `secure_cookie` should remain enabled.
## What is intentionally not implemented yet
Full Citrix-style profile roaming/restoration is not part of v0.1. Copying an entire profile, especially `NTUSER.DAT` and registry-backed settings, after Windows has loaded that profile is unsafe. See `docs/ARCHITECTURE.md` for the recommended extension path.
## Repository layout
A brokered desktop connection uses:
```text
cmd/master Linux/Docker master binary
cmd/agent Windows service binary
internal/agent agent lifecycle, cleanup, heartbeat, local UI
internal/master enrollment, dashboard, policy distribution
internal/windowsx WTS, profile and Windows server APIs
internal/templates template comparison/application
internal/auth Pocket ID / OIDC login
configs example JSON configurations
deploy Docker Compose example
scripts build/install helpers
docs architecture notes
hostname = ${SESSIONGUARD_HOST}
```
A RemoteApp can additionally use the `SESSIONGUARD_REMOTE_APP*` tokens. Map the Guacamole connection ID or name to a SessionGuard Resource in the master UI.
**Important:** with Guacamole header authentication, untrusted containers or networks must not be able to reach Guacamole directly and forge the authentication header. See `docs/GUACAMOLE.md` and `docs/SECURITY.md`.
## Master environment overrides
Secrets do not need to be stored in JSON:
| Variable | Purpose |
|---|---|
| `SESSIONGUARD_DATABASE_URL` | PostgreSQL DSN |
| `SESSIONGUARD_ENROLLMENT_TOKEN` | one-time agent enrollment shared secret |
| `SESSIONGUARD_BROKER_API_KEY` | Guacamole-extension → broker credential |
| `SESSIONGUARD_OIDC_CLIENT_SECRET` | master PocketID/OIDC client secret |
| `SESSIONGUARD_ALERT_WEBHOOK_URL` | optional alert webhook |
Agent overrides:
| Variable | Purpose |
|---|---|
| `SESSIONGUARD_MASTER_URL` | master URL |
| `SESSIONGUARD_ENROLLMENT_TOKEN` | enrollment secret |
| `SESSIONGUARD_OIDC_CLIENT_SECRET` | local agent UI OIDC secret |
## Broker placement order
1. Resolve an enabled Published Resource if one matches the requested Guacamole connection ID/name.
2. Determine the requested/default farm.
3. If enabled, reconnect a matching existing Active/Connected/Disconnected session in that farm. A `drain` host may be used for this; `maintenance` may not.
4. Reuse a non-expired matching user lease if possible.
5. Select only online, healthy, `online`-mode members of the farm.
6. Score candidates using health, active/disconnected sessions, CPU and memory pressure.
7. Create/refresh the user lease and return connection tokens.
A farm boundary is fail-closed: a session on another farm is not reused for a request for the selected farm.
## Policy precedence
```text
agent override
> explicit farm membership (order in agent.farm_ids)
> centrally selected farm (agent_ids / required_tags, deterministic farm-ID order)
> global policy
```
All policy writes are normalized and validated before activation. Every global/farm/agent policy write creates a version that can be rolled back from the UI/API.
## RBAC roles
Built-in roles are:
- `viewer`
- `helpdesk`
- `operator`
- `profile_admin`
- `policy_admin`
- `auditor`
- `admin`
PocketID `groups` claims are mapped in `master.json`. If the RBAC group mapping is intentionally empty, SessionGuard preserves backwards compatibility and grants authenticated users the `admin` role; do not leave it empty in a multi-admin production environment.
See `docs/RBAC.md`.
## Profile scope and safety
SessionGuard synchronizes selected **profile folders**, not a complete Windows profile. Do not configure `NTUSER.DAT`, registry hives, the whole profile root, EFS material or Windows-owned profile internals as roaming content.
Good candidates include:
- `AppData\Roaming\Vendor\Product`
- `Desktop`
- `Documents`
- application-owned configuration folders safe to merge after logon
If the profile backup fails, cleanup is not scheduled. If the user reconnects before snapshot activation or immediately before cleanup, the dangerous operation is cancelled.
## Build and test
Requirements:
- Go 1.23+
- Windows Server 2019/2022/2025 x64 for agent integration testing
- Maven/JDK 11+ for the Guacamole extension
- PostgreSQL 17 recommended for production
Go:
```bash
go mod tidy
go test ./...
go vet ./...
```
Guacamole extension:
```bash
cd guacamole-extension
mvn -DskipTests package
```
The current source tree contains unit tests for configuration, templates, profile snapshot behavior and broker placement invariants. A real release should additionally pass the Windows/RDS/SMB/PostgreSQL integration matrix in `docs/TESTING.md`.
## Documentation
- `docs/ARCHITECTURE.md` – components, state and trust boundaries
- `docs/BROKER.md` – farms, resources, leases, scoring and maintenance states
- `docs/DIRECTOR.md` – telemetry, history, processes, alerts and health scoring
- `docs/GUACAMOLE.md` – extension and integration with Guacamole/PocketID
- `docs/PROFILE-LIFECYCLE.md` – profile backup/restore/cleanup pipeline
- `docs/RBAC.md` – roles and PocketID group mapping
- `docs/API.md` – HTTP API
- `docs/OPERATIONS.md` – deployment, upgrade, backup and recovery
- `docs/SECURITY.md` – security model and hardening
- `docs/TESTING.md` – release and failure-mode test matrix
- `CHANGELOG.md` – release changes
## Production-candidate status
The design intentionally fails closed around destructive profile operations and broker farm boundaries. Nevertheless, v0.3.0 should be introduced as a canary before broad production rollout. In particular, validate native WTS behavior, SMB failure/recovery, PostgreSQL backup/restore, Guacamole extension loading and your exact PocketID group claims in your environment.