HashiCorp Vault KV Connector¶
Built-in vault connector for HashiCorp Vault KV secrets engine v2 using token auth (X-Vault-Token).
This connector is not the Authifi tenant secrets vault in packages/server/src/config/vault-config.ts — that is a separate product integration. See Secrets management for Authifi vault settings.
Local Vault with Docker¶
Run Vault in dev mode (in-memory, auto-unsealed, root token from env):
docker run -d --name vault-dev \
-p 8200:8200 \
-e VAULT_DEV_ROOT_TOKEN_ID=dev-root-token \
-e VAULT_DEV_LISTEN_ADDRESS=0.0.0.0:8200 \
hashicorp/vault:1.15 server -dev
Or use the Compose overlay:
Enable KV v2 at mount secret¶
Dev images often ship with secret/ already mounted as KV v2. If not:
export VAULT_ADDR=http://127.0.0.1:8200
export VAULT_TOKEN=dev-root-token
vault secrets enable -version=2 -path=secret kv
HTTP equivalent: POST /v1/sys/mounts/secret with body {"type":"kv","options":{"version":"2"}}.
Root token → Floh connection field¶
| Vault (Docker env) | Floh connector field |
|---|---|
VAULT_DEV_ROOT_TOKEN_ID |
token (secret) |
Use a least-privilege token in production — not the dev root token.
address and allowPrivateNetworkEndpoint¶
| Floh runs on | Vault runs on | Typical address |
allowPrivateNetworkEndpoint |
|---|---|---|---|
Host (pnpm run dev) |
Host (-p 8200:8200) |
http://127.0.0.1:8200 |
Yes |
| Docker Compose (Floh + Vault) | Service vault:8200 |
http://vault:8200 |
Yes |
| Production cluster | Public ingress | https://vault.example.com |
No |
The address must be a bare base URL (no trailing slash, no embedded credentials). Floh validates it at connection resolution time and applies SSRF rules on every request unless the connection was validated with the private-network flag.
TLS recommendation for public addresses¶
For public (non-RFC1918) Vault endpoints, prefer https://. The connector validates address once at connection-config time and pins DNS only when the validator returns a resolved address — for arbitrary public hostnames DNS stays dynamic. An http:// public endpoint is therefore vulnerable to DNS rebinding between the config-time SSRF check and each subsequent request. TLS certificate validation on https:// defeats rebinding because a rebinding attacker cannot serve a valid certificate for the configured hostname. Private-network endpoints (loopback, RFC1918) are unaffected — allowPrivateNetworkEndpoint: true pins DNS to the resolved address.
Connection configuration¶
Create a connector instance via the Connectors API or UI with type vault.
| Field | Required | Secret | Purpose |
|---|---|---|---|
| address | Yes | No | Vault API base URL. |
| token | Yes | Yes | X-Vault-Token. |
| kvMount | No | No | KV v2 mount path (default secret). |
| namespace | No | No | Enterprise namespace (X-Vault-Namespace). |
| allowPrivateNetworkEndpoint | No | No | Allow localhost / RFC1918 / Docker-internal address targets. |
Commands¶
| Command | Description |
|---|---|
| test | GET /v1/sys/health — fails if sealed or not initialized. |
| kvGet | Read secret (path, optional version). Outputs data, version, createdTime. |
| kvPut | Create/replace secret (path, data JSON object). Outputs version. |
| kvPatch | Patch latest version (path, data JSON object). |
| kvDelete | Soft-delete latest version at path. |
| kvList | List keys under optional path prefix. |
data must be a JSON object string (for example {"apiKey":"abc"}).
Minimal policy (HCL)¶
path "secret/data/myapp/*" {
capabilities = ["create", "read", "update", "delete"]
}
path "secret/metadata/myapp/*" {
capabilities = ["list", "read", "delete"]
}
Adjust secret if you use a different kvMount.
Manual QA checklist¶
- test —
ok: true, not sealed. - kvPut — write
app/demowith{"env":"dev"}. - kvGet — read back
env. - kvList — list under
app/(expectdemoordemo/). - kvPatch — merge another field; kvGet shows both.
- kvDelete — soft-delete; kvGet should fail or show deleted per policy.
Integration tests (Testcontainers)¶
From the repo root (Docker required):
Troubleshooting¶
| Symptom | Likely cause |
|---|---|
permission denied |
Token policy missing data or metadata path. |
sealed / not initialized |
Cluster sealed or dev server not ready. |
Connection blocked on 127.0.0.1 |
Set allowPrivateNetworkEndpoint to true. |
no handler for route on kv ops |
Mount is KV v1 or wrong kvMount — enable KV v2. |
Vault API request failed on kvList |
Check step diagnostics.httpStatus. 404: list a parent folder, not the leaf secret path (after kvPut at app/demo, list app or "", not app/demo). 403: add list on metadata/. Wrong kvMount vs where you wrote secrets. |
Related code¶
| Area | Path |
|---|---|
| Connector definition | packages/server/src/modules/connectors/handlers/vault.ts |
| Client, KV ops, policy | packages/server/src/modules/connectors/handlers/vault-support/ |
| Unit tests | packages/server/test/unit/connectors/vault-*.test.ts |
| Integration tests | packages/server/test/integration/vault-kv.test.ts |
| Dev Compose overlay | docker/docker-compose.vault-dev.yml |