DEMO/Infrastructure

← Back to dashboard

The Certificate Lab

Audience: operator · Profile: demo · Read this when: you need a private certificate authority to onboard certificate identities against, and want it gone again when the demo ends.

Preview. Neither cloud build path has been run against a live account — the GCP CAS and AWS Private CA modules, the enrollment identities they mint, and the Entra publishers are all unexercised. Off by default; enable it under Settings → Preview features → Certificate Lab.

This page builds the lab for the Password Safe Certificate custom platform plugin family, and tears it down again. Two things happen here and nothing else does: a private certificate authority is provisioned and destroyed, and identities are onboarded onto it as Password Safe managed accounts. Issuance itself belongs in BeyondInsight, behind an approval — see Why nothing is issued from here.

Read Certificates first if you have not decided on a backend or a platform: which of the nine can sign what, and the address grammar everything here composes, are there rather than here.


A forgotten private CA is the expensive mistake

Standing the lab up by hand is the bottleneck, and its cloud half has a standing cost that nothing else reclaims.

A CA pool bills whether or not it ever issues a certificate. A GCP CAS pool on the DevOps tier is about $20/month plus roughly $0.30 per certificate; an AWS Private CA is about $400/month standing. One nobody remembers is invisible on every page this dashboard had before this one — which is why every CA built here is a first-class inventory row carrying an auto-delete timer.

The mTLS endpoint and the CI runner are ordinary VMs deployed through the normal cloud pages. They already have their own timers, ref-counted NAT and Password Safe onboarding; nothing here re-implements any of that.


Pages

The larger parts have a page each:

Page Read it when
Certificate lab prerequisites you are about to build a certificate lab and need each cloud's prerequisites in place first
Building a CA you are building the lab's private certificate authority, root or subordinate

Onboarding a certificate identity

Workload Lab → Certificates → Add identity. The dashboard composes the address from the CA row (so project=, location= and pool= can never drift from the pool that was actually built), creates the Secrets Safe folder tree, resolves the functional account and platform, and registers the managed system plus one managed account.

What lands in Password Safe:

Field Value Why
Platform Certificate inherited from the functional account
Port 0 the platform does not use one. The CLI packager defaults it to 5432, inherited from the PostgreSQL plugin it was written for — that is the documented mistake
Network Address the profile the whole configuration surface
Timeout 60 read by the plugin, in seconds. Key generation plus a CA round trip is slower than a password change
Enable for API Access on GET /ManagedAccounts returns only accounts with ApiEnabled, and a CI pipeline is the whole use case
Change Password Using Own Credentials off a certificate identity holds no CA credential and cannot enroll for itself; the plugin reports NotSupported. The dashboard refuses to set it

One managed account per identity — and with an Entra publisher, one per app registration. Graph's PATCH replaces the whole keyCredentials collection, so the publisher reads the existing entries and carries them forward; two rotations against the same registration can each read the collection and clobber the other's key. Password Safe serialises rotations per managed account, which is what makes that mapping safe. It is easy to break by accident when copying a platform instance.

Why nothing is issued from here

Registration does not fire a credential change, unlike the Kubernetes token path which rotates on register to prove the whole path at once.

Issuance is meant to be gated by an approval with a reason, and that record is the first thing the demonstration shows. Firing a rotation from the dashboard would produce a certificate nobody approved and quietly remove the point. So the first certificate comes from Change Password in BeyondInsight, and until it runs Test password correctly fails — no bundle exists yet. That failure is step 1 of the demonstration, not a fault.


Proving it works

Run these in order; each is cheap and fails fast.

# Step What it proves
1 Test functional account on the managed system The CA accepted the enrollment credential, and the action log echoes the resolved backend, lifetime, key shape, folder and secret title — the fastest way to see the address parsed the way it reads
2 Test password with no certificate yet Verification correctly fails: no bundle exists
3 Submit a request with a reason, approve it Issuance is gated by approval, with a record
4 Change Password The completion message names the bundle's secret title and thumbprint — the one place the two halves are visibly linked for a human
5 Test password Passes, and reports remaining validity. A real cryptographic check, not a string comparison
6 nginx-mtls-endpoint.yml, then ci-fetch-cert.yml — see What consumes these credentials A program retrieves both halves and the endpoint echoes CN=svc-deploy-pipeline. This is the step usually skipped, and the only one that proves anything
7 Change Password again, re-run the consumer Renewal is transparent. Note the new serial
8 Break the Secrets Safe folder permission, Change Password It fails — and step 6 still works on the previous certificate

A third consumer, which holds nothing

ci-fetch-cert.yml proves the mechanism, and it does so with a Password Safe client id and secret supplied to the run. The Agent Demo Cell is the consumer that has neither: it reaches Password Safe with a workload identity brokered by Workload Credentials, requests the passphrase as a recorded request, downloads the bundle over GET Secrets-Safe/Secrets/{id}/file/download, and presents the certificate to an mTLS endpoint.

That route rather than ps-cli for the reason password-safe.md records: every path ps-cli offers decodes the body to text, so a PEM bundle survives and a PKCS#12 is corrupted rather than refused. The lab writes a .pfx, so the agent keeps the bytes itself.

It is also where the boundary below gets demonstrated rather than described. Revoke the certificate, run the agent again, and watch it work — because nothing on that path checks.

Step 8 is the one to spend time on. The plugin writes the bundle to Secrets Safe and only then reports success, because Password Safe commits the new passphrase when the action reports success. Reporting first would leave an account holding a passphrase that opens nothing — a broken identity with no automatic recovery. Writing first means a failed write leaves the old passphrase, the old bundle, and an identity that keeps authenticating. Every credential-management demonstration shows the happy path; showing that a mid-rotation failure leaves a working identity is what distinguishes this from a script.


The on-premises topology

For an existing AD CS enterprise CA, the dashboard's job is narrow — the CA already exists, so it creates the two things the plugin needs on it. Run windows/adcs-pipeline-template.yml over WinRM against the issuing CA. It creates the template and the enrollment account, and prints the ca= configuration string in the exact form the address wants.

Four template settings decide whether the plugin works at all, and the playbook sets all four:

The enrollment account's password is deliberately not set by the playbook: it belongs in the functional account's protected field, and a password passed as an extra var lands in job metadata and in Live Output.

Two ADCS-specific things to test, because they fail at a customer and nowhere else:


Where things live

The path table for the whole family is on Certificates. The pieces this page drives are web_dashboard/services/cert_lab_service.py (the CA lifecycle), terraform/cert_ca/gcp_cas/ and terraform/cert_ca/aws_pca/, and examples/playbooks/certificates/.