DEMO Community

Settings
Demo

Workload Lab

Preview

The labs for identities that belong to machines. There is no human in any of these workflows by design — the consumer is a pipeline, a broker or a cluster, not a person. A certificate, a SPIFFE identity, a Kubernetes token and a short-lived cloud credential are four answers to the same question, which is why they share a page — and which one an operator can actually use depends on what the consumer is and who runs the thing it is reaching. The Agent tab is the fifth, and it is not a fifth answer: it is a consumer holding one, so the others stop being demonstrations of a mechanism and become a principal you can watch be stopped. Which one you want.

Password Safe as a certificate registrar and broker — it never becomes a certificate authority. The managed account holds the PKCS#12 passphrase, Secrets Safe holds the bundle it opens, and both halves are governed. Build a private CA here, onboard an identity onto it, then rotate from BeyondInsight. The plugin ships as two platforms: Certificate for an end-entity certificate, and Subordinate CA for an issuing authority another system mints beneath — a delegation of issuing authority rather than one more credential, which is why its access control is separate. Feature guide.

Finish the Password Safe setup before onboarding an identity

Settings → Certificate Lab

Loading…
No certificate authority yet. A GCP CAS pool on the DevOps tier is about $20/month plus roughly $0.30 per certificate, and it bills whether or not it issues anything — so every CA built here carries an auto-delete timer.
Name Pool Location Signs Status Identity Expires

Build a certificate authority

A CAS pool, a self-signed root CA, and the enrollment service account the plugin authenticates as. Destroying it removes all three in one pass — the module sets the three flags CAS otherwise uses to refuse deletion.

A private CA, its self-signed root certificate, and the IAM user the plugin authenticates as. ~$400/month standing, twenty times the GCP pool, billed whether or not it issues anything — so it is refused outright unless auto-delete would stamp a timer on it.

The project id, not its display name or number. Defaults to the project this dashboard's GCP credential is scoped to; the pool lands wherever this says, so a different project needs the CA Service roles granted there too.

The pool, its CAs and any certificate template are all location-scoped and must agree.

A free-text field rather than a list: a Private CA is regional, and it is available in far more regions than CAS is.

Fixed when the root is created and not widenable afterwards. A root built to issue leaves has a path length of 0 and will refuse to sign a subordinate CA — and it is the certificate authority that refuses, with a policy error naming neither this choice nor the flag behind it. Pick can also sign a subordinate CA to demonstrate the Subordinate CA platform; it still issues end-entity certificates exactly as before.

One managed account per identity. The account name becomes the subject CN by default, and the whole profile below becomes the managed system's Network Address on the Certificate platform.

The managed credential here is an issuer, not a certificate. Password Safe obtains a subordinate CA from this root, something else — PRA Vault, typically — holds it and mints its own short-lived leaves beneath it, and Password Safe rotates the subordinate on a schedule. It lands on the Subordinate CA platform, whose access control is separate for that reason. Install the root chain into every target's trust store once, before anything else: that is what makes rotation safe, and nothing here automates it.

Rotation does not revoke. A target validates a leaf by walking its chain to the root and neither knows nor cares which subordinate was current when the leaf was minted — so this, not the rotation interval, is the exposure window if the signing key leaks. Issue it just longer than the interval Password Safe rotates it on: 8d against a 7-day schedule leaves two concurrently valid authorities briefly, where 1y would leave ~52.

A pipeline identity is not a host, so usually leave this empty — and note that under eku=Auto a DNS SAN drags serverAuth into the EKU set.

A vaulted CA key is not a credential to one system — it is the authority to mint an identity for anything the CA may assert. Constraints turn a compromise from mint anything into mint within a bounded namespace, and the extension is marked critical, so a relying party that cannot interpret it refuses the certificate rather than ignoring the boundary. Leaving this empty is allowed and warned about. Prefer the parent CA pool's own issuance policy where you can: the subordinate then inherits a boundary it cannot widen, and it costs nothing against the 255-character address.

A subtree is a network: 10.0.0.0/8, never 10.1.2.3/8. Host bits set is refused here, because no later action can detect a subordinate constrained to the wrong thing.

How many further CAs may appear beneath the subordinate. 0 is almost always right, and is all this lab's roots permit.

The Subordinate CA package already defaults to PemBundle, which is what a consumer of a CA key needs — PRA Vault's X.509 Parent Certificate Authority account takes a PEM key, a passphrase and a PEM certificate as three separate fields, and its key box says "Only PEM encoding is valid". Leaving this alone is both correct and 17 characters of address budget back.

No isca= and no bundle=: the Subordinate CA package already defaults both, and restating them would spend 27 characters of the budget the permitted-DNS list is competing for. No eku= either — the plugin asserts none on a CA certificate, because an EKU there constrains everything issued beneath it and implementations disagree about how.

CA chain

Hand this to the mTLS endpoint playbook as ca_chain_pem. It is the ssl_client_certificate nginx verifies presented certificates against.

A SPIRE trust domain on a VM this dashboard already deployed, so the Password Safe SPIFFE SVID plugin has workload identities to govern. The lab is four Ansible playbooks against an ordinary Linux host — there is no Terraform module, because a SPIRE server is a Go binary and a sqlite file. Feature guide · Standup runbook.

Check these before building

Settings → SPIRE Lab · Storage (upload playbooks)

Loading…
No trust domain yet. Deploy a small Ubuntu VM from a cloud page first — on a subnet the Resource Broker can reach — then build the lab onto it. Tearing the lab down closes tcp/8081 and leaves the VM alone.
Trust domain Host Status Stages Reachable from SVID expires

Build a trust domain

A bare DNS-style name, not a spiffe:// URI. It is baked into the server config, every SPIFFE ID and the Managed System, and the plugin asserts it on every connect — so it cannot be changed afterwards.

One mode per host. Every mode publishes an OIDC Discovery Provider on tcp/8443 and registers the trust domain with this dashboard by JWKS URL. Not staged: .

No VM available. Deploy one from its cloud page first — the lab attaches to an existing host so it inherits that VM's SSH key, its auto-delete timer and its Destroy. Ubuntu 22.04 on the smallest shape that runs a Go binary is plenty. It must be on a subnet the Resource Broker can reach.

How the runs log in (optional)

Looking up managed accounts for this host…

Pick a host first. No managed accounts for this host — Password Safe may be disabled, or the VM is not onboarded there.

An account and a key are two answers to one question, so picking one disables the other. A managed account also supplies its own login user. An SSH-key secret works on every runner; a managed account works on the local and Azure (ACI) runners, and on ECS / Cloud Run via a short-lived RBAC-locked store copy. on ECS / Cloud Run only if ‘Ephemeral cloud secrets’ is enabled in Settings.

The functional account's name is this SPIFFE ID, not a username. It goes into the server's admin_ids — which is the line people get wrong: an entry-level -admin grants nothing to a minted SVID, and a missing admin_ids surfaces much later as PERMISSION_DENIED on Verify Functional Account.

What this will do, in order:

Connecting as (Password Safe, checked out just‑in‑time, sudo included) , using the SSH-key secret you picked , using the keypair this VM was deployed with

  1. Open tcp/ and tcp/8443 on the cloud ACL to — nothing, because no source CIDRs are configured
  2. Install SPIRE under systemd
  3. Install Docker, then run SPIRE from the official images under Compose
  4. Install k3s, then the SPIRE Helm charts
  5. Open the host firewall on the same sources
  6. Seed registration entries ( should be discovered)
  7. Mint the administrative credential into Secrets Safe
  8. Publish the OIDC Discovery Provider and register the trust domain's JWKS URL here

Kubernetes access

Never live-validated

Attest a second VM — a small k3s node — into , and make its API server accept this trust domain's JWT-SVIDs. The workload then reaches the cluster with a token that lives s and is written to no disk and no vault. What this proves.

Self-managed clusters only. This sets --authentication-config on the API server, which EKS, AKS and GKE do not let you pass — which is why the lab brings its own k3s node. For a managed cluster, the brokers a bound ServiceAccount token through Password Safe instead — audited and rotated, though the vault authenticates whoever can retrieve rather than the workload itself. That is the trade, and it is the one axis this page wins on.

Onboard into BeyondInsight

Govern creates the managed system for you. These are the values it uses, and the two below it cannot; §5 of the runbook is the order.

Trust bundle

Set SpiffeTrustBundlePem on the managed system to this. A SPIRE server presents only its leaf certificate, so there is nothing in the handshake to chain against and fingerprint pinning cannot substitute — without it every connect fails SPIRE Server certificate could not be validated. Public by construction: it is what every consumer of the trust domain has to trust.

A bound ServiceAccount token for a machine outside the cluster — a CI build, a fleet scan — minted by the API server, rotated and audited by Password Safe, retrieved per run. This is the path that works on a managed cluster: the SPIFFE route needs --authentication-config, which EKS, AKS and GKE do not let you pass. Feature guide. For a self-managed cluster with no credential at rest at all, use .

Not configured yet

Loading…

No workload identity is onboarded.

Onboard a Deployer to show a CI build deploying into one namespace and being refused in another, or a Reader to show a cluster-wide scan that cannot read Secrets.

Onboard a workload identity

The profile is the decision here, not the token. It selects the RBAC, and the RBAC is what makes a vaulted token worth demonstrating — neither profile is cluster-admin.

Not selectable (no stored kubeconfig, so nothing here can reach the API server):

Created if it does not exist. For deployer this is the only namespace the token may write in.

needs the cloud's own name for this cluster. The dashboard only stores the label you registered it under, and the Password Safe plugin looks it up by its real name — a mismatch fails at the first rotation, inside Password Safe.

A zonal cluster's location is its zone, a regional one's is its region. Mixing them is the documented cause of a GKE 404.

bound mints through the TokenRequest API and creates no Secret at all — strictly less privilege for the rotator.

600 is the TokenRequest API's own floor; anything lower is clamped.

A forgotten workload token keeps authenticating, and this row is the only page it appears on.

What the consumer needs

No credential is shown here, and none ever will be. The consumer retrieves with its own Password Safe client id — that is what records which build read the token. A value proxied through this page would record only that the dashboard did.

A short-lived AWS or Azure credential for a workload, minted on demand by Workload Credentials against a dynamic secret and handed out as a lease. This is the tab that replaces the access key in a CI secret store — the one with no expiry, no revocation and no record of who read it. Feature guide.

Not configured yet

Before you choose a cloud

Loading…

No workload identity is registered.

Register one against a Workload Credentials dynamic secret, then press Issue to mint. Registering alone costs nothing — issuance is metered.

Register a workload identity

Records that this workload draws from a dynamic secret. Mints nothing — issuance is metered, so the first credential appears when you press Issue.

Not available (no Workload Credentials configuration):

This decides the scope — which role is assumed and what it may do. Defined in Workload Credentials; this page cannot change it, which is why there is no default.

The dashboard uses for its own cloud calls. A lab identity is usually a different secret with a narrower scope.

A request, not a guarantee — AWS caps a role-chained credential at one hour and the provider may clamp.

Retiring the identity stops it minting another credential. An outstanding one lives out its TTL regardless.

The current lease

Read live from Workload Credentials, not from this dashboard's record — the two diverge exactly when it matters, if the lease was released elsewhere. No credential is shown here and none ever will be: the values went to the consumer that asked for them, which is what puts that consumer in the audit log rather than this page.

The consumer. A worker on a VM this dashboard already deployed, which attests itself to the 's trust domain and reads your estate through this dashboard's MCP server — authorized by a token that expires and can be revoked while somebody watches. Make it answerable for a lease, a token or a identity and the other tabs stop being four separate demos. Feature guide.

Not ready yet

Minting, linking and revoking need config_mgmt:write — what the cell ultimately does is run playbooks against a host. You can see the agents below.

Identity, and authorization

Its identity is a SPIFFE SVID it re-attests on every loop, holding nothing at rest. Its authorization is bounded by the RBAC of the user you mint it against. Mint it against a service account and the SVID is the authorization's root: the worker exchanges a fresh JWT-SVID at /api/oauth/token for an access token that lives minutes, and holds no secret at all. Mint it against a person-shaped user and it gets a Personal Access Token, and the two stay separate.

Either way the SVID does not authenticate to /mcp directly — it is exchanged for a bearer token, and that is what /mcp sees. The other bridge, the SVID minting a credential inside Password Safe through the SPIFFE SVID plugin, is still unresolved.

The demo is the same in both: revoke the agent and the worker is still who it was, and may no longer do anything.

Loading…

No agent has been minted.

Minting issues a bounded token and records what was issued. It deploys nothing — the worker attaches to a VM you already have, and the two playbooks that put it there are runs you make.

Mint an agent

Issues a bounded token and records what was issued. Deploys nothing — the worker attaches to a VM you already have, and the install is two playbooks you run yourself.

The worker will attest as , one ID per cell. The dashboard creates the entry itself and hands you a one-use join token; the worker's host runs its own SPIRE agent, so there is no lab and no second VM. The host must reach on tcp/8081.

The worker will attest as . That path is fixed, so the registration entry the playbook creates and the ID the worker expects cannot drift.

None yet. Start the dashboard's own SPIRE server (docker-compose.spire.yml, or the examples/cloud-spire sidecar on a managed runtime), or stand a lab up on first — without a trust domain the worker logs unattested and the identity half of the demo becomes an assertion.

1 to . It always expires — a non-human principal whose authorization never ends is what this cell argues against.

Re-derived against this dashboard's own deploy records, never trusted as given. Pick the VM that is already a SPIRE agent node — the lab's host is prefilled above when you choose one.

Administrators are not listed, and the cell refuses one. Every MCP tool applies this user's own permissions, so the token user is the agent's blast radius — picking an admin would quietly make the demo say the opposite of what it means to.

Everyone in this workstream can see and manage the agent — including revoking its token. Leave it blank and only you and administrators can.

is recorded

The row keeps the SPIFFE ID, the token's name and its expiry. It never keeps the token. This is the only time it is shown — carry it into the install playbook now, because nothing can recover it afterwards.

SPIFFE ID
Token name
Expires
Token None — this agent holds no secret. It authenticates with its JWT-SVID as OAuth client .
Token
SPIRE join token

One use, fifteen minutes, for the SPIRE agent on the worker's host. Mint the cell again if it lapses.

Trust bundle

          
        

Save it as bootstrap.crt where you run the playbook.

Put the worker on the host

Two playbooks, in this order, from examples/playbooks/agent/. The first creates the SPIRE registration entry on the SPIRE server so the worker can attest; the second installs the unit on the worker's host. The dashboard does not run these and does not watch them — which is why there is no progress bar here and why the journal is the record.

1 — a SPIRE agent on the worker's host

The dashboard already created the registration entry. This attests the host with the join token from the mint, and proves a JWT-SVID can be fetched as the worker.


        

1 — the registration entry (on the SPIRE server)

The worker's host must run a SPIRE agent. In this lab the host that does is the SPIRE row's Kubernetes-linked k3s node — link the lab first, and put the worker there.

This agent's host is not the lab's k3s node (), the one host that runs a SPIRE agent — the worker there would log unattested.


        

2 — the worker


          

agent_token is the token from the mint response. It was shown once; if it is gone, revoke this agent and mint another rather than reusing a token whose owner you are no longer sure of.

No token: the worker exchanges a fresh JWT-SVID from the host's SPIRE agent at /api/oauth/token for each short-lived access token.

3 — watch it


          

Every line names both halves — the SPIFFE ID it proved and the token it spent — so the gap between them stays visible while the room watches.

What is it answerable for?

One credential at a time, deliberately. An agent answerable for a cloud lease and a cluster token and a certificate would be the most over-credentialed principal in the estate — which is the shape this cell argues against, so widening is a decision rather than an accumulation.

That tab has no rows to link. Create one there first.

The subscription the credential must be able to read — what --cloud-scope takes. Azure only: on AWS the allowed call is sts:GetCallerIdentity, which needs no scope and cannot be denied.

The refusal is what proves anything. That the credential works shows only that it exists; that it is refused something is the scope. This dashboard cannot see the dynamic secret's role, so this is your assertion about it — and none makes the episode say out loud that the run proves authentication and not scope.

Neither is stored here. The row records the mechanism and the identity's id; these two shape the command the link hands back.

The identity you onboarded onto this CA. Its credential is the PKCS#12 passphrase — one half, and useless without the other.

The bundle's Secrets Safe reference — the other half, and what --cert-bundle-title takes. One containing / is looked up as a folder path, one without it as a bare title. It is a name, not a credential.

What --cert-cn asserts. An endpoint that answers 200 without having seen the certificate proves nothing, so the episode checks that the subject comes back.

Neither half is stored here. The row records the mechanism and the CA; these three compose the notes you carry to mcp_agent.py --cert-episode. And this link is the one whose removal changes nothing — nothing on that path checks revocation.

Request cluster access

Opens one bounded request against the linked token. It hands nothing over — Password Safe decides whether to release, and where the access policy requires approval it holds the request for a person. That is the beat: an agent that cannot authorise its own access.

Clamped to 5–, because this is how long the account's concurrent slot stays held if nobody approves. An abandoned request makes the next attempt fail on the cap, reporting the cap rather than the approval it was waiting on.