Skip to main content
RunBook Academy

Secrets, PKI & CertificatesCapstone · Production CapstoneCapstone

Capstone — build and defend a production secrets and PKI architecture

Expert⏱ ~240 minDocker with a working daemonopensslssh-keygencertbotbao

What you'll learn

  • Design a two-tier trust hierarchy and prove each constraint is set correctly
  • Automate issuance and renewal and prove the running process serves the new certificate
  • Operate a secret manager under least privilege and prove both an allow and a deny
  • Rehearse rotation, compromise response and restore, and record the evidence for each

Prerequisites

None — start here.

Verified against OpenSSL 3.5.x teaching target; 3.0+ minimum · OpenSSH 10.x teaching target; 8.2+ minimum for certificate workflows · OpenBao 2.6.x · Smallstep step-ca 0.30.x · Certbot / Pebble Certbot current release; Pebble 2.10.x ACME test server · Kubernetes (cross-course target) 1.36.x · PostgreSQL 17.x · 2026-08-26

Not yet marked complete on this device.

Every control in this course has so far been examined on its own. The capstone puts them in one estate and asks whether they hold together. You will build a trust hierarchy, an automated issuance path, a secret manager and the credentials it hands out, all on containers you can destroy, and then you will attack your own build and prove that each control does what you claim it does.

What you are building, and the diagram you owe

The estate is deliberately small: one root, one issuing authority, one ACME server, one TLS service, one SSH certificate authority, one secret manager, one database, one workload and one monitor. Nothing here needs a cloud account or a spare machine. Every component runs as a container on the same host, using the same fixtures the labs use, so that stage thirteen can destroy half of it without consequence.

Before you build anything, redraw the diagram below as your own. Use your own host names, your own validity windows and your own arrows, and add the two flows this version deliberately leaves incomplete: how a failure reaches a human, and how each authority is itself replaced. Your diagram is a graded deliverable, and it is the artefact a reviewer will read first.

flowchart TD
    ROOT["Offline root CA\npowered off between ceremonies"]
    ISS["Issuing CA\nonline, constrained by the root"]
    ACME["Internal ACME server"]
    TRUST["Trust anchor distribution\nOS store and runtime store"]
    SVC["TLS service\nrenewed and reloaded"]
    SSHCA["SSH certificate authority\nbounded principals and validity"]
    ADMIN["Operator session\ncertificate, not a key file"]
    BAO["Secret manager\nsealed until quorum"]
    WL["Workload\nno credential at rest"]
    DB["Database\nshort-lived dynamic roles"]
    MON["Probes\nexpiry and availability"]
    ROOT --> ISS
    ROOT --> TRUST
    ISS --> ACME
    ACME --> SVC
    TRUST --> SVC
    SSHCA --> ADMIN
    WL --> BAO
    BAO --> DB
    SVC --> MON
    BAO --> MON

Read the diagram as two independent trust systems that meet only at the host. On the left, an offline root vouches for an online issuing authority, which sits behind an enrolment service, and a separate distribution path carries the anchor to every client that must recognise it. On the right, a secret manager holds a barrier that stays closed until a quorum opens it, and hands out credentials that expire on their own. The SSH authority is a third, smaller system with no intermediates at all. A weakness in any one of the three is contained by the other two only if you can say, out loud, where the boundary between them runs.

Stages 1 to 3 — the hierarchy and the certificates it issues

The first three stages produce a chain that validates, a leaf whose identity is unambiguous, and an automated renewal that actually reaches the running process. Nothing later in the capstone works if these are wrong, and the third one is where most real estates fail.

StageWhat you buildAcceptance criterion you can verify yourself
1A root authority and a separate issuing authority, each with its own keyThe root carries basicConstraints=critical,CA:TRUE,pathlen:1 and keyUsage=critical,keyCertSign,cRLSign; the issuing authority carries CA:TRUE,pathlen:0; openssl verify -CAfile root.crt srv-ca.crt returns srv-ca.crt: OK; the root private key is not present on the host that runs the issuing authority, and you can demonstrate that by listing the filesystem it does not appear on
2A server certificate with a subject alternative name and a server-authentication extended key usageVerification succeeds for a name in the SAN and fails with error 62 for a name that is not; verification succeeds for sslserver and fails with error 26 for sslclient; a certificate whose common name disagrees with its SAN is still accepted or rejected purely on the SAN
3Automated issuance and renewal against an ACME server, with a reloadThe serial the process serves over the network changes after renewal; and, in a separate deliberate run, renewing without reloading leaves the old serial on the wire while the new one sits on disk

Stage 1 repeats secrets-pki-lab-04-build-two-tier-certificate-authority and stage 2 repeats secrets-pki-lab-05-issue-server-certificate-san-eku; revisit either if a constraint does not come out as you expected. The path length on the root is not decoration. A pathLenConstraint may only appear where the certificate is a certificate authority and asserts keyCertSign, and setting it to zero on the issuing authority is what stops that authority minting a third tier you never approved.

PKI="$HOME/rbpki-capstone/pki"
cd "$PKI" || exit 1

# Identity is whatever the SAN says, and nothing else.
openssl verify -CAfile root.crt -untrusted srv-ca.crt \
  -verify_hostname app.lab.example app.crt
openssl verify -CAfile root.crt -untrusted srv-ca.crt \
  -verify_hostname wrong.lab.example app.crt
openssl verify -CAfile root.crt -untrusted srv-ca.crt \
  -purpose sslclient app.crt

Those three calls are your stage 2 evidence, and they must produce three different results:

app.crt: OK
error 62 at 0 depth lookup: hostname mismatch
error 26 at 0 depth lookup: unsuitable certificate purpose

Stage 3 is the one to take seriously. Renewal writes a file; serving reads memory. Prove the gap exists before you prove you have closed it, by renewing once with the reload hook removed and capturing both serials.

HOST=app.lab.example
PORT=8443
LIVE=/etc/ssl/certs/app.lab.example.pem

# What the process is serving, read over the network.
openssl s_client -connect "$HOST:$PORT" -servername "$HOST" </dev/null 2>/dev/null |
  openssl x509 -noout -serial

# What the renewal wrote to disk.
openssl x509 -in "$LIVE" -noout -serial

Two identical serials after a renewal and a reload is a pass. Two different serials, with the older one on the wire, is the failure this stage exists to make visible. Labs secrets-pki-lab-13-automate-issuance-with-acme, secrets-pki-lab-14-prove-renewal-and-reload and secrets-pki-lab-15-run-internal-acme-ca cover the mechanics.

Stages 4 to 6 — distribution, SSH identity and the barrier

StageWhat you buildAcceptance criterion you can verify yourself
4The private anchor installed into an operating system trust store and into at least one language runtimeA container without the anchor fails the connection with curl: (60) SSL certificate problem: unable to get local issuer certificate; after installing the anchor and refreshing the store, the same call returns the page body; a runtime that ships its own bundle, such as the Python certifi bundle, still fails until it is told about the anchor separately, and you capture both failures
5An SSH certificate authority issuing host and user certificatesssh-keygen -L -f on the user certificate shows a non-empty principals list and a validity window measured in hours rather than years; a login attempt as an unlisted principal is refused, with the server log recording Certificate invalid: name is not a listed principal; an expired certificate is refused with Certificate invalid: expired
6A secret manager running as a real server, initialised and unsealedThe server starts uninitialised and sealed; initialisation produces three shares with a threshold of two; the first share reports Unseal Progress 1/2 and the second reports Sealed false; the audit device is declared in the configuration file, because this release refuses to enable one through the API; the shares are recorded as held by different people and the bootstrap root token is revoked once the policies exist

Stage 4 is the stage engineers skip because it works on their laptop. It works on their laptop because their laptop has the anchor. The acceptance criterion demands two separate failures before the two successes, because the operating system store and a language runtime bundle are different stores with different refresh mechanisms, and a fleet that has one but not the other produces the report that a service works from the shell and fails from the application on the same host. Revisit secrets-pki-lab-11-install-private-trust-anchor.

Stage 5 has two traps worth naming before you meet them. Signing a key without a validity flag produces a certificate valid from the Unix epoch to the distant future, so pair the signing flag with an explicit validity interval every single time. And from OpenSSH 10.3, an empty valid-principals list matches nothing rather than matching everything, which reverses the advice in a great deal of published material. Labs secrets-pki-lab-17-build-ssh-certificate-authority and secrets-pki-lab-18-issue-and-revoke-ssh-certificates cover issuance and the key revocation list.

Stage 6 is a ceremony, not a command, and the wording the tool gives you states the obligation plainly:

Vault initialized with 3 key shares and a key threshold of 2. Please securely
distribute the key shares printed above. When the Vault is re-sealed,
restarted, or stopped, you must supply at least 2 of these keys to unseal it
before it can start servicing requests.

That text still says Vault, because OpenBao inherited the strings; quote it as it is rather than tidying it. Your deliverable is not the transcript but the written answer to the question it raises: who holds share one, who holds share two, who holds share three, and which two of those people can be in the same room within your recovery objective. Rehearse it in secrets-pki-lab-19-deploy-secret-manager-and-unseal.

Stages 7 to 9 — policy, dynamic credentials and workload identity

StageWhat you buildAcceptance criterion you can verify yourself
7A least-privilege policy and a token bound to itReading the one permitted path succeeds; reading a neighbouring path returns Code: 403 with permission denied; writing to the permitted path returns 403; listing the parent returns 403, and you can explain that last one by naming the two different paths a versioned key-value store uses for data and for metadata
8A dynamic database credential with a short leaseThe lease has a stated expiry time; the generated database user authenticates successfully; after an explicit lease revocation the same credential is refused by the database itself, not merely by the secret manager; a count of leftover dynamic roles in the database returns zero
9A workload that authenticates for its credentials rather than reading themNo file on the workload filesystem contains a credential that still works after the workload exits; the authentication response carries a bounded lease duration; copying the workload’s on-disk material into a second container does not obtain a usable token after the bounded window has passed

Stage 7 must produce a deny as carefully as it produces an allow. A policy that has only ever been tested against the path it permits has not been tested. The list denial is the instructive one: a policy granting read on the data path of a versioned key-value store does not grant list, because listing reads the metadata path, and the refusal names a path the policy author never wrote down. Work it through in secrets-pki-lab-20-write-least-privilege-secret-policy.

Stage 8 is where the phrase short-lived stops being a slogan. The credential must stop working at the database, and the way you demonstrate that is by connecting as the dynamic user after revocation and capturing the database’s own refusal. Anything observed only at the secret manager proves that the manager forgot the credential, not that the database did. Lab secrets-pki-lab-21-issue-dynamic-database-credentials walks the lease lifecycle.

Stage 9 will not eliminate the bootstrap problem, and your writeup should say so. An application role identifier plus a secret identifier is still a credential the workload must obtain from somewhere, so the honest acceptance criterion is not that no secret exists but that nothing at rest remains useful: the identifier is single-use or short-lived, the token it buys has a lease, and a copy taken from disk after the window is worthless. State plainly which component you have chosen to trust as the first link, because that component is now the whole chain. Lab secrets-pki-lab-22-authenticate-workload-with-approle is the starting point.

Stages 10 to 13 — operate it, rotate it, break it, restore it

StageWhat you buildAcceptance criterion you can verify yourself
10Monitoring for certificate expiry and for secret-manager availabilityA probe reports the served certificate entering a renewal window; a probe reports the secret manager as unavailable when it is sealed, which returns Code: 503 with Vault is sealed; and, critically, deleting the certificate the probe reads produces an alert rather than a silent success
11A credential rotation and a certificate rotation with no downtimeA request loop running throughout both rotations records zero failed requests; the old credential is retired only after the new one has served real traffic; you can state, from your own timings, the overlap window each rotation required
12A response to one injected compromise of your choosingA timeline with clock times; a containment action that names the component enforcing it; evidence from an independent channel that the old credential no longer works; and an explicit statement of what your response did not contain
13A backup and a restore that has actually been performedThe issuing authority is destroyed and rebuilt from backup, and the rebuilt authority issues a certificate that validates against the untouched root; the secret manager is restored and unsealed with the same shares; both restores are timed

For stage 10, a three-state probe is the smallest honest one:

CERT=/etc/ssl/certs/app.lab.example.pem
WINDOW=$((30 * 24 * 3600))

if openssl x509 -in "$CERT" -noout -checkend "$WINDOW"; then
  echo "ok"
elif [ -r "$CERT" ]; then
  echo "expiring"
else
  echo "probe broken"
fi

Two of those three states must reach a human, and the third state is the one every estate forgets. A monitor that cannot read its input is not reporting good news, and a check whose failures are swallowed reports good news for exactly as long as it takes for the certificate behind it to expire. Build the probe in secrets-pki-lab-24-monitor-certificate-expiry, then break it on purpose and confirm you hear about it.

Stage 11 wants overlap rather than replacement: add the new credential, move consumers, remove the old one, in that order, which is the same shape for a database password and for a certificate. Rehearse in secrets-pki-lab-23-rotate-database-password-zero-downtime. Stage 12 gives you three choices, and they are not equivalent: a leaked interface token is contained by revoking it at the issuer, a compromised server key requires reissue with a fresh key rather than reissue alone, and a compromised issuing authority requires you to decide what every certificate it ever signed is now worth. Whichever you pick, the deliverable that carries the marks is the sentence describing what you did not contain. Stage 13 closes the loop with secrets-pki-lab-26-restore-pki-from-backup and secrets-pki-lab-25-recover-from-secret-manager-outage; a backup whose restore has never been executed is an untested assumption with a filename.

How the build is assessed

Score each row on evidence, not on intent. A stage with a working build and no captured proof scores zero on that row, because the proof is the deliverable.

WeightAssessed element
14Trust hierarchy and service certificates, stages 1 and 2
10Renewal with a proven reload, stage 3
7Trust anchor distribution to two kinds of store, stage 4
7SSH identity with bounded principals and validity, stage 5
8Secret manager deployment and unseal ceremony, stage 6
9Least privilege proven by an allow and a deny, stage 7
8Dynamic credential revoked at the database, stage 8
7Workload identity with nothing usable at rest, stage 9
8Monitoring proven to fail loudly, stage 10
6Rotation with no failed requests, stage 11
6Compromise response, including what was not contained, stage 12
5Backup with an executed restore, stage 13
5Your architecture diagram and the six-question analysis

Seventy is a pass. Three rows are gating regardless of the total: stage 3, stage 7 and stage 13. A renewal you cannot prove reached the process, a policy that has never denied anything, and a backup that has never been restored are the three failures that turn a well-built estate into an outage on a date nobody has written down.

The six questions, answered three times

Close the capstone by returning to the analysis the course opened with, in secrets-pki-i-02-six-questions-that-govern-every-credential. Answer all six, in order, for three credentials from your own build: one certificate-based credential, one bearer credential and one dynamic credential. The questions are:

  1. Which principal is being trusted?
  2. Which credential proves that identity?
  3. Who issued it, and who checks the issuer?
  4. Where does the private material live?
  5. How long does it live?
  6. What revokes it, and what enforces that?

Two rules make this exercise worth doing rather than pleasant to write. First, questions four and six are never answered by the artefact itself, so write them from your build rather than from the credential, and enumerate the copies of the private material by hand until the count is exact. Second, question six is answered by naming a component and saying what that component does when the revocation record is unavailable. If no component consults it, write that down, and then justify the lifetime you chose in question five as the only containment control you actually have.

Production discipline

  1. Capture the evidence at the moment it exists. A serial, a log line or an exit status recorded during the stage is worth more than a reconstruction afterwards, and reconstruction is how honest people end up documenting a state that never was.
  2. Test the deny before you trust the allow. Every policy, principal list and trust store in this build has a negative case, and the negative case is the one that carries the security property.
  3. Write down what you did not contain. The scope of an incident response is a claim, and the unqualified version of that claim is the one that gets repeated in a review six months later by somebody who was not there.
  4. Time every restore. A recovery objective is a number somebody promised; your measured restore is the number that is true, and the gap between them belongs in the design document.
  5. Let lifetime absorb whatever enforcement cannot. Where you cannot name the component that reads a revocation record, shorten the credential and automate its reissuance instead of documenting an intention.

Cross-course references

  • Kubernetes for Production Sysadmins - Part LXXVI (Certificates) covers a cluster whose authorities and leaves renew by entirely different mechanisms, which is the same two-deadline problem stage 13 asks you to rehearse.
  • Observability for Production Sysadmins - Part LXIV (TLS Monitoring) covers probing from the consumer’s network position and alerting on an absent series, which is stage 10 implemented the way a real estate implements it.
  • Linux for Production Sysadmins - Part XXXI (Systemd) covers the unit reload that carries a renewed certificate into the running process, the mechanism stage 3 exists to prove.

Quiz

Knowledge check · 4 questions

  1. Q1. A student reports stage 3 complete: the renewal ran, the new certificate is in the expected directory, and the file modification time is minutes old. What is missing from that evidence?

  2. Q2. In stage 8, demonstrating that the secret manager no longer lists the lease is insufficient evidence that the dynamic credential has been revoked.

  3. Q3. Stage 4 requires two separate failures before its two successes. Name the two stores involved and explain what estate-level symptom the stage is teaching you to predict.

  4. Q4. Decide what the reviewer should do with this submission, and what the student must produce before it can pass.

    A capstone submission arrives on 2026-09-10. Stages 1, 2, 4, 5, 6, 8, 9, 11 and 12 are complete with captured evidence and would score 71 marks on their own. Stage 3 shows a renewal transcript and a directory listing but no handshake capture. Stage 7 shows a successful read against the permitted path and no denied operation. Stage 13 shows a backup script, a cron entry and a note that the restore was not attempted because the environment was needed for stage 12.

Passing score: 75%. Answers are checked in this browser.