Skip to Content
Internal ScanningDeployHelmHashiCorp Vault (Recorded Logins)

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

  1. You store recorded-login credentials (username, password, MFA TOTP secret) in Vault’s KV engine.
  2. In your recording file, you reference the credentials using inline secret references (e.g., secret={backend=hashicorp_vault, path=recorded-login/credentials, key=username}).
  3. The Helm chart configures the scan-worker with Vault connection details (HASHICORP_VAULT_* environment variables).
  4. Each scan-worker pod authenticates to Vault using its ServiceAccount JWT via the Kubernetes auth method.
  5. The worker resolves all secret references in the recording before starting the scan — no token caching or renewal needed.

Two modes

ModeDescriptionWhen to use
External VaultPoint to your existing Vault clusterProduction — you already have Vault with policies, audit logging, and HA
Bundled VaultChart deploys a single-replica Vault in dev modeDevelopment, 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-worker ServiceAccount 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.hcl

Create 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=1h

2. 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.yaml

4. 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: 2
helm install detectify-scanner detectify/internal-scanning-agent \ --version '~> 2.0' \ -n scanner \ -f values.yaml

When 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-worker role
  • hashicorp_vault.address is automatically set to http://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

KeyDefaultDescription
hashicorp_vault.enabledfalseEnable Vault as the secret backend for recorded-login credentials
hashicorp_vault.bundledfalseDeploy 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.kvVersion2Vault 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:

VersionPath behaviorNotes
KV v1Reads directly from the logical pathSimple key-value, no versioning
KV v2Injects /data/ into the API path automaticallySupports 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/scanner

Ensure 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.crt

Bundled 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/vault

Next steps

Last updated on