Files
sessiongurad/docs/BROKER.md
jbergner e104e7289f
All checks were successful
release-tag / release-image (push) Successful in 2m2s
release-main / release-images (push) Successful in 3m34s
0.4.0
2026-08-22 23:48:15 +02:00

5.6 KiB

Broker, Farms, Apps and Desktops

Goal

The SessionGuard broker provides the Citrix-like placement functions that Guacamole's generic RDP connections do not know about: reconnect affinity, farm boundaries, drain/maintenance state, health-aware placement and user leases.

Farms

A farm is a logical group of RDS hosts.

Membership can be expressed in three ways:

  1. the agent has the farm ID in farm_ids;
  2. the farm has the agent ID in agent_ids;
  3. the agent satisfies all required_tags of the farm.

Example:

{
  "id": "office",
  "name": "Office",
  "required_tags": {
    "role": "office",
    "environment": "production"
  },
  "enabled": true
}

Tags are exact key/value matches. Explicit membership and tag membership can coexist.

Published resources

A Resource maps a Guacamole connection to a farm and optionally to a RemoteApp definition.

Desktop example:

{
  "name": "Office Desktop",
  "kind": "desktop",
  "farm_id": "office",
  "guacamole_connection_name": "Office Desktop",
  "enabled": true
}

RemoteApp example (Guacamole parameters only):

{
  "name": "Sage",
  "kind": "remoteapp",
  "farm_id": "erp",
  "guacamole_connection_name": "Sage",
  "remote_app": "||Sage",
  "remote_app_dir": "C:\\Program Files\\Sage",
  "remote_app_args": "",
  "enabled": true
}

To let SessionGuard publish and continuously reconcile the RemoteApp on all members of the farm, additionally set:

{
  "manage_remote_app": true,
  "remote_app_path": "C:\\Program Files\\Sage\\Sage.exe",
  "remote_app_command_line_setting": 0,
  "remote_app_show_in_portal": false
}

The Master sends this desired state only to Agents that are members of the Resource farm. Agents use the local Terminal Services WMI provider, report the observed state, and never delete unrelated/manual RemoteApps.

Connection ID matching is preferred where stable IDs are known; connection-name matching is case-insensitive and convenient for initial deployment.

Placement algorithm

Given username, optional resource_id, farm_id, and Guacamole connection identity:

  1. Resolve an enabled Resource by explicit resource ID, Guacamole connection ID, or Guacamole connection name.
  2. Resolve the farm: Resource farm → explicit request farm → configured default farm.
  3. If a non-empty farm does not exist or is disabled, fail closed.
  4. If reconnect_existing is enabled, search only matching farm members for an existing Active, Connected or Disconnected session for the user.
  5. maintenance hosts are excluded from reconnect; drain hosts are allowed for reconnect.
  6. Reuse a non-expired lease if its host is still available and belongs to the farm.
  7. For a managed RemoteApp Resource, require the Agent to report that exact Resource as published, path_exists and in_sync with no error. This readiness gate also applies to reconnect and lease reuse.
  8. For a new session, consider only hosts that are:
    • online,
    • in online maintenance mode,
    • members of the farm,
    • at or above min_health_score,
    • application-ready when Agent-managed RemoteApp publication is enabled.
  9. Rank candidates by broker score and select the highest score.
  10. Create or refresh the lease and return connection tokens.

Broker score

The current score deliberately favors health and penalizes load:

health score * 10
- active sessions * 20
- disconnected sessions * 5
- CPU percent * 2
- memory-used percent

This is an intentionally understandable heuristic rather than an opaque ML model. Thresholds and formula can be evolved without changing the Guacamole integration.

User leases

A lease reduces races between two nearly simultaneous connection attempts.

With:

"single_session_per_user": true

the normalized username is the global lease key. The intent is that a user is directed back to one RDS host whenever possible.

With it disabled, the lease key is scoped by:

user | farm | resource

This permits independent sessions in separate published resources/farms.

Leases expire after lease_seconds; successful broker use refreshes the expiry.

Username normalization

Broker comparisons are case-insensitive. DOMAIN\user and matching WTS domain/user fields are normalized before comparison.

In environments with ambiguous duplicate short usernames across domains, pass the domain-qualified username from the identity/gateway layer.

Drain and maintenance

Drain

Use before patching or planned retirement of a server:

  • new sessions: blocked
  • existing-session reconnect: allowed
  • existing sessions: untouched

Optionally set restart_when_drained=true. When zero user sessions remain, a restart command is queued.

Maintenance

Use for a host that must not receive any brokered access:

  • new sessions: blocked
  • reconnect: blocked

Existing Windows sessions are not forcefully killed merely by changing this state; use bulk session actions if required.

Farm policy inheritance

Farm policies are independent from broker selection but use the same membership concepts. Explicit agent.farm_ids determine precedence when an agent intentionally belongs to multiple farms. Centrally inferred memberships are evaluated deterministically by farm ID.

Broker API security

The broker endpoints are not user-OIDC endpoints. They are server-to-server APIs protected by SESSIONGUARD_BROKER_API_KEY.

Recommendations:

  • generate a high-entropy secret;
  • keep Guacamole and Master on a private Docker/network path;
  • do not reuse the enrollment token, OIDC client secret or PostgreSQL password;
  • rotate the key by updating Master and Guacamole together during a controlled maintenance window.