Skip to content
The Vault lab

The Vault lab

A throwaway HashiCorp Vault (1.21.4, dev mode, in memory), served over TLS with a certificate from qfence-orchestrator pki, and a PostgreSQL 17 for its database secrets engine. Everything in it is written with the real vault CLI, as an operator would write it. It checks that qfence’s Vault provider (pkg/secrets) reads what Vault’s own tools write, signs in the ways ADR 0011 §11 names, and holds, renews and revokes leases (§4). Never use it for anything real: the root token is qfence-lab-root, and every value below is public.

deploy/vault-lab/lab.sh up
QFENCE_VAULT_LAB=deploy/vault-lab/.lab go test ./pkg/secrets ./cmd/orchestrator -run RealVault -p 1 -v
deploy/vault-lab/lab.sh down

-p 1 runs the two packages one after the other: one test pauses Vault. lab.sh uses only the docker CLI and copies its files into the containers, so it runs against a local daemon and against a remote one: CI’s test_vault job runs it in a docker:dind service. It builds qfence-orchestrator (Go) for its pki command, and needs GNU tar.

What it contains

PathEngineWritten withFields
secret/qfence/lab-fortigateKV 2vault kv put secret/qfence/lab-fortigate token=...token
secret/qfence/lab-bmcKV 2vault kv put -mount=secret qfence/lab-bmc ...username, password
secret/qfence/deletedKV 2vault kv put, then vault kv deletenone: the latest version is deleted
secret/other/not-for-qfenceKV 2vault kv puttoken, outside qfence’s policy
kv1/qfence/oldKV 1vault kv put kv1/qfence/old token=...token
database/creds/qfencedatabasevault write database/roles/qfence ...username, password: a new PostgreSQL role per read, leased for an hour (24 h at most)
database/creds/qfence-shortdatabasethe same, with secondsleased for 6 s, 10 s at most: renewals, the max TTL and reading again show within a test

The policy qfence-read reads and lists under qfence/ in both KV engines, reads the two database roles’ credentials, and renews and revokes the leases of those two roles alone (sys/leases/renew/database/creds/qfence/* and its revoke): qfence never writes a secret.

How qfence signs in:

MethodHereIts token
Certificate (the default)auth/cert, role qfence: a client certificate from .lab/pki/client with the common name qfence-lab. .lab/pki/untrusted holds one from another CA, which Vault refusesperiodic, an hour at a time
role qfence-short, the same certificate6 s, 10 s at most: renewed, then replaced by signing in again
AppRoleauth/approle, role qfence: .lab/approle/role-id and .lab/approle/secret-idperiodic, an hour at a time
Static token (development).lab/reader.token, qfence-lab-readerno end

A provider for the lab, in an orchestrator’s -secrets-config:

providers:
  - id: vault
    type: vault
    site: dc1
    address: https://127.0.0.1:18200
    ca_file: deploy/vault-lab/.lab/pki/server/ca.pem
    mounts:
      - {path: secret}
      - {path: kv1, kv_version: 1}
      - {path: database, engine: leased}
    auth:
      role: qfence
      cert_file: deploy/vault-lab/.lab/pki/client/qfence-lab.pem
      key_file: deploy/vault-lab/.lab/pki/client/qfence-lab-key.pem

Then vault:secret/qfence/lab-bmc#password names one field of secret/qfence/lab-bmc, and vault:database/creds/qfence a PostgreSQL login of this orchestrator’s own, renewed while Vault answers and revoked when it stops.

The lab’s state is .lab (or VAULT_LAB_DIR): address (the URL), postgres.url (the superuser’s, for a test to see a leased role come and go), container, the certificates and the AppRole files. VAULT_LAB_PORT (18200) and VAULT_LAB_PG_PORT (15433) move the published ports.