Skip to content

feat: fetch GPG passphrases from HashiCorp Vault - #71

Open
anfimovdm wants to merge 1 commit into
mainfrom
feat/vault-passphrases
Open

feat: fetch GPG passphrases from HashiCorp Vault#71
anfimovdm wants to merge 1 commit into
mainfrom
feat/vault-passphrases

Conversation

@anfimovdm

@anfimovdm anfimovdm commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Adds a HashiCorp Vault KV v2 provider as an alternative to Bitwarden for resolving GPG key passphrases at startup, so unattended restarts don't fall back to an interactive prompt.

Resolves: AlmaLinux/build-system#548

Implements the sign-file half of that issue. The albs-sign-node half is AlmaLinux/albs-sign-node#184.

What changed

  • sign/pgp/vault.py — KV v2 fetcher. Static token (inline or from a file), AppRole, or VAULT_ADDR/VAULT_TOKEN from the environment for hosts already running a Vault agent. hvac ships as a [vault] extra and is imported lazily, so it's only needed when the provider is enabled.
  • sign/pgp/secrets.py — provider resolver. Passphrase resolution for both Bitwarden and Vault now goes through here.
  • sign/config.py — pydantic fields, a YAML vault: section, and twelve SF_VAULT_* env overrides.
  • sign/signing/backend.py — calls the resolver instead of Bitwarden directly.
  • README and the [vault] extra in setup.py.

Secret layout

One secret per keyid at <mount>/<path_prefix>/<keyid>, passphrase in the passphrase field:

vault kv put secret/albs/sign-keys/7C3955C2A345DA89 passphrase='...'

The field name is configurable via vault.passphrase_field / SF_VAULT_PASSPHRASE_FIELD for operators with an existing layout.

Two design decisions worth reviewing

  1. Only one provider may be enabled at a time. Enabling Bitwarden and Vault together is a configuration error, not a precedence chain — a signing key silently resolved from the wrong vault is a bad failure mode. If a migration period needs both configured at once, this rule has to soften.
  2. The Vault field is named passphrase, not password. Bitwarden's wrapper hardcodes the item's password field; Vault has no such constraint, so the field name describes the value. vault.passphrase_field covers anyone who wants literal parity.

Out of scope

Storing the GPG private keys themselves in Vault, Vault Transit as a signing backend, and the albs-deploy Ansible wiring — all called out in the issue as follow-ups.

Testing

Run in the CI Docker target (sign-file-tests):

  • 39 passed — 23 new (vault_test.py, secrets_test.py), against a stubbed hvac client.
  • Verified against real Settings, not only mocks: the YAML vault: section maps through, SF_VAULT_MOUNT correctly overrides the YAML value, and enabling both providers raises Only one secret provider may be enabled at a time, but these are enabled: bitwarden, vault.
  • New files are black/isort-clean (repo pyproject.toml config) and bandit-clean. Remaining pylint notes are the deliberate lazy imports (C0415) and the local count in a function with twelve config options (R0914).

Note: the local dev config.yaml is untracked in this repo, so the documented vault: block for it isn't part of this PR — the README carries the YAML example instead.

Adds a HashiCorp Vault KV v2 provider as an alternative to Bitwarden
for resolving GPG key passphrases at startup, so unattended restarts
do not fall back to an interactive prompt.

Passphrase resolution now goes through sign/pgp/secrets.py, which
picks the enabled provider and refuses to run with more than one
enabled: signing keys should have a single unambiguous source of
truth, so that is a configuration error rather than a fallback chain.

Each keyid maps to a secret at <mount>/<path_prefix>/<keyid> holding
the passphrase in a configurable field ('passphrase' by default).
Supports a static token (inline or from a file), AppRole, and the
VAULT_ADDR/VAULT_TOKEN environment variables for hosts already
running a Vault agent. hvac ships as a [vault] extra and is imported
lazily, so it is only needed when the provider is enabled.

Configurable via the YAML 'vault' section or SF_VAULT_* env vars.

Resolves: AlmaLinux/build-system#548
@anfimovdm
anfimovdm force-pushed the feat/vault-passphrases branch from 7cc1c99 to 73fcbaa Compare August 25, 2026 15:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

albs-sign-node, albs-sign-file: support fetching GPG key passphrases from HashiCorp Vault

1 participant