Audio:
- RDP audio output now works through guacamole. Advertise audio/L16
and audio/L8 mimetypes in the guacd handshake, and explicitly set
disable-audio=false. Fixed mimetype mismatch that silently prevented
guacd from creating audio streams.
- Browser AudioContext auto-resumed on user interaction (click/keydown)
to comply with autoplay policy.
Video performance:
- Per-entry GFX pipeline toggle (enable_gfx) — enables RemoteFX codec
- Per-entry desktop composition toggle (enable_desktop_composition)
- Per-entry force lossless toggle (force_lossless) — PNG-only mode
- WebSocket proxy buffer increased from 8KB to 64KB
- Video Performance section in address book UI for RDP entries
Documentation:
- RDP Video Performance guide with Windows server tuning (AVC444,
60fps, GPU encoding) and Linux xrdp setup (Debian 13)
- contrib/setup-xrdp-gfx.sh — automated GFX/H.264 setup for xrdp
- contrib/setup-xrdp-audio.sh — automated PulseAudio module build
Session history:
- New session_history SQLite table tracks all sessions with user,
entry, folder, hostname, duration, and recording file
- Automatic cleanup via session_history_retention_days config (default 90)
- 8 new unit tests for session history DB functions
Reports page (poweruser+ only):
- Summary cards: total sessions, hours, unique users, active now
- Sortable/filterable session history table with pagination
- Top Connections and Top Users leaderboards
- CSV export for session history with filters
Enriched recordings:
- Recording .meta sidecars now include user, folder, entry name, session type
- Recordings API returns enriched metadata from .meta files
- Recordings page shows User, Entry, Folder columns
Other:
- Reports nav link on all pages, hidden for operator/viewer roles
- Reports documentation in docs/reports.md
- Onboarding wizard for new users (role-scoped, dismissable)
- Settings menu label visibility improvement
- Fixed stale credential variable test (hyphen support)
Credential variables — address book entries reference $domain_username /
$domain_password instead of storing static credentials. Users fill in their
own values via My Credentials (gear menu), stored per-user in Vault KV.
All variables set → silent launch; missing → prompted. Hyphens allowed in
variable names. Docs section added.
Bug fixes:
- Rate limiting disabled by default; opt-in via rate_limit = true (#62)
- Docker: copy FreeRDP guac-common-svc plugins to runtime image (#64)
- Docker/install: add chromium-sandbox package for non-root web sessions (#61)
- Logo: skip redundant JS src= when server-side branding already set (#65)
- Sessions page: hide Open/Share buttons for non-active sessions (#63)
- Drive: expose drive_configured in /api/auth/status, warn in UI when
[drive] not configured
- install.sh: verify FreeRDP plugin installation
UI polish:
- Nav bar: border separator + spacing between header and nav on all pages
- Address book: password show/hide toggle on all password fields
- Drive diagnostic logging (session.rs, websocket.rs, client.html)
Closes#61, #62, #63, #64, #65
- Add optional `banner` field to address book entries (shown before session
starts, user must click Continue). No longer auto-populates from display_name.
- Restructure web entry form: username, password, login script, and autofill
collapsed under a collapsible "Automation" section.
- Filter login scripts dropdown to .js/.sh/.py files only (skip package.json etc.)
- Fix CDP/login scripts: change DeveloperToolsAvailability policy from 2 (disabled)
to 0. DevTools UI remains blocked by chrome://* URLBlocklist. Fixes login script
automation that was silently broken by the v0.6.0 security hardening.
- Update Dockerfile, debian/postinst, install.sh with corrected policy.
- Update docs/security.md and docs/web-sessions.md.
Chromium refuses to start as root without --no-sandbox, causing a
silent black screen in Docker. Now detected via euid check — bare
metal installs with the SUID sandbox helper are unaffected.
Also adds a 500ms post-spawn liveness check: if Chromium exits
immediately (sandbox failure, missing libs, etc), stderr is captured
and logged, and the session fails with a clear error instead of
leaving Xvnc running with a black screen.
Add GET /api/addressbook that returns all visible folders with entries
in a single response. Eliminates N+1 fetch pattern (list folders, then
entries per folder) that triggered 429 rate limiting on page load.
Address book UI now uses batch endpoint for initial load, falls back to
per-folder fetch after mutations (create/edit/delete).
Rate limits increased: API 20/s burst 100 (was 5/s burst 30), session
create 2/s burst 10 (was 1/s burst 5), WebSocket 5/s burst 50 (was
1/s burst 20).
- Rewrite Vault mTLS client to build rustls ClientConfig directly,
bypassing reqwest::Identity::from_pem() which fails with rustls
backend for PKCS#8 keys from OpenBao/Vault PKI
- Make cert_path/key_path optional in [tls] — guacd TLS now works
independently of server HTTPS (for reverse proxy setups)
- Add webpki-roots direct dependency for custom TLS config
- Add mTLS tests: PKCS#8 key, fullchain cert, tls_skip_verify
- Update docs: configuration.md, security.md
Closes#51, closes#49
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Allow dead_code on module (used via CLI subcommand, not direct calls)
- Simplify bool toggle: in_quote = !in_quote
- Use iterator instead of index loop in deduplicate_names
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New features:
- Native Chromium autofill: pre-populate Login Data SQLite before launch,
zero external deps (no Node.js/Playwright needed for simple login flows)
- Per-entry domain allowlisting: restrict which domains Chromium can reach
via --host-rules (separate from server-side web_allowed_networks CIDR)
- Per-entry clipboard control: disable-copy and disable-paste for all
session types (SSH, RDP, VNC, Web) via guacd native parameters
- Guacamole import: `rustguac import-guacamole` parses mysqldump SQL and
writes entries to Vault address book
Security hardening:
- Comprehensive Chromium managed policy deployed via install.sh, Dockerfile,
and debian/postinst (blocks DevTools, downloads, file dialogs, extensions,
dangerous URL schemes)
- Profile isolation: each web session gets a unique UUID-based profile dir
- Autofill credentials encrypted with Chromium's native os_crypt (AES-128-CBC)
Documentation:
- Updated README, docs/api.md, docs/security.md, docs/configuration.md,
docs/overview.md, docs/integrations.md with all new features
- Clarified two-layer domain restriction (web_allowed_networks vs allowed_domains)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
RemoteApp/RAIL (closes#19):
- Pass remote-app, remote-app-dir, remote-app-args through to guacd
- Address book UI: collapsible RemoteApp section for RDP entries
Recording rotation:
- New [recording] config section (backwards-compatible with recording_path)
- Automatic disk-space management: max_disk_percent, max_recordings thresholds
- Background rotation task with configurable interval
- Sidecar .meta JSON files track address book entry per recording
Per-entry recording overrides:
- Address book entries can enable/disable recording and set max recordings
- Per-entry rotation runs on session disconnect
- UI: collapsible Recording Settings section for all connection types
Bump version to 0.4.0
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix open redirect via protocol-relative URLs (//evil.com) in OIDC next parameter
- Add Secure flag to all cookie-clearing Set-Cookie headers
- Add single-quote escaping to html_escape() (defence-in-depth)
- Cross-check OIDC state cookie against state query parameter in callback
- Switch API key and user token validation to constant-time hash comparison (subtle)
- Add 3 new fuzz targets: api_input, vault_response, websocket_message
- Bump version to 0.3.3
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When an address book entry has prompt_credentials: true or no stored
credentials, /api/connect now returns an inline credential form instead
of failing or connecting without auth. The form POSTs to the existing
connect endpoint and redirects to the client page.
Fix NetBox webhook body template docs: use "type" not "session_type"
(matches Vault storage format), replace regex_replace/cut filters with
standard Jinja2 equivalents (lower, split) since NetBox's Jinja2
environment doesn't include Ansible or Django template filters.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New GET /api/connect quick-connect endpoint for external integrations
- Ad-hoc mode (poweruser+): hostname/protocol/port params, redirects to client
- Address book mode (operator+): scope/folder/entry params, credentials from Vault
- Unauthenticated users redirected through SSO login and back automatically
- OIDC deep-link support: login handler accepts ?next= param, stores as cookie,
callback redirects there instead of /addressbook.html after authentication
- New docs/netbox.md integration guide: Custom Fields, Custom Links with
console_enabled/console_mode gating, webhook-driven address book sync
- Updated docs/api.md with GET /api/connect endpoint documentation
- Updated screenshots/screenshots.md with descriptions for all screenshots
- Bump version to 0.3.1
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
russh 0.54.1 fixes CVE-2025-54804 (integer overflow panic in channel
window adjust). Adapts tunnel.rs to the new API: AuthResult enum
instead of bool, native async trait instead of #[async_trait], and
keys module re-exported from russh directly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Multi-hop SSH tunnel chains allow routing any session type through
multiple bastion hosts. VNC is now a first-class session type.
Web browser sessions can tunnel through jump hosts with automatic
URL rewriting.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Patch guacd with Kerberos NLA support (002-kerberos-nla.patch),
based on upstream GUACAMOLE-2057 PR #581, adapted for FreeRDP 3.x
- Add per-entry auth_pkg, kdc_url, and prompt_credentials settings
to the address book (configurable in admin UI)
- Frontend credential prompt for entries without stored credentials
or with prompt_credentials enabled (never stored, session-only)
- Wire auth-pkg, kdc-url, kerberos-cache params through rustguac
to the guacd RDP handshake
- Comprehensive Kerberos NLA docs: krb5.conf setup, KDC discovery
options, FQDN requirements, troubleshooting guide
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
User API tokens allow OIDC users to authenticate via bearer token for
automation and scripting. Powerusers and admins can create their own
tokens; admins can create tokens for operators. Tokens use SHA-256
hashing, optional max_role caps, optional expiry, and full audit
logging of create/revoke operations with client IPs.
- DB schema: user_api_tokens and token_audit_log tables
- Auth middleware: validates user tokens as fallback after admin keys
- API: 7 new endpoints (self-service + admin token management)
- UI: tokens.html (self-service) + admin.html token/audit sections
- Nav: Tokens link added to all pages (visible for operator+)
- Docs: API reference, security model, roles/access control updated
- Background cleanup: expired tokens + 90-day audit log retention
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add cargo-fuzz targets for the Guacamole protocol parser:
- protocol_parse: single instruction parsing
- protocol_stream: streaming parser with chunked input
Fix panic in Instruction::parse when a length prefix splits a multi-byte
UTF-8 character (found by fuzzer within seconds). Now returns
ParseError::Truncated instead of panicking on invalid char boundary.
Run with: cargo +nightly fuzz run protocol_parse
cargo +nightly fuzz run protocol_stream
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Vault TLS: replace hardcoded danger_accept_invalid_certs(true) with
configurable tls_skip_verify option (default: false)
- Share tokens: use constant-time SHA-256 hash comparison to prevent
timing side-channel attacks
- OIDC pending states: add 10-minute TTL, evict stale entries on each
login to prevent unbounded HashMap growth
- Recording path traversal: add canonical path validation as defense-
in-depth alongside existing string checks
- Frontend XSS: escape all user-controlled data (filenames, paths) in
innerHTML via escapeHtml/escapeAttr in client.html and recordings.html
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>