CoursesVault from dev to productionTLS listeners and auto-unseal

TLS listeners and auto-unseal

api_addr, seals, and recovery keys done right.

In short. A Vault cluster that speaks cleartext HTTP on the open network is a secrets platform that advertises every token and response body to anyone on the wire.

Intermediate30 min · lesson 3 of 13

A Vault cluster that speaks cleartext HTTP on the open network is a secrets platform that advertises every token and response body to anyone on the wire. TLS is not a compliance checkbox here — it is the difference between encrypted storage at rest and plaintext in flight. Combined with auto-unseal, TLS and seal configuration are the two settings that decide whether a reboot is boring or a pageable ceremony.

This lesson sits on top of raft HA. You already know quorum and api_addr. Now you pin certificates correctly, separate listener concerns, choose a seal, and understand why recovery keys are not unseal keys when KMS holds the master key material.

Listener TLS that matches api_addr

Every production listener should require TLS. The certificate SAN list must include every DNS name and IP clients use — including the load balancer hostname and each node name if clients talk to nodes directly. Mismatched SANs produce intermittent failures that look like networking ghosts: some clients succeed (cached IPs), others fail TLS verify.

api_addr must be a URL clients can reach after redirects. Standbys forward writes to the leader and may redirect; if api_addr is https://127.0.0.1:8200, remote clients authenticate then fall into a black hole. cluster_addr stays on the private fabric (typically 8201). Do not reuse one address for both roles.

terminal
# sanity-check the cert clients will see
openssl s_client -connect vault.example.com:8200 -servername vault.example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName
vault status
output
subject=CN = vault.example.com
X509v3 Subject Alternative Name:
DNS:vault.example.com, DNS:vault-1.internal, DNS:vault-2.internal
Sealed false
HA Mode active

Auto-unseal vs Shamir unseal

With Shamir seal, a reboot leaves every node sealed until humans supply a threshold of key shares. That is secure and operationally expensive at 3 a.m. Auto-unseal delegates wrapping of the master key to AWS KMS, GCP Cloud KMS, Azure Key Vault, or another Vault Transit mount. Nodes unseal themselves on boot if they can reach KMS and the IAM/permissions still allow Decrypt.

Auto-unseal does not remove break-glass. Initialization returns recovery keys (sized with -recovery-shares / -recovery-threshold) used for generate-root and seal migration — not for operator unseal. Confusing those ceremonies is a classic outage prolonger. Document the seal type on the runbook cover.

vault.hcl (seal + listener excerpt)
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/etc/vault.d/tls/vault.crt"
tls_key_file = "/etc/vault.d/tls/vault.key"
}
seal "awskms" {
region = "us-east-1"
kms_key_id = "arn:aws:kms:us-east-1:111122223333:key/abcd-1234"
}
api_addr = "https://vault.example.com:8200"
cluster_addr = "https://vault-1.internal:8201"
Boot with auto-unseal
1Process starts
reads seal stanza
2KMS decrypt
unwraps master key material
3Unsealed
joins raft if peers healthy
4Serve traffic
no human unseal on reboot
If KMS or IAM breaks, nodes stay sealed — monitor KMS errors as a Vault availability signal.

Operational traps

Rotate TLS certificates one node at a time; wait for autopilot health between nodes. Restrict KMS key policy to the Vault instance roles only. Store recovery keys with the same custody as Shamir shares. Disable cleartext listeners entirely — an accidental second listener on 8200 without TLS undoes the hard work.

Recovery keys ≠ unseal keys
Under auto-unseal, init prints recovery keys. Feeding them to vault operator unseal fails. Use them for generate-root and seal migration only.

Try this

In a lab with TLS enabled, intentionally set api_addr to localhost on one standby and watch a client login succeed then fail on subsequent requests. Fix api_addr and recheck.

terminal
vault status -address=https://vault.example.com:8200
# after mis-set api_addr on a node, expect redirect/connect errors on write paths
vault kv put secret/lab ok=1
output
Key Value
Sealed false
HA Mode active
Error writing data: ... dial tcp 127.0.0.1:8200: connect: connection refused
# FAIL until api_addr is client-reachable

Takeaway

TLS SANs and reachable api_addr keep clients honest; auto-unseal keeps reboots boring; recovery keys keep break-glass possible. Treat all three as one production unit.

Next: hang real doors on the building — OIDC for humans and Kubernetes auth for workloads.

Quick check
01Why must api_addr be client-reachable?
Incorrect — Raft is integrated storage; etcd is unrelated here.
Correct — a localhost api_addr black-holes remote callers.
Incorrect — KMS uses the seal stanza, not api_addr.
Incorrect — api_addr is an address advertisement, not encryption.
02With seal "awskms", init should use…
Incorrect — Those size Shamir unseal keys.
Correct — auto-unseal returns recovery keys.
Incorrect — You still size recovery shares.
Incorrect — PGP wrapping is optional, not a substitute.

Related