How Web Access works
Web Access puts web consoles — Grafana, Prometheus, a mail catcher, a BMC's web interface or API — behind the same login, groups, approvals, locks and audit as SSH. There is no agent on the console's host and no browser extension: the brokers are a reverse proxy in front of each application, and the browser talks to the application's own host name, which resolves to the brokers' shared address.
This page is the path a request takes. Configuring applications is how an application gets
into apps.yaml, Web Access policy is who gets in and what each request may do, and
preparing a web console is what the console itself needs.
The parts
- One TLS listener on every broker (
apps.yaml:listen, by default port 8444 in the shipped Ansible). Every application is reached on it; the host name (SNI and theHostheader) picks the application. An unknown host name gets 421. The listener's certificate (tls_cert_file) has to cover every application'shost; this is checked whenapps.yamlloads. - The web UI, which does the sign-in. Web Access has no login of its own: password and second factor, or SSO,
exactly as for the web UI itself (
webui_url). - The application's upstream (
upstream), reached by the broker, never by the browser. The firewall on the console's host lets the brokers in; with Web Access the direct route from the admins' network can be closed. - OpenBao, read by the broker for an application whose identity is a shared account (
identity: credential). - The record store, where every application session is a session record with a transcript, like an SSH
session (protocol
http).
Opening an application
- The user opens the application's address (or clicks it on the web UI's My applications page,
/apps, which lists what this user may open). The application's links open in a new tab. - No application session yet: the proxy gives the browser a short-lived state cookie and sends it to the web
UI's hand-off endpoint (
/apps/handoff). If the user is not signed in to the web UI, the web UI asks for the login first and then continues. - The gates, on the web UI: the user must be in one of the application's
allow_groups, must not be on a reserved name, must not be locked (neither the user nor the application), and — if the application asks for one — must hold an open approval window (four-eyes, or self-service on a stated reason). A refusal is anAPP_ACCESS_DENIEDaudit line with its reason; the user sees a general sentence. - The hand-off: the web UI issues a one-time code, valid for 60 seconds, and sends the browser back to the
application's
/_isg/callback. The proxy redeems the code only in the browser that holds the matching state cookie, checks the approval, the lock and the licence once more, and opens the application session: theisg_appcookie (HttpOnly,Secure,SameSite=Lax) on the application's host. The audit line isAPP_SESSION_STARTED. - From then on every request carries
isg_app, and the proxy forwards it to the upstream.
Each request
For every request of an open application session the broker, in this order:
- checks that the session is alive (idle limit
idle_timeout_seconds, 1800 s by default; absolute limitmax_duration_seconds, 28800 s by default; the web UI sign-in it came from must still be alive) and that no lock has been placed since; - judges the request against the
http_rulesofpolicy.yaml(method, path, role; adeny, astep_upfor a fresh second factor, or the application's allow-list mode) — the Web Access policy page; - removes ISG's own cookies and any identity header the browser sent, then adds the application's identity:
nothing (
identity: none), a fresh signed assertion (identity: jwt), or a shared account's secret from OpenBao (identity: credential: anAuthorization: Basicheader, a named header, or — for a console with a sign-in form — the upstream session cookies of an account ISG signed in with itself); - writes one line to the session's transcript: method, path, status, size, time, the brokered account; with
record_bodyalso the masked body of a modifying request.
If the request cannot be recorded, the policy cannot be loaded, the directory cannot be asked or the secret cannot be read, the request is not let through (503) — Web Access fails closed.
How a session ends
Signing out of the web UI, losing the group, a lock, an administrator closing the session, the end of an approval
window, or the idle or absolute limit closes the application session — on every broker, within about 5 seconds for
the serving broker. A request to one of the application's logout_paths signs the user out of ISG for that
application. The audit line is APP_SESSION_CLOSED with its reason.
What the browser never sees
- The shared account's password or key, for an
identity: credentialapplication: it is read from OpenBao by the broker and added to the request on the way up. - The upstream's session cookies, for a
kind: formaccount: they stay in the broker's memory, per application session and account. Only the cookies a recipe names inbrowser_cookiespass in both directions. - The assertion's signing key, for an
identity: jwtapplication: the application checks the assertion with the public key the listener serves at/.well-known/isg/jwks.json.
What Web Access is not
- It does not record the screen of an application in the default mode: the transcript is request-level. An
application in
mode: isolatedruns in a remote browser and is recorded as a picture; see isolated browser. - It does not judge an operation that is encoded in a request body, or the frames of a WebSocket: a rule sees the request that opens it.
- It does not give a console a second login of its own: the user signs in to ISG, and the console sees the identity ISG puts into the request.