Skip to main content

HashiCorp Vault

The HashiCorp Vault adapter delivers certificate chains and matching private keys into a Vault KV version 2 secrets engine. Your applications, Vault Agent templates or sidecars read the certificate from Vault instead of from a local file. The Mesh Node writes each certificate as an immutable generation, moves a check-and-set (CAS) protected pointer to it, optionally runs a pinned reload executable, and commits only when consumers serve the exact new leaf certificate. Any failure moves the pointer back.

note

This adapter writes certificates that Sectigo Edge issued into Vault. It does not use Vault's PKI secrets engine as an issuing CA.

Security model​

  • The console stores only the local/ adapter reference, profile, target node and revision. The Vault address, token and private keys never leave your environment.
  • The Vault token is read from a private local file on every request — never from node JSON, an argument, an environment variable or the console.
  • Connections require HTTPS with certificate verification (TLS 1.2 or later). An optional CA file pins Vault's issuer; an optional client certificate supports Vault TLS authentication. Proxies and redirects are refused.
  • Generations are written with cas=0, so an existing slot is never overwritten; the pointer is updated with CAS against its current version.
  • Mesh Node startup never contacts Vault. If Vault is unreachable, only this integration becomes Degraded; everything else on the node keeps working, and the node retries on every poll until it recovers.
  • Routine reconciliation checks Vault version metadata and does not read private keys.

Vault data layout​

All paths are relative to the KV mount and base path you configure.

PathContents
<base>/generations/slot-NNNNNNOne immutable generation: protocol (sectigo-edge.vault-kv-generation.v1), slot, certificate_pem (leaf then ordered chain), private_key_pem, certificate_sha256, created_at
<base>/pointerThe active selection: protocol (sectigo-edge.vault-kv-pointer.v1), generation, active, active_sha256, optional standby/standby_sha256 and previous/previous_sha256, and updated_at

Consumers should read <base>/pointer, then the generation named by active, and check that its certificate_sha256 equals active_sha256. Reading both in one Vault Agent template, or re-reading on a short interval, picks up promotions automatically.

Prepare Vault​

Enable (or reuse) a KV v2 mount and create a narrowly scoped policy for the node — create/update/read on the base path's data and read on its metadata:

path "secret/data/sectigo-edge/prod-service/*" {
capabilities = ["create", "update", "read"]
}

path "secret/metadata/sectigo-edge/prod-service/*" {
capabilities = ["read"]
}
vault-admin (bash)
vault secrets enable -path=secret kv-v2
Success! Enabled the kv-v2 secrets engine at: secret/
vault policy write sectigo-edge-prod sectigo-edge-prod.hcl
Success! Uploaded policy: sectigo-edge-prod

Skip vault secrets enable if the mount exists. Grant consumers read access to <base>/pointer and <base>/generations/* only — never write.

Prepare the node host​

Create a token bound to that policy (periodic, or renewed by your normal token process) and store only the token (optionally followed by one newline) in a dedicated file.

mesh-node-01 (bash)
vault token create -policy=sectigo-edge-prod -period=24h -field=token \
  | sudo tee /etc/sectigo-edge/vault.token >/dev/null
sudo chown edgepki:edgepki /etc/sectigo-edge/vault.token
sudo chmod 0600 /etc/sectigo-edge/vault.token
sudo install -d -o edgepki -g edgepki -m 0700 /var/lib/sectigo-edge/vault

The node rejects token and bootstrap-key files readable by group or others. The installed systemd unit only allows writes to /var/lib/edgepki; add a drop-in (sudo systemctl edit edgepki-node) with ReadWritePaths=/var/lib/sectigo-edge/vault.

Also place the currently used certificate chain (leaf first) and its key in bootstrap files; they become the first generation the first time the console assigns desired state.

Node configuration​

{
"hashicorp_vault_enabled": true,
"hashicorp_vault_adapter_reference": "local/vault-production",
"hashicorp_vault_profile_id": "prod-service",
"hashicorp_vault_address": "https://vault.example.internal:8200",
"hashicorp_vault_namespace": "",
"hashicorp_vault_token_file": "/etc/sectigo-edge/vault.token",
"hashicorp_vault_ca_certificate_file": "/etc/sectigo-edge/vault-ca.pem",
"hashicorp_vault_kv_mount": "secret",
"hashicorp_vault_kv_base_path": "sectigo-edge/prod-service",
"hashicorp_vault_managed_directory": "/var/lib/sectigo-edge/vault",
"hashicorp_vault_bootstrap_certificate_file": "/etc/sectigo-edge/bootstrap/vault-chain.pem",
"hashicorp_vault_bootstrap_private_key_file": "/etc/sectigo-edge/bootstrap/vault-key.pem",
"hashicorp_vault_request_timeout_seconds": 10,
"hashicorp_vault_rollback_window_seconds": 3600,
"hashicorp_vault_reload_binary_path": "/opt/example/bin/reload-tls",
"hashicorp_vault_reload_binary_sha256": "<64 lowercase hex characters>",
"hashicorp_vault_reload_arguments": ["--certificate", "{certificate}"],
"hashicorp_vault_reload_timeout_seconds": 15
}
KeyNotes
hashicorp_vault_addressExact HTTPS origin, no path, query, fragment or credentials
hashicorp_vault_namespaceVault Enterprise namespace; leave empty otherwise
hashicorp_vault_kv_mountA single path segment
hashicorp_vault_kv_base_pathRelative, no empty, . or .. segments, no leading/trailing /, at most 512 characters
hashicorp_vault_client_certificate_file / _private_key_fileOptional, set together, for Vault TLS auth
hashicorp_vault_request_timeout_seconds2–60
hashicorp_vault_rollback_window_seconds60–86400
hashicorp_vault_reload_*Optional. When a binary path is set: SHA-256 pin required, up to 32 fixed arguments, 2–60 s timeout. Pin, arguments and timeout are rejected without a binary path. The only substitution is {certificate}.

Use the reload executable to signal consumers that do not poll Vault (for example an application endpoint or a Vault Agent reload). It runs without a shell after the pointer moves and before health checks, and its digest is re-checked before every run.

Configure in the console​

  1. Open ConsoleIntegrations and select Configure on HashiCorp Vault KV v2.
  2. Select Enable this integration and the exact enrolled node.
  3. Enter the profile (for example prod-service) and the adapter reference (for example local/vault-production) exactly as in config.json.
  4. Select Save desired state. On this first assignment the node contacts Vault, writes the bootstrap generation and reports Healthy.
Configure HashiCorp Vault KV v2 dialog in the console
HashiCorp Vault desired state. The Vault address, token and paths stay in the node's local configuration.

Policy requirements​

  • Allow hashicorp_vault in allowed deployment modes only on profiles permitted to activate this adapter. The Sectigo platform policy must also allow it (platform policy version 3 and later).
  • Use dual-slot rotation: renewal lead time, overlap, health grace, manual or automatic_after_health promotion, and a rollback window.
  • A reference, profile, node or revision mismatch denies the transaction even after issuance. Disabling or unassigning sends an authority-withdrawal tombstone.

How rotation and rollback work​

  1. The node durably stores the signed intent, CSR, generated key, SANs, policy digest, adapter reference and revision.
  2. The chain and key are written as the next generation with cas=0. After a crash an existing generation is accepted only if its content digest matches what was intended.
  3. A write-ahead record is committed locally, the pointer is moved with CAS and the optional reload executable runs.
  4. The node retries the exact HTTPS health URL with backoff until the live leaf matches, or until the rollback window ends — giving polling consumers time to pick up the new pointer.
  5. Success commits the generation and keeps the previous slot for the rollback window; any failure restores the previous pointer.
  6. After a crash, reconciliation finishes or reverts the recorded transition: an unconfirmed promotion is reverted, an interrupted rollback is completed.

Rotation requests use POST /v1/managed/rotate with "integrationKind": "hashicorp_vault" (see NGINX → How rotation works); manual promotion uses Verify & promote in ConsoleRotation.

Verify​

vault-admin (bash)
vault kv get -mount=secret sectigo-edge/prod-service/pointer
======= Secret Path =======
secret/data/sectigo-edge/prod-service/pointer

======= Data =======
Key              Value
---              -----
active           slot-000004
active_sha256    Hk1yQ8vT2nW5zR0cJ7mB4xE9pL3sA6dF1gU8iO2kN5c
generation       4
previous         slot-000003
previous_sha256  b5Zq0kVf3sR8yW1nT6uE2hJ9cL4xA7mD0pG5iK3oB1Q
protocol         sectigo-edge.vault-kv-pointer.v1
updated_at       2026-10-01T15:20:11Z

Example output (metadata section omitted). Then confirm the card is Healthy and that sectigo-edge status shows "kind": "hashicorp_vault" with "health": "healthy". Before production, qualify against a real Vault server with your consumer, reload executable and health URL: promotion, rollback, failed health, and a node restart during promotion.

Troubleshooting​

CodeMeaningWhat to do
hashicorp_vault_not_configuredThe node has no Vault settingsAdd the keys and restart
adapter_reference_invalid / profile_not_mappedConsole and node disagreeMake them identical
vault_awaiting_desired_stateConfigured locally but not yet assigned by the consoleEnable and assign the integration
vault_unavailableVault unreachable, TLS failure or token rejectedCheck network, CA file, token validity and policy; the node retries automatically
vault_bootstrap_failedThe first generation could not be writtenCheck token permissions on the base path and the bootstrap pair
vault_state_invalidA generation was modified outside the adapter (digest or version mismatch) or local state is unreadableRepair the cause; the node never adopts content it did not write
vault_pointer_invalidThe pointer was modified outside the adapterRestore it; check that consumers have no write access
vault_recovery_pendingAn interrupted transition is unresolvedFix connectivity or permissions so recovery can complete
hashicorp_vault_integration_unavailable / hashicorp_vault_integration_binding_mismatchRotation refusedFix desired state, then retry