HashiCorp Vault Integration
Internal Scanning supports HashiCorp Vault as the secret backend for recorded-login credentials (username, password, MFA secret). When enabled, the scan-worker authenticates to Vault using Kubernetes auth and reads credentials at scan time — no Kubernetes Secrets needed for login data.
This feature covers recorded-login credentials only. Infrastructure secrets (license key, connector API key, registry credentials) continue to be managed via Helm values or Kubernetes Secrets as described in Secrets Management.
How it works
- You store recorded-login credentials (username, password, MFA TOTP secret) in Vault’s KV engine.
- In your recording file, you reference the credentials using inline secret references (e.g.,
secret={backend=hashicorp_vault, path=recorded-login/credentials, key=username}). - The Helm chart configures the scan-worker with Vault connection details (
HASHICORP_VAULT_*environment variables). - Each scan-worker pod authenticates to Vault using its ServiceAccount JWT via the Kubernetes auth method.
- The worker resolves all secret references in the recording before starting the scan — no token caching or renewal needed.
Two modes
| Mode | Description | When to use |
|---|---|---|
| External Vault | Point to your existing Vault cluster | Production — you already have Vault with policies, audit logging, and HA |
| Bundled Vault | Chart deploys a single-replica Vault in dev mode | Development, testing, or quick evaluations without existing Vault infrastructure |
Option 1: External Vault (production)
Prerequisites
- A running HashiCorp Vault instance reachable from the cluster
- Kubernetes auth method enabled and configured for the scanner’s cluster
- A Vault policy granting read access to the credential paths
- A Vault role bound to the
scan-workerServiceAccount in the scanner namespace
1. Configure Vault
Enable Kubernetes auth (if not already):
vault auth enable kubernetes
vault write auth/kubernetes/config \
kubernetes_host="https://kubernetes.default.svc.cluster.local"Create a policy for the scanner:
# scanner-policy.hcl
path "secret/data/recorded-logins/*" {
capabilities = ["read"]
}vault policy write scanner scanner-policy.hclCreate a role bound to the scan-worker ServiceAccount:
vault write auth/kubernetes/role/scanner \
bound_service_account_names=scan-worker \
bound_service_account_namespaces=scanner \
policies=scanner \
ttl=1h2. Store credentials in Vault
Store a recorded-login credential (KV v2):
vault kv put secret/recorded-logins/my-app \
username="testuser@example.com" \
password="s3cret" \
mfa_secret="JBSWY3DPEHPK3PXP"3. Install the chart with Vault enabled
# values.yaml
hashicorp_vault:
enabled: true
address: "https://vault.example.com:8200"
role: "scanner"
authMount: "kubernetes"
kvVersion: 2
# Optional: custom KV mount path (default: "secret")
# kvMount: "my-kv-engine"
# Optional: for Vault Enterprise
# namespace: "my-namespace"
# Optional: custom CA for TLS
# caCert: "/etc/vault/ca.crt"
# caCertConfigMap: "vault-ca-cert"helm install detectify-scanner detectify/internal-scanning-agent \
--version '~> 2.0' \
-n scanner \
-f values.yaml4. Reference credentials in your recorded login file
In your recorded login (.trail) file, replace hardcoded credentials with inline secret references pointing to Vault. The scanner resolves these at scan time.
Password or username:
{
"type": "UserAction",
"version": "1.0",
"command": "input",
"selectors": [
["css=#id-of-password-input"]
],
"values": [
"secret={backend=hashicorp_vault, path=recorded-logins/my-app, key=password}"
]
}TOTP/MFA secret:
{
"type": "UserAction",
"version": "1.0",
"command": "setLocalMFAWithVault",
"values": [
"secret={backend=hashicorp_vault, path=recorded-logins/my-app, key=mfa_secret}"
]
}See Recorded Login Secrets for full syntax details including the mount argument and TOTP setup.
Option 2: Bundled Vault (development/testing)
The chart can deploy a single-replica Vault instance in dev mode with Kubernetes auth pre-configured. This is useful for local development or testing the Vault integration without an external Vault cluster.
Bundled Vault runs in dev mode with a hardcoded root token (root). It is not suitable for production. Data is stored in-memory and lost on pod restart.
# values.yaml
hashicorp_vault:
enabled: true
bundled: true
role: "scanner"
authMount: "kubernetes"
kvVersion: 2helm install detectify-scanner detectify/internal-scanning-agent \
--version '~> 2.0' \
-n scanner \
-f values.yamlWhen bundled: true:
- A Vault Deployment, Service, ServiceAccount, and ClusterRoleBinding are created
- A bootstrap Job runs automatically to enable Kubernetes auth, create the scanner policy, and bind the
scan-workerrole hashicorp_vault.addressis automatically set tohttp://vault:8200
Writing test credentials to bundled Vault
After the bootstrap Job completes, write credentials:
kubectl exec -n scanner deploy/vault -- vault kv put \
secret/recorded-logins/my-test-app \
username="test@example.com" \
password="testpass123" \
mfa_secret="JBSWY3DPEHPK3PXP"Configuration reference
| Key | Default | Description |
|---|---|---|
hashicorp_vault.enabled | false | Enable Vault as the secret backend for recorded-login credentials |
hashicorp_vault.bundled | false | Deploy a bundled Vault instance (dev mode, not for production) |
hashicorp_vault.address | "" | Vault server address. Auto-set to http://vault:8200 when bundled: true |
hashicorp_vault.role | "scanner" | Vault Kubernetes auth role name |
hashicorp_vault.authMount | "kubernetes" | Vault auth mount path |
hashicorp_vault.kvVersion | 2 | Vault KV engine version (1 or 2) |
hashicorp_vault.kvMount | "secret" | Vault KV engine mount path |
hashicorp_vault.namespace | "" | Vault Enterprise namespace (leave empty for OSS Vault) |
hashicorp_vault.caCert | "" | Path to CA certificate file for Vault TLS verification |
hashicorp_vault.caCertConfigMap | "" | Name of a ConfigMap containing the CA certificate (mounted into scan-worker pods) |
KV version differences
The scanner supports both KV v1 and KV v2 engines. Set hashicorp_vault.kvVersion to match your engine:
| Version | Path behavior | Notes |
|---|---|---|
| KV v1 | Reads directly from the logical path | Simple key-value, no versioning |
| KV v2 | Injects /data/ into the API path automatically | Supports versioned secrets; the scanner always reads the latest version |
You provide the logical path (e.g., secret/recorded-logins/my-app). The scanner handles the API path construction internally.
Troubleshooting
Worker pods fail with “permission denied”
The Vault role is not bound to the correct ServiceAccount or namespace. Verify:
vault read auth/kubernetes/role/scannerEnsure bound_service_account_names includes scan-worker and bound_service_account_namespaces includes your scanner namespace.
Worker pods fail with “secret not found”
Check that the path in your secret reference (e.g., secret={backend=hashicorp_vault, path=recorded-logins/my-app, key=password}) matches an existing KV path in Vault. For KV v2, the logical path should not include /data/ — the scanner adds it automatically.
Vault unreachable from pods
Ensure the Vault address is resolvable and reachable from the cluster network. For external Vault with TLS using a private CA, you need both settings:
hashicorp_vault.caCert— the file path where the CA cert will be mounted inside scan-worker pods (e.g.,/etc/vault/ca.crt)hashicorp_vault.caCertConfigMap— the name of a ConfigMap in the scanner namespace containing the CA certificate (key:ca.crt)
The scan-manager mounts this ConfigMap into each scan-worker pod at the path specified by caCert. Create the ConfigMap before installing the chart:
kubectl create configmap vault-ca-cert -n scanner \
--from-file=ca.crt=/path/to/your/vault-ca.crtBundled Vault bootstrap Job stuck
The bootstrap Job waits for Vault to be ready. Check the Vault pod is running:
kubectl get pods -n scanner -l app=vault
kubectl logs -n scanner deploy/vaultNext steps
- Secrets Management — Chart-level secrets (license key, registry)
- Configuration — Full values reference
- Getting Started — Install the chart