Commit Graph

23 Commits

Author SHA1 Message Date
Jad Seifeddine d75446f040 connections: open ad-hoc connections in the entry editor
Ad-hoc sessions had their own form on the Sessions page with a subset
of the options a connection entry has. "Connect to a host" now opens
the Connections entry editor in an ad-hoc mode instead, so an ad-hoc
connection has every option an entry has, now and as entries gain more.

The button sits beside the global search box on every page that has
one. It is shown to poweruser and above under the same rules as the
search box, and opens the editor in place on Connections or goes to
/connections.html#adhoc elsewhere. Ad-hoc mode has no name or folder,
Connect instead of Save, and for admins a "Save as entry" action.

POST /api/addressbook/adhoc/connect takes the entry as the editor
builds it. It resolves only the caller's own credential variables,
never lends folder defaults, and goes through apply_adhoc_policy and
the session rate limit like every other ad-hoc route.
POST /api/sessions is unchanged.

session_request_from_entry() replaces the two copies of the entry to
session request conversion, used by the Connections connect endpoint
and /api/connect, so an option added to entries works in all three.
can_connect_adhoc() is the one place that decides who may connect
ad-hoc.

The Sessions ad-hoc form is removed. loadFolders() now runs its
callback, so #open= links work again.

Refs sol1/rustguac#295

(cherry picked from commit dd40bf08ec)
2026-10-20 08:54:46 +11:00
Jad Seifeddine dd6ddf3763 docs: link the SPICE and Proxmox VE page from the other docs
Add the page to the Documentation lists in the README and overview, and
link it where SPICE and Proxmox come up: the session types in the
overview, the session and entry fields in the API reference, the
vnc_allowed_networks row in the configuration reference, and the entry
types and jump-host table in integrations. Those two lists also now name
SPICE and Proxmox VE, which they had left out, and no longer claim that
every session type can use a jump host, since VDI cannot.

(cherry picked from commit 638c579402)
2026-10-20 08:54:31 +11:00
Jad Seifeddine 1450164eca docs: use reserved example names in API and audit log samples
A few examples used real-world names instead of reserved ones:

- the web session example pointed at www.saucedemo.com, a real
  third-party site, with that site's demo login; it now uses
  app.example.com and neutral credentials;
- the jump host examples used *.corp.local, and .local is the mDNS
  domain, so copied settings resolve oddly; they now use
  *.example.internal, as does the Squid proxy example;
- the move-entry response showed the folder "mrgelato"; now
  "customer-a";
- the CLI audit log sample showed the user "dave"; now "alice", with
  the columns kept aligned;
- the commented-out Knocknoc redirect in 403.http suggested
  knocknoc.mycompany.com, a registered domain; now knocknoc.example.com.

Fixes #304

(cherry picked from commit b43de8678a)
2026-10-20 08:54:31 +11:00
Jad Seifeddine 80d6bf82ed docs: list SPICE, Proxmox VE and VDI in the jump host session types
The supported session types table for jump hosts listed only SSH, RDP,
VNC and web, although session creation also tunnels SPICE and Proxmox
VE. Add rows for them as the code behaves: SPICE forwards to the TLS
port when TLS is on, and Proxmox VE tunnels the PVE API and the SPICE
proxy separately, with certificate checks as they apply to each hop.
VDI is marked not applicable, since the container is local and the UI
hides the jump host section for it.

Also note how the network allowlists apply through a chain: the target
is resolved by the bastion, and only the first jump host is checked
locally, for ad-hoc sessions from non-admins.

Fixes #301

(cherry picked from commit 15390667b8)
2026-10-20 08:54:30 +11:00
Dave Kempe 8df2f254eb vault: JWT token_command, JWT login in the CLI tools, checked settings
On top of the workload JWT login from #285:

- token_command: a program and its arguments (run directly, never
  through a shell) that prints the JWT, run again at every login, with a
  timeout. For issuers that hand out a token on request, such as a cloud
  metadata service. Its output never reaches an error or the log.
- import-guacamole and vault-migrate log in with jwt_auth too; they
  insisted on VAULT_SECRET_ID. One helper, VaultConfig::login_secret,
  now decides the secret for the server and both commands.
- jwt_auth is checked before any login: the mount goes into the login
  URL, so plain path segments only, and exactly one of path and
  token_command.
- Docs: the jwt_auth keys, and setting it up with Nomad workload
  identity, SPIRE (spiffe-helper), and cloud VM identity tokens, with a
  note on when JWT login really removes the stored secret.
2026-10-18 13:23:14 +11:00
Christopher Svensson df5afd9742 Add Vault and OpenBao workload JWT authentication
(cherry picked from commit 5c453f708d)
2026-10-18 13:23:14 +11:00
Dave Kempe 3e6c1d2ce1 docs: add-admin --if-none, and the Entra Enterprise application signature failure (#237) 2026-10-06 10:41:51 +11:00
Dave Kempe 627c05074b docs(oidc): document outbound HTTP proxy (Squid) egress 2026-07-06 20:12:44 +10:00
Dave Kempe 575d4ab798 docs(integrations): add Microsoft Entra ID setup guide
Covers the claim-vs-scope distinction that trips Entra setups when users
copy the Authentik example. Includes the AADSTS650053 error explainer
plus a troubleshooting section. Promised in #153.
2026-06-03 15:19:57 +10:00
Dave Kempe 70763932ca fix(drive): honour cleanup_on_close and retention_secs at session end
Closes #123 (parts 2 + 3 only; upload disconnect and drag-drop UX
postponed for further testing).

cleanup_browser hardcoded retention_secs=0 and never consulted
cleanup_on_close, so the [drive] config flags were dead code at
end-of-session teardown. This was visible to operators as
"cleanup_on_close = false has no effect" and "retention_secs = 0 wins
over cleanup_on_close = false" - the same root cause for both reports.

  - cleanup_browser now takes (cleanup_on_close, retention_secs) and
    only calls drive::cleanup_session_dir when cleanup_on_close = true,
    passing retention_secs through. session.drive_path is still cleared
    either way so subsequent reads don't think we still own the dir.
  - drive_cleanup_settings() centralises the resolution from the
    optional [drive] config; absent config uses the legacy "always
    wipe immediately" defaults so installs that never enabled drive
    keep prior behaviour.
  - All four cleanup_browser call sites updated (delete, complete,
    error, pending-timeout).
  - Three new unit tests covering the resolution helper.
  - Docs gain a "Cleanup behaviour" subsection in the Drive section
    explaining the precedence (retention_secs only matters when
    cleanup_on_close=true) and the per-session UUID subdir model that
    means files do not persist across sessions even with cleanup
    disabled. The cross-session "personal drive" model would be a
    separate feature.
2026-05-06 15:57:26 +10:00
Dave Kempe 4aa18e1f32 docs(authentik): add missing groups scope mapping step
Closes #122.

Authentik does not ship a `groups` scope mapping by default, so the
existing instruction "Add the `groups` scope" in the provider's Advanced
protocol settings has nothing to select. Operators following the guide
end up with rustguac unable to read group memberships, which silently
breaks group-to-role mapping.

Adds a new step 1 explaining how to create a Scope Mapping under
Customisation > Property Mappings with the standard
`request.user.ak_groups` expression, and updates the provider step to
reference the newly-created mapping by name. Subsequent steps renumbered.
2026-05-06 15:57:26 +10:00
Dave Kempe c7554fec1d docs: emphasise vault/openbao as a dependency, point at quickstart
The Connections UI is the main user-facing rustguac feature, and it does
not work without Vault or OpenBao. The previous docs framed this as
"recommended" or "optional Vault-backed connections", which understated
how much of rustguac depends on the secret store being there.

  - README.md gains a Requirements table listing guacd (bundled),
    vault/openbao (required for Connections), OIDC (optional), Docker
    (optional). The Features bullet now points at it.
  - docs/installation.md upgrades step 4 from "(Recommended)" to
    "(Required for connections)" with explicit "without one of these,
    the Connections UI is unavailable" wording. Includes the
    quickstart one-liner and a --local example.
  - docs/overview.md drops "optional" from the Vault-backed connections
    description and adds a follow-up paragraph pointing at the
    integrations doc and the quickstart script.
  - docs/integrations.md adds a Quickstart subsection above the manual
    walkthrough with a mode table, three example invocations, and the
    --local on-disk-unseal security caveat reiterated where it'll be
    seen by anyone reading the section.
2026-05-06 15:57:26 +10:00
Dave Kempe 542474dc3a v1.6.6 polish: OIDC error clarity, xrdp TLS perms, aurora default, reverse-proxy docs
### OIDC discovery error wrap

src/oidc.rs now reshapes the openidconnect crate's "unexpected issuer
URI `X` (expected `Y`)" validation error into something an operator
can act on without having to reason about which side is the config
and which is the provider. For the overwhelmingly common
trailing-slash case the message collapses to a single actionable
line; for the rarer case where the two URIs differ materially (wrong
tenant, copy-pasted authorise URL, Keycloak path change), it falls
back to a config/provider/fix three-liner. Raw Debug output is
preserved for any non-mismatch discovery failure. Works for any
OIDC provider, not just JumpCloud.

### xrdp TLS key permissions (contrib/setup-xrdp-gfx.sh)

After rebuilding xrdp from sid, the `adduser xrdp ssl-cert` step
from the Debian postinst doesn't always re-apply cleanly, leaving
the xrdp user unable to read /etc/xrdp/key.pem. xrdp then falls
back to "classic RDP security" and FreeRDP drops the connection
with a MAC checksum error. New Step 9 in Phase 3 explicitly adds
xrdp to ssl-cert and normalises the key's ownership/mode. Step
list in the help text renumbered to match.

### Aurora default when [theme] is absent

src/main.rs resolved the theme via ThemeConfig::resolve() only when
config.theme was Some(..), and otherwise fell through to a hardcoded
("dark", builtin_presets()[0].1.clone()) pair. That meant aurora
only kicked in when a [theme] section was present in config.toml,
even an empty one. ThemeConfig now derives Default (all fields
Option<String>, so zero-cost), and main.rs resolves via
config.theme.clone().unwrap_or_default().resolve() so absent and
empty [theme] behave identically. Regression test added.

### docs/reverse-proxies.md

New doc covering nginx, Caddy, Apache, and Traefik with per-proxy
configs and the %2F-decoding gotcha that causes 404s on nested
subfolder paths when a proxy normalises the URI before forwarding.
The issue was surfaced by @mauroparente in #105 — thanks for the
repro and the nginx config that made the root cause obvious.
deployment-guide.md Step 3 and integrations.md HAProxy section
both link to the new doc.
2026-04-25 15:40:08 +10:00
Dave Kempe 0d69e8fed4 Rename Address Book → Connections; allowed_groups picker; session privacy (#102)
Three pieces of v1.6.0 work that happened together and are easier to
review as one save point.

Rename: Address Book → Connections
- static/addressbook.html renamed to static/connections.html
- Nav links, page titles, empty states, onboarding, and prose updated
  across all 8 static pages (connections, admin, docs, index,
  recordings, reports, sessions, tokens).
- README, CLAUDE.md, and every file under docs/ updated.
- src/main.rs: connections.html added to the branded-page map and
  route list; /addressbook.html returns a 308 permanent redirect so
  existing bookmarks keep working.
- Backend API paths, Rust types, and Vault storage paths are
  deliberately unchanged — internal only.

Folder allowed_groups picker
- New SQLite table `seen_groups` tracks OIDC groups observed in any
  user login; OIDC callback upserts after extracting groups.
- `GET /api/auth/known-groups` (admin-only) returns the union of
  group_role_mappings and seen_groups.
- `GET /api/addressbook/folders/{scope}/{folder}/config` adds the
  missing endpoint the frontend was already calling — existing
  allowed_groups now prefill the edit-folder modal.
- Folder modal swaps the free-text comma-separated input for a chip
  picker with a themed combobox dropdown: autocomplete over known
  groups, keyboard nav, "+ add custom" row for unlisted groups.

Active session visibility (GitHub #102)
- `GET /api/sessions` scopes to the caller's own sessions by default;
  `?all=true` lets admins opt in (used by the Sessions page).
- `GET /api/sessions/{id}` and the thumbnail GET/PUT endpoints are
  now owner-or-admin, returning 404 for other callers so session
  existence isn't leaked.
- Connections' Active Sessions strip is now always owner-scoped —
  admins still manage everyone via the Sessions page.
2026-04-18 21:56:20 +10:00
Dave Kempe 5a5bd8ff75 Document Vault KV v2 metadata policy requirement for deletes (#54)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 20:05:35 +11:00
Dave Kempe ac77bfab98 v0.6.1: Vault mTLS support, comprehensive test suite (87 tests)
Vault/OpenBao mTLS:
- Add ca_cert, client_cert, client_key fields to [vault] config
- Supports custom CA certificates and mutual TLS authentication
- Clear error messages for missing files, invalid PEM, missing key
- Fixes #48 (OpenBao requiring client certificates)

Test suite (8 → 87 tests):
- vault: 13 tests (TLS client builder, config deserialization, name validation)
- auth: 11 tests (role hierarchy, effective role capping, XFF/trusted proxy, has_role)
- session: 12 tests (CIDR network checks, autofill JSON parsing, placeholder substitution)
- browser: 8 tests (Chromium password encryption, Login Data SQLite, RangeAllocator)
- config: 8 tests (preset resolution, theme overrides, defaults, vault config)
- api: 6 tests (HTML escaping, recording name path traversal protection)
- db: 7 tests (SHA-256 hashing, key generation, user groups parsing)
- import: 12 tests (already existed, now wired into module tree)
- protocol: 8 tests (already existed)

Other:
- Wire import.rs into module tree (fixes orphaned tests)
- Document mTLS config in configuration.md and integrations.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 11:44:46 +11:00
Dave Kempe b5d31b13ce Add dedicated web sessions doc, fix credential docs, global policy warning
- New docs/web-sessions.md: comprehensive guide with autofill, domain
  allowlisting, login scripts (Playwright + shell examples), clipboard
  control, API reference, and troubleshooting
- Fix integrations.md: web sessions DO use credentials (for autofill
  and login scripts)
- Add global Chromium policy warning to security.md and web-sessions.md
- Add web-sessions.md to in-app docs (build.rs)
- Condense overview.md web section to link to new dedicated doc

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 07:30:09 +11:00
Dave Kempe 99d79fe05d v0.6.0: Web autofill, domain allowlisting, clipboard control, Guacamole import
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>
2026-03-07 00:54:26 +11:00
Dave Kempe 76c93bc502 Add multi-hop SSH tunnels, VNC sessions, and web session tunnel support
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>
2026-02-11 08:22:34 +11:00
Dave Kempe d07d79c4f2 Add Kerberos NLA support for RDP and credential prompting
- 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>
2026-02-10 10:39:18 +11:00
Dave Kempe 466ffd2d44 Upgrade openidconnect v3 -> v4, remove JumpCloud references
- openidconnect 3.5.0 -> 4.0.1 (oauth2 4 -> 5)
- Eliminates duplicate reqwest/hyper/http dependency chains
- 386 -> 355 crate dependencies
- Resolves rustls-pemfile 1.0.4 unmaintained warning
- HTTP client now uses stateful reqwest::Client (no-redirect policy)
- exchange_code returns Result for EndpointMaybeSet token URLs
- Remove JumpCloud from provider examples, prefer Authentik

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 10:39:24 +11:00
Dave Kempe 292db1fa55 Update OIDC docs to prefer Authentik, add setup guide
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 09:52:23 +11:00
Dave Kempe 67101e27ce Initial public release
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 14:38:53 +11:00