Preparing a web console

What a web console needs before ISG can broker it — the Web Access counterpart of target host preparation. The console itself is not changed in most cases: what has to be decided is how it learns who the user is, and where its shared account's secret lives.

The network

Choosing how the console learns who the user is

The console… identity What to prepare
has no users of its own (Prometheus, a mail catcher) none nothing
accepts a signed token in a header (Grafana's [auth.jwt]) jwt the console trusts the listener's public key; role_map for its roles
takes HTTP Basic, or an API key in a header (a BMC's Redfish API) credential, kind: basic or header a shared account, its secret in OpenBao
signs in with a form and a session cookie (most web GUIs) credential, kind: form a shared account, its secret in OpenBao, and the sign-in recipe

A console that accepts a signed token

Grafana as the example, in its configuration file (or the matching environment variables):

[auth.jwt]
enabled = true
header_name = X-ISG-Assertion
jwk_set_url = https://grafana.apps.example.internal:8444/.well-known/isg/jwks.json
expect_claims = {"aud": "grafana", "iss": "isg"}
username_claim = sub
email_claim = email
role_attribute_path = role

The shipped Ansible sets this for the bundled Grafana, with jwk_set_file instead of the URL (the JWKS file from the config source), so Grafana depends on neither the brokers nor name resolution. Keep reserved_usernames as it is: the token's sub lands on the console's local account of the same name.

A shared account

The account is created on the console, by the console's own administrator, with the rights the users of that credentials entry should have — a read-only account for a read-only group, an administrator account for the administrators. Its secret goes to OpenBao, written by the operator; ISG only reads it:

bao kv put secret/isg/apps/<app name>/<account name> username=<account> password=<password>
bao kv put secret/isg/apps/<app name>/<account name> value=<api key>

The path is <kv_mount>/data/<prefix>/<app>/<name>; the brokers' OpenBao token needs read on that prefix (the isg_bao_init role's broker policy has it). isg system check on a broker says whether every account is readable: the Web Access credentials row. For a form sign-in with a second factor, add totp_secret (the base32 seed) and use {{totp}} in the recipe.

A form sign-in recipe

The recipe repeats what the console's own login page does. To write one:

  1. Open the console's login page directly, with the browser's developer tools on the Network tab, and sign in.
  2. Find the request that carries the user name and password: its method, path, and whether the body is a form (encoding: form) or JSON (encoding: json). If the page first loads a token (a CSRF field in the HTML, a header, a cookie), add a GET step before it with an extract rule (from: body, header:<Name> or cookie:<name>, and an RE2 regex with one capturing group) and use it as {{token.<name>}}.
  3. Success: what the answer to the sign-in looks like — its status, the session cookie it sets, or where it redirects. Name at least one.
  4. Signed out: what an answer looks like once the console's session has ended — usually a 401 from its API, or a redirect to the login page. If the console also answers 401 for something that is not a sign-out (a token rotation its own script handles), name a string of that answer's body in unless_body_contains.
  5. Cookies the page's script has to read itself (a CSRF cookie mirrored into a header) go to browser_cookies; every other cookie stays with the broker.

Behaviours seen on real consoles