Secrets, PKI & CertificatesI · Secrets and Identity FoundationsFoundations
A working taxonomy of infrastructure credentials
What you'll learn
- Place any credential you meet into one of eight classes
- State where the sensitive half lives and what issues it for each class
- Explain why a signed self-describing token resists revocation
- Distinguish rotating a data encryption key from rotating the key that wraps 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
Nobody manages credentials in the abstract. They manage a particular class of thing with particular habits, and the habits are what catch people out: a token that cannot be revoked, a key whose rotation means touching forty hosts, an encryption key whose rotation is either trivial or a six-month project depending on which key was meant. A taxonomy earns its place if it predicts those habits before you meet them.
Three facts define a credential class
For any credential, three facts do most of the predictive work: where the sensitive half lives, what brought it into existence, and what it unlocks. Together they tell you who can steal it, who can replace it, and how much you lose if the first happens before the second.
| Class | Where the sensitive half lives | What issues it | What it unlocks |
|---|---|---|---|
| Password | a password manager, a config file, an automation store | whoever last set it | one account on one system, with all that account can do |
| API token | an environment variable, a CI secret store, a client config | the service authorisation system | the scope granted at issuance, until expiry or revocation |
| Private key | a key file, an agent, a hardware module | nobody: you generate it locally | whatever the matching public half has been registered with |
| Cloud credential | instance metadata, a profile file, an environment variable | the cloud identity service | every API call the attached policy permits, account wide |
| Database credential | a config file, or a lease held by a secret manager | the database, or a broker holding a privileged account | the rows and statements the role has been granted |
| Encryption key | a key management service or a hardware module | the key management service | the plaintext of everything wrapped under it |
| Certificate private key | a key file beside the certificate, or a hardware module | you generate it; the CA signs only the public half | the right to answer as that name in a handshake |
| Bootstrap credential | provisioning data, a template, an operator action | the provisioning or enrolment system | the first real credential, and so everything downstream |
flowchart TD
A["Infrastructure credentials"] --> B["Bearer\ncopy it and you are it"]
A --> C["Possession\nprove it, never send it"]
A --> D["Data protection\nunwraps ciphertext"]
B --> B1["Passwords"]
B --> B2["API tokens"]
B --> B3["Bootstrap credentials"]
C --> C1["SSH and TLS private keys"]
C --> C2["Certificate private keys"]
D --> D1["Data encryption keys"]
D --> D2["Wrapping keys in a KMS"]
The tree splits on behaviour rather than on technology. Bearer credentials are copied and replayed, so their defence is scope and lifetime. Possession credentials are never transmitted, so their defence is where the key rests and who can read that location. Data protection keys are different again: they do not authenticate anybody, they merely make ciphertext readable, and their defence is that the key and the data never sit in the same place. Cloud and database credentials are usually bearer in form but platform-issued in lifecycle, which is why they get their own treatment later in the lesson.
Bearer credentials: passwords, tokens and bootstrap secrets
A password carries a coordination problem that has nothing to do with cryptography. A PostgreSQL role holds one password at a time, so changing it is atomic on the database side and simultaneous for every client on the other side. That mismatch is the entire reason zero-downtime password rotation uses two roles or two credential slots rather than one careful change window: if a single value must be true for everyone at once, there is no overlap period in which to move clients across.
An API token comes in two shapes that behave oppositely under revocation, and the difference is easy to miss because both arrive as a string in a header. An opaque token is a random identifier that the issuer looks up on every request, so revocation is a row deletion and takes effect on the next call. A self-describing signed token carries its own claims and is validated by checking a signature, which means the verifier never contacts the issuer. That is exactly what makes it scale, and exactly what makes it hard to revoke: unless the design adds a lookup it was built to avoid, a leaked token remains valid until its expiry passes. If the answer to the revocation question is uncomfortable, the lifetime question has to carry the weight.
A bootstrap credential exists to obtain a better credential. An AppRole login is a clean example: a role identifier plays the part of a username and may sit in configuration, while the secret identifier is the sensitive half and should be delivered separately and expire quickly. Exchanging the pair yields an operational token.
"auth": {
"client_token": "s.<REDACTED>",
"accessor": "n5GSr33ga2aoj00X03Z3y4Qh",
"policies": ["app-read", "default"],
"token_policies": ["app-read", "default"],
"metadata": {"role_name": "app-role"},
"orphan": true,
"lease_duration": 1200,
"renewable": true
}
Three fields matter operationally. The policy list is the entire authorisation of the resulting token, so it is the thing to review. The lease duration of 1200 seconds is the window during which a stolen token is useful without renewal. The accessor is a handle that lets an operator revoke or inspect the token without holding the token itself, which is what makes token management possible without token exposure.
Possession credentials: private keys and certificate keys
Nothing issues a private key. You generate it, which is why the issuer answer for this class is “nobody” and why the location of generation is a security decision: any party that generates a key on your behalf has held a copy of it. Generate on the host that will use it, or in the hardware module that will hold it, and let only the public half travel.
The act that makes a private key useful is registration of its public half. For SSH that is a line appended to a file. For TLS it is a certificate authority signing a request. That request carries what it would like the certificate to say, and the wording in the tooling is precise about the status of those wishes: they appear under a heading that reads Requested Extensions. The authority is free to ignore them, and in practice a well-run authority issues from its own policy template rather than from whatever the requester asked for. A signing request is a request, not a contract.
Because the certificate and the key are separate files that must correspond, a routine check earns its keep. Compare the digests of the public key derived from each.
openssl pkey -in app.key -pubout | openssl sha256
openssl x509 -in app.crt -noout -pubkey | openssl sha256
SHA2-256(stdin)= 75061de3387b8969c6c33ba0931ec0e541ad4c90a4ee2ec3e734e031af66874b
SHA2-256(stdin)= 75061de3387b8969c6c33ba0931ec0e541ad4c90a4ee2ec3e734e031af66874b
Matching digests prove the pair belongs together. Mismatched digests explain a service that refuses to start after a deployment moved one file and not the other, and they explain it in one second rather than after an hour of reading TLS error messages. Rotation for this class is an inventory exercise rather than a cryptographic one: generating a new key takes moments, and re-registering the public half in every place the old one was trusted is the part that takes a fortnight if nobody kept a list.
Data protection keys, and why rotating one is cheap
An encryption key authenticates nobody. It exists to turn ciphertext back into plaintext, and its class is defined by a structure rather than by a protocol: almost every serious system uses two levels. Data is encrypted under a data encryption key. That data encryption key is stored beside the ciphertext, itself encrypted under a wrapping key that never leaves the key management service. To read, the application sends the wrapped key to the service, receives the unwrapped key, and decrypts locally.
The consequence is the most useful fact in this lesson. Rotating the wrapping key re-wraps a small number of short keys and touches no data at all. Rotating a data encryption key means re-encrypting everything it covers. A policy stating that encryption keys are rotated annually therefore describes either a trivial operation or a multi-month migration, and the only way to know which is to ask whose key is meant.
The same structure explains a Kubernetes caveat worth carrying now rather than learning later. Encryption at rest with locally managed keys protects against a compromise of etcd, not against a compromise of a control-plane host, because the key sits on that host. Moving to a key management provider is what changes the threat model, and the choice of algorithm matters too: the documented strength of the CBC-based provider is weak, and the plain GCM provider carries a rotation requirement tied to the number of writes.
Credentials a platform mints on demand
Cloud credentials and database credentials look like the bearer class, but their lifecycle belongs to a platform rather than to a person, and that changes how they should be handled. A cloud credential obtained by assuming a role arrives with an expiry already attached and is delivered through metadata rather than configuration, so no file on disk ever holds it. Its blast radius is defined by policy, which means the interesting review question is never about the key material at all.
A dynamic database credential goes further: the credential does not exist until somebody asks for it.
{
"lease_id": "database/creds/app-readonly/xoHI541EXoFgKn1OTiusOdd9",
"lease_duration": 120,
"renewable": true,
"data": {
"password": "[REDACTED]",
"username": "v-token-app-read-ghRGRAxnCRE9Q8zLVsIw-1787779423"
}
}
A broker holding a privileged account created that role on request, gave it a generated name that encodes its origin, and set an expiry on the role inside PostgreSQL itself. The lease is enforced twice over: the broker will revoke at expiry, and the database will refuse the login independently once its own validity timestamp passes. When the lease is revoked, the role is dropped, and a client still holding the password receives an error saying the role does not exist. There is nothing left to steal, which is the whole argument for the pattern.
Production discipline
- Name the class before designing the handling. The class predicts the rotation cost, and rotation cost is what decides whether your policy will actually be followed.
- Ask which key somebody means by rotation. Wrapping key or data key, certificate key or CA key: the two answers differ by orders of magnitude in effort.
- Generate private keys where they will live. A key generated elsewhere has been copied at least once, and the copy is outside your control from that moment.
- Treat self-describing tokens as unrevokable until proven otherwise. Set the lifetime you would accept for an attacker holding it, because that is the lifetime you have.
- Keep the public half of every key pair in an inventory. Rotation of a possession credential is limited entirely by your ability to enumerate where the old public half was trusted.
Cross-course references
- Docker & Containers for Production Sysadmins - Part XXXVIII (Secrets) covers how a build and a running container receive a credential without persisting it into an image layer.
- Terraform for Production Sysadmins - Part XI (State-Security) covers what happens to a credential that passes through a resource definition and lands in state.
- Ansible for Production Sysadmins - Part XXI (Secrets) covers the control-machine side of the same problem, where encrypted variables are decrypted into ordinary ones at run time.
Quiz
Knowledge check · 4 questions
Q1. An internal API issues signed, self-describing tokens with a thirty-day expiry, validated locally by each service without contacting the issuer. One token is leaked. What is the practical exposure?
Q2. A certificate authority issues the private key that goes with the certificate it signs.
Q3. Explain why rotating a wrapping key is cheap while rotating a data encryption key is expensive.
Q4. Classify each credential in the incident and decide the order of work.
At 09:40 on 2026-08-26 a laptop is reported stolen. It held a checkout of the platform repository, an SSH private key registered on sixty hosts, a personal cloud access key created in 2024, a file containing the AppRole secret identifier used by the staging deployer, and a copy of an internal CA certificate.
Passing score: 75%. Answers are checked in this browser.