Skip to main content

HashiCorp Vault Integration

GGScout supports integration with HashiCorp Vault to collect and monitor your secrets, including both Vault Community Edition and Vault Enterprise (self-hosted and HashiCorp Cloud Dedicated). This guide will help you set up and configure the integration.

Supported Features

Available on all editions (Community and Enterprise):

  • KV1 and KV2 secret engines
  • Multiple secret versions collection
  • Path-based filtering
  • Token-based authentication
  • Optional explicit mount type (kv-v1/kv-v2) to skip sys/* auto-detection

Available on Vault Enterprise and HashiCorp Cloud Dedicated (HCD) only:

  • Namespace support

HashiCorp Cloud Dedicated Support

GGScout fully supports HashiCorp Cloud Dedicated (HCD) environments. When using HCD, namespaces are automatically included as part of the exported resource_id, following the Vault API pattern described in the HashiCorp documentation.

Namespace Filtering

Restricting Namespace Access (Enterprise and HCD only)

Namespaces are a Vault Enterprise and HCD feature. With these editions, you can limit which namespaces ggscout accesses using the namespaces configuration parameter:

[sources.vault]
type = "hashicorpvault"
vault_address = "${VAULT_ADDR}"
fetch_all_versions = true
namespaces = ["admin", "prod", "shared"] # Only fetch from these namespaces
info

If the namespaces parameter is not specified, ggscout will fetch secrets from all accessible namespaces. Use this parameter to limit the scope of secret collection and improve performance.

Resource-Level Filtering

You can also filter secrets by namespace directly in your include/exclude patterns. The namespace becomes part of the resource path, allowing for precise filtering:

# Include all secrets from the 'admin' namespace, 'kv' mount, under 'my_app' path
[[sources.vault.include]]
resource_ids = ["admin/kv/my_app/*"]

# Include secrets from multiple namespaces
[[sources.vault.include]]
resource_ids = ["admin/kv/*", "dev/secrets/*", "prod/database/*"]

# Exclude test secrets from all namespaces
[[sources.vault.exclude]]
resource_ids = ["*/test/*", "*/temp/*"]

The filtering works seamlessly with the namespace structure, where the resource_id format follows: namespace/mount/path

Configuration

To configure GGScout to work with HashiCorp Vault, add the following configuration to your ggscout.toml file:

Token-based Authentication

[sources.vault]
type = "hashicorpvault"
vault_address = "${VAULT_ADDR}"
fetch_all_versions = true
mode = "read"
env = "production"
owner = "devops-team@example.com"

auth.auth_mode = "token"
auth.token = "${VAULT_TOKEN}"

# Optional: Limit to specific namespaces (Vault Enterprise and HCD only)
namespaces = ["admin", "prod", "shared"]

# Standard filtering (works on all editions including Vault Community Edition)
[[sources.vault.include]]
resource_ids = ["app/*", "database/*", "api-key"]

# Namespace-aware filtering (Vault Enterprise and HCD only)
[[sources.vault.include]]
resource_ids = ["admin/kv/my_app/*", "prod/secrets/database/*"]

[[sources.vault.exclude]]
resource_ids = ["*/test/*", "*/temp/*", "dev/old-secret"]

HashiCorp Cloud Dedicated Configuration

For HashiCorp Cloud Dedicated environments, the configuration is identical, but the filtering automatically works with namespaces:

[sources.vault_hcd]
type = "hashicorpvault"
vault_address = "${HCD_VAULT_ADDR}"
fetch_all_versions = true
mode = "read"
env = "production"
owner = "devops-team@example.com"

auth.auth_mode = "token"
auth.token = "${HCD_VAULT_TOKEN}"

# Optional: Limit to specific namespaces
namespaces = ["admin", "prod", "shared"]

# Namespace-specific filtering
[[sources.vault_hcd.include]]
resource_ids = [
"admin/kv/my_app/*", # All secrets in admin namespace, kv mount, my_app path
"prod/database/credentials/*", # Database credentials in prod namespace
"shared/api-keys/*" # Shared API keys across teams
]

[[sources.vault_hcd.exclude]]
resource_ids = [
"*/test/*", # Exclude test secrets from all namespaces
"dev/temp/*", # Exclude temporary secrets in dev namespace
"*/legacy/*" # Exclude legacy secrets from all namespaces
]

Kubernetes Authentication

[sources.vault]
type = "hashicorpvault"
vault_address = "${VAULT_ADDR}"
fetch_all_versions = true
mode = "read"
env = "production"
owner = "devops-team@example.com"

auth.auth_mode = "k8s"
auth.k8s.service_account = "${KUBERNETES_SERVICE_ACCOUNT}"
auth.k8s.namespace = "${KUBERNETES_NAMESPACE}"
auth.k8s.role = "${KUBERNETES_ROLE}"

[[sources.vault.include]]
resource_ids = ["app/*", "database/*", "api-key"]

[[sources.vault.exclude]]
resource_ids = ["test/*", "temp/*", "old-secret"]

Configuration Parameters

ParameterDescriptionRequiredDefault Value
typeMust be set to "hashicorpvault"Yes
vault_addressThe address of your Vault serverYes
fetch_all_versionsWhether to collect all versions of secretsYes
namespacesList of Vault namespaces to fetch from (Vault Enterprise and HCD only). If not specified, fetches from all accessible namespacesNoAll accessible namespaces
mount_typeSecret engine type of the pinned mount(s): "kv-v1" or "kv-v2". When set, ggscout uses the declared type and skips sys/* mount-detection. Only applies when include/path pins the mount(s).NoAuto-detected
auth.auth_modeAuthentication mode (e.g., "token", "k8s")Yes
modeIntegration mode (one of: "read", "write", "read/write")No"read"
envEnvironment label for categorizing secrets (e.g., "production", "staging", "development")No
ownerOwner of this source (an email, usually of an employee or a team)No
[[sources.<name>.include]]Table of resource_id patterns to include (see below)No
[[sources.<name>.exclude]]Table of resource_id patterns to exclude (see below)No

With additional parameters depending on the chosen authentication mode:

For Token-based Authentication:

ParameterDescriptionRequiredDefault Value
auth.tokenThe Vault authentication tokenYes

For Kubernetes Authentication:

ParameterDescriptionRequiredDefault Value
auth.k8s.service_accountThe Kubernetes service accountYes
auth.k8s.namespaceThe Kubernetes namespaceYes
auth.k8s.roleThe Kubernetes roleYes

Environment Variables

  • VAULT_ADDR: The address of your Vault server (e.g., http://localhost:8200)

For Token-based Authentication:

  • VAULT_TOKEN: Your Vault authentication token

For Kubernetes Authentication:

  • KUBERNETES_SERVICE_ACCOUNT: The Kubernetes service account
  • KUBERNETES_NAMESPACE: The Kubernetes namespace
  • KUBERNETES_ROLE: The Kubernetes role

Skipping mount detection with mount_type

ggscout auto-detects each mount's KV engine version, so this is optional — see Required Vault Policies below for why no sys/* access is needed. Declaring the type with mount_type skips detection entirely, which helps when your token is denied sys/* or when you want to avoid extra calls on every scheduled run:

[sources.vault]
type = "hashicorpvault"
vault_address = "${VAULT_ADDR}"
mount_type = "kv-v1" # or "kv-v2"

auth.auth_mode = "token"
auth.token = "${VAULT_TOKEN}"

# mount_type only applies when an include pattern (or the deprecated `path`) pins the mount:
[[sources.vault.include]]
resource_ids = ["secret/app/*"]
info

mount_type only takes effect when a mount is pinned by an include pattern (or the deprecated path). If no mount is pinned, ggscout falls back to auto-detection. The value applies to every mount the filters pin, so use it when those mounts share the same engine version.

Required Vault Policies

GGScout needs permission to read the secrets you want to inventory. It detects the KV engine type and version automatically (using the same per-path lookup as the vault kv CLI), so granting sys/mounts is not required.

How broad the policy needs to be depends on whether you collect a whole mount or scope to a specific path. For write modes (mode = "write" or "read/write"), also grant create and update on the data paths.

Collect a whole mount

The simplest option — grant access across the mount:

# KV2
path "secret/metadata/*" { capabilities = ["read", "list"] }
path "secret/data/*" { capabilities = ["read"] }

# KV1
path "secret/*" { capabilities = ["read", "list"] }

Scope to a specific path (least privilege)

When you set a path (or an include filter), grant access only to that path. GGScout collects it whether the path is a folder of secrets or a single secret — no access to the rest of the mount (or to sys/mounts) is needed.

A folder — every secret under a path, e.g. path = "secret/app/team":

# KV2
path "secret/metadata/app/team" { capabilities = ["list"] }
path "secret/metadata/app/team/*" { capabilities = ["read", "list"] }
path "secret/data/app/team/*" { capabilities = ["read"] }

A single secret, e.g. path = "secret/app/db-creds":

# KV2
path "secret/metadata/app/db-creds" { capabilities = ["read"] }
path "secret/data/app/db-creds" { capabilities = ["read"] }

For KV1, drop the data/ and metadata/ segments (e.g. path "secret/app/team/*").

For HashiCorp Cloud Dedicated & Enterprise (with Namespaces)

When using HCD or Vault Enterprise with namespaces, policies are scoped to the namespace where they are created. Each namespace has its own policy space, and you create policies within specific namespaces rather than using wildcards to match across namespaces.

For namespace-specific policies, you would create policies within each namespace:

# Policy created within the 'admin' namespace for KV2 secrets
path "kv/data/*" {
capabilities = ["read", "list"]
}

path "kv/metadata/*" {
capabilities = ["read", "list"]
}

# Policy for specific paths within a namespace using the + wildcard
path "kv/data/my_app/+/config" {
capabilities = ["read", "list"]
}

path "kv/metadata/my_app/+/config" {
capabilities = ["read", "list"]
}
info

When working with namespaces, policies are created and managed within each namespace's scope. The + wildcard character can be used within path segments as documented in the HashiCorp Vault Policies documentation, but cross-namespace policy wildcards are not supported. For more information about namespaces, see the HashiCorp Vault Namespace documentation.

Best Practices

  1. Use environment variables for sensitive values like auth.token
  2. Leverage namespace filtering in Vault Enterprise and HCD environments to precisely control which secrets are collected
  3. Consider using path restrictions to limit the scope of secret collection
  4. Enable fetch_all_versions to track changes in your secrets over time
  5. Use a dedicated service account with minimal required permissions
  6. For HCD environments, take advantage of the automatic namespace inclusion in resource_ids for fine-grained filtering
  7. Use wildcard patterns like */test/* to exclude test secrets across all namespaces