Components
Audience: Platform operator
vault-db-injector runs as four cooperating components. They share the
same Go binary and select their role at startup via the mode config
key.
Injector (webhook)
File: pkg/injector/injector.go
The injector runs as a Deployment and serves a Kubernetes Mutating
Admission Webhook on TLS. When a pod carrying the
vault-db-injector: "true" label is admitted, the webhook reads the
db-creds-injector.numberly.io/* annotations and decides what to put
into the pod's env.
In legacy mode, the webhook calls CanIGetRoles against Vault to
verify that the pod's ServiceAccount is bound to the requested DB
role, issues a Vault orphan token holding the role's policy, fetches
dynamic credentials, and writes them as plaintext env vars onto the
container spec.
In NRI mode (projected auth), the webhook does not fetch
credentials. It writes opaque __VDBI_PH_<64hex>___ placeholders into
the env — one pair per dbConfig — and stamps a per-dbConfig UUID into
the db-creds-injector.numberly.io/uuid annotation for later
correlation. CanIGetRoles is skipped because Vault attests pod
identity natively at pod-token time.
NRI plugin (DaemonSet)
Files: pkg/nri/...
The NRI plugin runs as a DaemonSet on every node, mounting
/var/run/nri/nri.sock from the host. It registers as an NRI plugin
with containerd or CRI-O. On every CreateContainer event it filters
by pod label, scans env for placeholders, and on a match fetches the
pod's identity from the kube-apiserver, logs into Vault as that pod
(projected mode) or as itself (legacy mode), issues the credentials,
and emits a ContainerAdjustment so runc starts the container with
the real env.
A per-node tmpfs cache at /run/<release-fullname>/nri/cache.json
holds unwrapped credentials between plugin restarts so a CrashLoop
does not require re-issuing creds on every retry. The cache is wiped
on node reboot.
Renewer (Deployment)
File: pkg/renewer/renewer.go
The renewer runs as a Deployment with leader election. Every 5 minutes
(configurable via SyncTTLSecond) the leader walks the KV bookkeeping
mount, calls auth/token/renew on each stored token, and
sys/leases/renew on each stored lease. In projected-SA mode the
renewer holds a minimal Vault policy: renew-only, no revoke, no KV
delete. Revocation is owned by the revoker exclusively.
Revoker (Deployment)
File: pkg/revoker/revoker.go
The revoker runs as a Deployment with leader election. The leader
watches the Kubernetes API for pod DELETE events filtered by the
vault-db-injector: "true" label. On a delete it revokes the pod's
token and lease, then wipes the KV entry. A 5-minute periodic
safety-net sweep (safetyNetSync) catches pods that died while the
watch was disconnected or the revoker was down.
In projected-SA mode the revoker owns all revocation: the renewer
no longer touches auth/token/revoke-orphan or KV delete.
Leader election
The renewer and revoker run multi-replica for high availability. Only
the elected leader does work; the others stand by. The webhook is
stateless and runs all replicas active. See operations
for the mechanics and the vdbi_is_leader metric.