Vault mTLS & Proxy Setup (Runbook)
Quick Commands
# edit + deploy
git status
git add -A
git commit -m "docs: update"
git push
# rebuild static blog output (local)
cd site
npm ci --no-audit --no-fund
npm run build
# VPS: pull only
# (on server)
git pull --ff-only
Runbook – Vault mTLS & proxy setup (as of 2025-09-29)
This document summarizes the exact steps and examples we executed, plus short explanations of how everything fits together and which script to run for which use case.
1) Architecture & terms (short)
- Vault (server): runs with PKI mount
pki-testand auth methods likeauth/cert(mTLS) andapprole. - Intermediate CA vs. chain:
- Intermediate = from Vault
pki-test/cert/ca. - Chain = intermediate + root (e.g. for NGINX as
current-ca-chain.pem). - For Vault TLS verification, it worked best to use the full chain (otherwise you saw “x509: unknown authority”).
- Intermediate = from Vault
- Agents:
- App agent (e.g. user
nctest) fetches leaf certificates (rolenginx-nctest) via mTLS. - Proxy CA agent (user
proxytest) updates only the CA chain for NGINX (no leaf).
- App agent (e.g. user
- Two agent units on
proxytest:vault-agent-proxytest-ca.service(new, CA chain only).vault-agent-proxytest.service(old, had TLS verify errors) → disable the old one (see troubleshooting).
Standard paths per user:
$HOME/vault/mtls/agent.{crt,key} # mTLS client cert/key (for auth/cert)
$HOME/vault/ca/ca.pem # CA file (yours initially was just “Intermediate”)
$HOME/nginx/ca/current-ca-chain.pem # NGINX-Chain (Intermediate + Root)
2) Scripts and what they do
-
setup-vault-agent-mtls-client-config-v4.5.sh
Provisions an mTLS client certificate for a Linux user (e.g.nctest,proxytest) includingauth/certmapping + policy.
Result:$HOME/vault/mtls/agent.crt|key,$HOME/vault/ca/ca.pem, mapping underauth/cert/certs/<mapping>. -
setup-vault-agent-app-config-v4.3.sh
Builds the app agent config (leaf issuance, templating, post-hook) usingcertauth (mTLS). -
setup-vault-agent-proxy-config-v4.6.sh
Builds the proxy CA agent config (reads intermediate from Vault, writes a chain for NGINX, starts a user unit).
Result:~/.vault-agent-<app>-ca/...andvault-agent-<app>-ca.servicerunning. -
distribute_ca_to_agents_v3.sh
Copies root / intermediate / chain to one or more users (from Vault or locally) to standardize trust material. -
vault-tls-check.sh
One-shot TLS/mTLS verification against Vault (or any TLS server). Great to validate CA/mTLS files without root.Note: fix a small typo in the script (
end→fi).
3) Playbooks (exactly how we did it)
A) App nctest – mTLS + app agent
-
Create the mTLS client cert for
nctest
(Policy/Mapping:pki-issue-nctest, Mapping-Nameagent-nctest)sudo -E ./setup-vault-agent-mtls-client-config-v4.5.sh \ --env test --config ./config/apps.yaml --app nctest \ --cn "agent-nctest.test.privsec.ch" \ --mapping-name "agent-nctest" \ --mapping-policy "pki-issue-nctest"Result includes:
/home/nctest/vault/mtls/agent.crt|key,/home/nctest/vault/ca/ca.pem -
Configure the app agent (leaf issuer, auth=cert)
sudo -E ./setup-vault-agent-app-config-v4.3.sh \ --env test --config ./config/apps.yaml --app nctest --auth cert -
Check (as
nctest, without root)-
Live TLS:
./vault-tls-check.sh --addr 127.0.0.1:22300 --sni vault.test.privsec.ch \ --cafile "$HOME/vault/ca/ca.pem" -
If the server requires mTLS:
./vault-tls-check.sh --addr 127.0.0.1:22300 --sni vault.test.privsec.ch \ --cafile "$HOME/vault/ca/ca.pem" \ --mtls-cert "$HOME/vault/mtls/agent.crt" \ --mtls-key "$HOME/vault/mtls/agent.key"
-
B) Proxy proxytest – mTLS client + CA-chain agent
-
Create the mTLS client cert for
proxytest
(Policy/Mapping:pki-ca-read-proxytest, Mapping-Nameproxytest-test-vault)sudo -E ./setup-vault-agent-mtls-client-config-v4.5.sh \ --env test --app proxytest \ --mapping-policy "pki-ca-read-proxytest" -
Set up the proxy CA agent (writes the chain for NGINX)
sudo -E ./setup-vault-agent-proxy-config-v4.6.sh \ --env test --app proxytestAusgabe (bei dir):
Root CA gespiegelt → /home/proxytest/vault/ca/ca.pem
CA chain present: /home/proxytest/nginx/ca/current-ca-chain.pem -
TLS check (as
proxytest, without root)-
With chain (recommended):
./vault-tls-check.sh --addr 127.0.0.1:22300 --sni vault.test.privsec.ch \ --cafile "$HOME/nginx/ca/current-ca-chain.pem" -
Public IP:
./vault-tls-check.sh --addr <public-ip>:22300 --sni vault.test.privsec.ch \ --cafile "$HOME/nginx/ca/current-ca-chain.pem" -
(Optional) With mTLS:
./vault-tls-check.sh --addr 127.0.0.1:22300 --sni vault.test.privsec.ch \ --cafile "$HOME/nginx/ca/current-ca-chain.pem" \ --mtls-cert "$HOME/vault/mtls/agent.crt" \ --mtls-key "$HOME/vault/mtls/agent.key"
-
C) CA an mehrere User verteilen (optional)
-
Intermediate aus Vault an App-User pushen:
sudo -E ./distribute_ca_to_agents_v3.sh \ --env test --config ./config/apps.yaml \ --which intermediate --users "nctest apptest" -
Chain for
**proxytest**(NGINX):sudo -E ./distribute_ca_to_agents_v3.sh \ --env test --config ./config/apps.yaml \ --which chain --users proxytest \ --dest "/home/proxytest/nginx/ca/current-ca-chain.pem"
4) Day-2 operations
-
Check user services (as the respective user):
systemctl --user --state=running systemctl --user status vault-agent-<app>-ca.service journalctl --user -u vault-agent-<app>-ca.service -e -n 60 -
(Proxy only) Apply updated chain: the post-hook triggers an NGINX reload (containers with label
tls=true).
5) Troubleshooting (quick reference)
| Symptom (log/output) | Cause | Fix |
|---|---|---|
x509: certificate signed by unknown authority / unable to get local issuer certificate |
Wrong/too short CA file | For proxy/tests: use --cafile \"$HOME/nginx/ca/current-ca-chain.pem\". Or point agent HCL ca_cert to the chain. |
403 permission denied when issuing (pki-test/issue/...) |
Policy doesn’t cover the path | Fix policy (e.g. path \"pki-test/issue/nginx-nctest\" { capabilities=[\"update\"] }) and re-apply token/mapping. |
certificate required |
Server requires mTLS | Pass --mtls-cert and --mtls-key (or defaults under $HOME/vault/mtls/). |
| SAN/hostname errors | Missing SNI | Always set --sni vault.test.privsec.ch. |
Two agent units on proxytest |
Old and new units running | Disable old: systemctl --user disable --now vault-agent-proxytest.service (keep only *-ca.service). |
6) Recommended settings (stability)
-
Agent HCL
ca_cert: for the proxy agent, prefer the chain (same file that makes your OpenSSL checks succeed). Example:vault { address = "https://127.0.0.1:22300" ca_cert = "/home/proxytest/nginx/ca/current-ca-chain.pem" # optional: client_cert / client_key if mTLS to Vault is enforced }
- File permissions:
agent.key0600,agent.crt/ca.pem0644 (that’s what the setup scripts write).
- Always set SNI correctly (checks, NGINX upstreams, etc.).
7) Exact commands from the session (copy/paste)
nctest (App, mTLS + App-Agent):
sudo -E ./setup-vault-agent-mtls-client-config-v4.5.sh \
--env test --config ./config/apps.yaml --app nctest \
--cn "agent-nctest.test.privsec.ch" \
--mapping-name "agent-nctest" \
--mapping-policy "pki-issue-nctest"
sudo -E ./setup-vault-agent-app-config-v4.3.sh \
--env test --config ./config/apps.yaml --app nctest --auth cert
proxytest (Proxy-CA-Agent):
sudo -E ./setup-vault-agent-mtls-client-config-v4.5.sh \
--env test --app proxytest \
--mapping-policy "pki-ca-read-proxytest"
sudo -E ./setup-vault-agent-proxy-config-v4.6.sh \
--env test --app proxytest
TLS checks (without root):
# proxy user with chain (local)
./vault-tls-check.sh --addr 127.0.0.1:22300 --sni vault.test.privsec.ch \
--cafile "$HOME/nginx/ca/current-ca-chain.pem"
# public IP
./vault-tls-check.sh --addr <public-ip>:22300 --sni vault.test.privsec.ch \
--cafile "$HOME/nginx/ca/current-ca-chain.pem"
# app user with standard CA & (optional) mTLS
./vault-tls-check.sh --addr 127.0.0.1:22300 --sni vault.test.privsec.ch
./vault-tls-check.sh --addr 127.0.0.1:22300 --sni vault.test.privsec.ch \
--cafile "$HOME/vault/ca/ca.pem" \
--mtls-cert "$HOME/vault/mtls/agent.crt" \
--mtls-key "$HOME/vault/mtls/agent.key"
Note (check script): In
vault-tls-check.shat the very bottom, replace the accidentalendwith**fi**(otherwise Bash errors).
If you want, I can turn this into a one-page mini checklist for rolling out a new app/proxy based on these playbooks.