Skip to main content
RunBook Academy

Secrets, PKI & CertificatesXVII · Inventory, Discovery and MonitoringInventory

Building a credential inventory that stays true

Intermediate⏱ ~22 minopenssl

What you'll learn

  • Justify each field of a credential record by the operational decision it feeds
  • Compute an identifier for key material that survives renewal, redeployment and file moves
  • Design a reconciliation loop that produces matched, unregistered and unobserved sets
  • Explain why a manually maintained register decays and what structurally prevents it

Prerequisites

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.

A register earns its place only if each field answers a question somebody asks while something is going wrong. Fields that nobody reads under pressure are not documentation, they are maintenance cost, and they are the first thing to go stale. So the way to design the record is backwards: start from the decisions, and keep only the fields those decisions need.

The fields, and the decision each one feeds

  • Identity. A value that names the same credential before and after a renewal, a redeploy or a file move. Without it there is no way to say that the thing observed today is the thing recorded last quarter, and every reconciliation run reports the whole estate as new.
  • Owner. A rota with an on-call channel and a named escalation, never an individual. This field feeds exactly one question, asked at three in the morning: who do I wake up. If it cannot be paged, the field is empty regardless of what it contains.
  • Location. Every place the material is stored, one entry each: host and path, secret manager path, key handle in a KMS, image reference. Not the canonical location, all of them, because the removal work at retirement is proportional to this list.
  • Issuer. Which authority signed or minted it. This determines the reissue path, the revocation path and the lead time, and the three differ enormously between a public CA reached over ACME, an internal CA and a vendor portal with a support ticket queue.
  • Consumer. Which processes read the credential. This is the field rotation depends on and the field hardest to keep true. Derive it from observation wherever possible: authentication audit records, established connections, deployment manifests. Where it is only declared, mark it as declared.
  • Expiry or rotation due. For a certificate this is notAfter, read from the copy actually being served. For a lease or a dynamic credential it is the lease expiry, which may be minutes away. For a static password it is a policy date, which is a promise rather than a measurement, and should be labelled so.
  • Rotation mechanism. The specific procedure, whether it is automated, and its worst-case wall-clock duration including approvals. An expiry date without this is not yet a lead time, and lead time is what alerting needs.
  • Blast radius. Two separate answers: what stops working while this credential is invalid, and what an attacker can reach with it. Availability impact and authority granted are different questions and a record that conflates them will mis-prioritise both incidents and rotation order.
  • Provenance. How the entry got here, when the credential was last observed in the wild, and when a human last confirmed the attested fields. An entry with no recent observation is not wrong, it is unverified, and the difference matters.

An identifier that survives renewal

The obvious candidates all fail. A hostname and port identify an endpoint, and the certificate behind it changes. A filesystem path is retargeted by renewal tooling. A serial number identifies one issued certificate and is deliberately different after every reissue. What stays constant across a renewal is the key material, and key material has a natural fingerprint: the digest of its encoded public key.

$ openssl pkey -in app.key -pubout | openssl sha256
SHA2-256(stdin)= 75061de3387b8969c6c33ba0931ec0e541ad4c90a4ee2ec3e734e031af66874b
$ openssl x509 -in app.crt -noout -pubkey | openssl sha256
SHA2-256(stdin)= 75061de3387b8969c6c33ba0931ec0e541ad4c90a4ee2ec3e734e031af66874b

The two commands read entirely different files and produce the same digest, because both emit the same encoded public key. That single value does three jobs at once. It is the primary key for the key material in the register. It proves this certificate belongs to this private key, which is the deployment check that catches a mismatched pair before a service refuses to start. And because it is computable from the certificate alone, a scanner that has never touched a private key can still report which registered key backs the endpoint it just probed.

Certificates still need their own identity, because you revoke certificates rather than keys, and revocation is expressed as a serial under an issuer. Both facts are one command away:

subject=CN=app.lab.example
issuer=O=RunBook Academy Lab, CN=RunBook Lab Server Issuing CA
serial=21173B360D80F4A69A91164F1067F4F81A1B1B6E
notBefore=Aug 26 21:19:00 2026 GMT
notAfter=Nov 24 21:19:00 2026 GMT

A serial is unique only within an issuer, so the certificate identity is the pair. The record therefore carries two identifiers: the key digest, which persists, and the issuer-and-serial pair, which changes at every reissue and is what a revocation request must name.

Reconciliation is what keeps it true

A register is not a document that is edited. It is the output of a loop that runs on a schedule and compares two inputs: what discovery observed, and what the register believes.

flowchart LR
    A["Discovery observations"] --> C{"Join on key digest"}
    B["Registered records"] --> C
    C --> D["Matched: refresh last_seen"]
    C --> E["Observed, not registered: a finding with an owner"]
    C --> F["Registered, not observed: ages to unverified"]

Three outputs, three different pieces of work. Matched entries have their observation timestamp refreshed and their measured fields overwritten from reality, because reality wins over any field that can be measured. Observed but unregistered is the interesting one: a credential is in production that nobody declared, and that is a ticket with an owner, not a row silently appended. Registered but unobserved is the one teams get wrong. It does not mean the credential is gone. It means discovery did not see it, which could equally be a scanner that lost network access, so the entry ages into an unverified state and only becomes a removal candidate after several consecutive misses and a human confirmation.

Why the spreadsheet always rots

A spreadsheet does not decay because people are careless. It decays because it has no writer other than a human, no observation timestamp, and no consequence for being wrong. Every field in it is an assertion made once, and nothing in the system ever contradicts it. The moment a certificate is renewed by automation, the sheet is wrong and nothing anywhere notices.

The structural fix is to change what kind of object the register is. Machine-measured fields, meaning identity, expiry, location and issuer, are written only by the reconciliation job and are never hand-edited, because a hand edit is a lie that survives the next run. Attested fields, meaning owner, blast radius and rotation mechanism, are hand-maintained but carry a confirmation date, and an entry whose attestation has expired is reported as a gap in the same way an unregistered credential is.

Store the whole thing as one machine-readable record per credential, in version control, so that every change is attributable and reviewable and the history answers when a field became wrong. Render the human views from it. The moment the readable view becomes the source of truth, the decay starts again.

key_digest: sha256:75061de3387b8969c6c33ba0931ec0e541ad4c90a4ee2ec3e734e031af66874b
kind: tls-server-key
owner_rota: platform-networking
attested_on: 2026-08-26
certificates:
  - issuer: O=RunBook Academy Lab, CN=RunBook Lab Server Issuing CA
    serial: 21173B360D80F4A69A91164F1067F4F81A1B1B6E
    not_after: 2026-11-24T21:19:00Z
    observed_at_endpoint: api.example.com:443
locations:
  - host: web-01
    path: /etc/ssl/private/api.example.com.key
    source: observed
rotation_mechanism: acme-client-with-reload-hook
worst_case_rotation_minutes: 45
blast_radius_availability: public API terminates here, no second listener
blast_radius_authority: impersonation of api.example.com to any client

Production discipline

  1. Let measurement overwrite declaration. Any field a scanner can determine is owned by the scanner. Humans own only what cannot be measured, and those fields carry a confirmation date.
  2. Never delete on a single miss. Absence in one run is evidence about the scanner as much as about the estate. Age entries through an unverified state and require a human to retire one.
  3. Alert on the deltas, not the totals. A jump in unregistered credentials means something is issuing outside the sanctioned path. A jump in unobserved entries usually means discovery broke. Both deserve attention; neither is visible in a count.
  4. Record the worst-case rotation duration alongside the expiry. Everything downstream, especially alert thresholds, is derived from the difference between those two numbers rather than from the expiry alone.
  5. Keep the register in version control and generate the views. The question that matters after an incident is when a field became wrong and who changed it, and only a reviewed history can answer that.

Cross-course references

  • Linux for Production Sysadmins - Part LXXIV (Drift) covers detecting divergence between declared host state and actual host state, which is the same reconciliation pattern applied to configuration rather than to credentials.
  • Git, CI/CD & GitOps for Infrastructure Engineers - Part LXIV (Auditability) covers making change history attributable, which is what turns the register from a claim into evidence.
  • Observability for Production Sysadmins - Part LXXXIV (ConfigAsCode) covers keeping generated configuration reviewable in version control instead of edited in place.

Quiz

Knowledge check · 4 questions

  1. Q1. Which identifier lets a register recognise that a certificate observed today is the same managed credential it recorded three months ago, across a renewal?

  2. Q2. A registered credential that discovery failed to observe in one reconciliation run should be treated as unverified rather than removed.

  3. Q3. Explain why blast radius must be recorded as two separate answers rather than one, and give the question each answer serves.

  4. Q4. Work out what the reconciliation loop should do with each of these two classes of record, and what should be alerted.

    The nightly reconciliation for the estate at example.com reports 412 matched records, 9 credentials observed at endpoints but present in no record, and 137 records that were not observed at all. Yesterday the unobserved count was 4. The scanner runs from one subnet and was redeployed yesterday afternoon.

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