Rotate Everything in Production (Part 2): Admin mTLS + Vault Server TLS (and Why These Two Come First)
This part covers the first two rotations from the run:
- Admin client cert (mTLS to Vault)
- Vault server TLS cert (listener cert)
These are the control-plane certs. If they fail, everything downstream is harder to recover.
TL;DR
- Admin client cert first, so you always have a working identity.
- Server TLS second, because every client depends on it.
- Validate with
vault statusand an SNI handshake before moving on.
1) Admin client cert rotation (mTLS)
What it is
The client cert for privileged CLI access. In this run:
- mount:
pki-prod - role:
admin-client - CN:
vault-admin-prod - TTL:
720h
Files written
/root/vault/tls-admin/prod/admin.key(0600)/root/vault/tls-admin/prod/admin.crt(0644)/root/vault/tls-admin/prod/issuing_ca.pem
Why it must be first
If server TLS changes and something about trust/SNI/mTLS is wrong, you want a fresh, known-good client identity ready before the listener flips.
Environment exports (from the run)
export VAULT_CLIENT_CERT='/root/vault/tls-admin/prod/admin.crt'
export VAULT_CLIENT_KEY='/root/vault/tls-admin/prod/admin.key'
# VAULT_CACERT / VAULT_TLS_SERVER_NAME come from /root/vault/env/prod.env
vault status
Operational note: keep the key 0600, and treat this directory as sensitive. If it leaks, it is not “just a cert”.
2) Vault server TLS rotation
What it is
The cert presented by the Vault listener.
VAULT_ADDR=https://127.0.0.1:22400- SNI / TLS server name:
vault.prod.privsec.ch - owner:
vaultprod:vaultprod - intermediate mount:
pki-prod - TLS directory:
/home/vaultprod/tls-prod
Files written
/home/vaultprod/tls-prod/server.key(0600)/home/vaultprod/tls-prod/server.crt(0644)/home/vaultprod/tls-prod/fullchain.crt(server + intermediate)/home/vaultprod/tls-prod/ca_chain.pem(intermediate + root)
That last file matters: it contains the trust chain clients need to validate.
Why the chain layout matters
You can have a perfect leaf cert and still break clients if:
- the server presents an incomplete chain
- the client trusts the wrong CA bundle
- SNI does not match what clients validate (
vault.prod.privsec.ch)
Your run set SNI explicitly with VAULT_TLS_SERVER_NAME=vault.prod.privsec.ch.
Two checks right after server TLS changes
Check A: Vault auth path works
Using the admin exports above:
vault status
If this works, you validated: client auth + server TLS + CA trust + routing.
Check B: TLS handshake works (SNI explicitly)
openssl s_client -connect 127.0.0.1:22400 -servername vault.prod.privsec.ch -brief </dev/null
If this fails with resets or handshake errors, the problem is below Vault auth (listener, certs, chain, process not ready).
Common gotchas in this setup
- File ownership and permissions: use
vaultprod:vaultprodand0600for keys. - SNI mismatch: tests without
-servernamecan mislead you. - Restart timing: rotating files does nothing until the process reloads (Part 4).
Next up
Part 3 goes downstream:
- agent mTLS issuance + cert-auth mappings
- app leaf issuance (nginx roles, domain constraints)
- vault-agent restarts (and the version mismatch message)