4 Commits

Author SHA1 Message Date
Ben Vincent 3fe5806ff5 Import (register) the rancher plugin in the catalog
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/plan Pipeline was successful
Registers the released v0.1.0 rancher secrets plugin in the OpenBao plugin
catalog so it can be mounted. The binary is laid down on the vault nodes by
Puppet (openbao-plugin-secrets-rancher RPM).

- Add config/plugins/vault-plugin-secrets-rancher.yaml (type=secret,
  command=vault-plugin-secrets-rancher, sha256 pinned to the released binary).
2026-07-17 23:38:13 +10:00
unkinben 933de177fa Register + mount the GPG secrets engine at gpg/ (#87)
ci/woodpecker/push/apply Pipeline was successful
Complete the deploy of the [vault-plugin-secrets-gpg](https://git.unkin.net/unkin/vault-plugin-secrets-gpg) engine. Puppet ([puppet-prod #480](unkin/puppet-prod#480)) installs the `openbao-plugin-secrets-gpg` RPM onto the OpenBao nodes; this registers that binary in the plugin catalog and enables the secrets engine so `gpg/` is actually usable.

- Add a `gpg_secret_backend` module using the standard `hashicorp/vault` provider (already required at 5.6.0): `vault_plugin` (catalog register with a pinned sha256) + `vault_mount` (enable at the mount path).
- Wire it through `vault_cluster` (new `gpg_secret_backend` variable + module block) and the config discovery (`config.hcl` group + syd1 terragrunt input), mirroring `litellm_secret_backend`.
- Add `config/gpg_secret_backend/gpg.yaml` mounting at `gpg/` and pinning the released v0.1.0 binary sha256 (`0e92d740…a7b20`, extracted from the published RPM). Puppet installs the RPM floating, so this sha must be bumped in lockstep on any plugin upgrade or OpenBao rejects the binary.

Validated locally with `tofu validate` + `tofu fmt`. Granting non-root access to `gpg/*` (auth roles + policies) is a follow-up scoped to whoever consumes the engine (e.g. passv from CI).

Reviewed-on: #87
Co-authored-by: Ben Vincent <ben@unkin.net>
Co-committed-by: Ben Vincent <ben@unkin.net>
2026-07-17 23:19:38 +10:00
unkinben ce1185deba Grant vault deployer access to import + manage the gpg engine (#88)
ci/woodpecker/push/apply Pipeline was successful
## Why
Applying the gpg mount (#87) needs two grants the deployer (`tf_vault` approle / `woodpecker_terraform_vault` k8s role) doesn't have. terraform-vault **registers the plugin itself** (`vault_plugin` → `sys/plugins/catalog`, a sudo-protected path) and **manages keys** via the gpgvaultsecret provider (`gpg/keys/*`). The deployer already has `sys/mounts/*` but neither of these, so apply would 403 on the plugin registration and on `gpg/keys` writes — the same failure mode as #84.

## Changes
- Add `policies/gpg/admin.yaml` granting:
  - `create/read/update/delete/sudo` on `sys/plugins/catalog/secret/vault-plugin-secrets-gpg` — to **import** (register/deregister) the plugin.
  - full management of `gpg/keys/*` (+ `gpg/keys` list) — to **manage keys**.
  - assigned to `tf_vault` (approle) + `woodpecker_terraform_vault` (k8s/au/syd1), mirroring `policies/litellm/admin.yaml` (#84).

Should merge/apply **before** #87 so the deployer can register the plugin and create the `pass` key.

Reviewed-on: #88
Co-authored-by: Ben Vincent <ben@unkin.net>
Co-committed-by: Ben Vincent <ben@unkin.net>
2026-07-17 23:09:31 +10:00
unkinben 3d59758324 Add a plugin-import module + config/plugins for catalog registration (#89)
ci/woodpecker/push/apply Pipeline was canceled
Split plugin catalog registration out of the per-engine backend modules into its own concern (previously bundled into #87's gpg_secret_backend).

## Changes
- New generic `plugin` module (`vault_plugin`: type/name/command/sha256/plugin_version) that imports a binary into the catalog.
- New `config/plugins/` discovery group (filename = catalog name = mount type), wired through `vault_cluster` (`plugins` variable + module) and the syd1 environment.
- `config/plugins/vault-plugin-secrets-gpg.yaml` pins the released v0.1.0 binary sha256 (`0e92d740…a7b20`, from the published RPM). Puppet installs the RPM floating, so bump this in lockstep on upgrade.

Any engine now registers its plugin by dropping a file in `config/plugins/`; its `*_secret_backend` module just mounts the registered type.

Needs the deployer's plugin-catalog access (#88). Merge order: **#88 → this → #87**.

Reviewed-on: #89
Co-authored-by: Ben Vincent <ben@unkin.net>
Co-committed-by: Ben Vincent <ben@unkin.net>
2026-07-17 23:08:04 +10:00
10 changed files with 138 additions and 0 deletions
+7
View File
@@ -198,6 +198,13 @@ locals {
}) })
if startswith(file_path, "litellm_secret_backend_role/") if startswith(file_path, "litellm_secret_backend_role/")
} }
plugins = {
for file_path, content in local.all_configs :
trimsuffix(basename(file_path), ".yaml") => merge(content, {
name = trimsuffix(basename(file_path), ".yaml")
})
if startswith(file_path, "plugins/")
}
gpg_secret_backend = { gpg_secret_backend = {
for file_path, content in local.all_configs : for file_path, content in local.all_configs :
trimsuffix(basename(file_path), ".yaml") => content trimsuffix(basename(file_path), ".yaml") => content
@@ -0,0 +1,10 @@
# config/plugins/vault-plugin-secrets-gpg.yaml
# Imports (registers) the gpg secrets plugin in the catalog. Filename = catalog
# name = mount type. The binary is installed on the OpenBao nodes by Puppet
# (openbao-plugin-secrets-gpg RPM -> /opt/openbao-plugins/vault-plugin-secrets-gpg).
#
# sha256 pins the released v0.1.0 binary; bump it in lockstep with any RPM
# upgrade or OpenBao will refuse to launch the plugin.
type: secret
command: vault-plugin-secrets-gpg
sha256: "0e92d7408795688badb55789bc1604e8f1dd4d71998656c7f831991fce9a7b20"
@@ -0,0 +1,11 @@
# config/plugins/vault-plugin-secrets-rancher.yaml
# Imports (registers) the rancher secrets plugin in the catalog. Filename =
# catalog name = mount type. The binary is installed on the OpenBao nodes by
# Puppet (openbao-plugin-secrets-rancher RPM ->
# /opt/openbao-plugins/vault-plugin-secrets-rancher).
#
# sha256 pins the released v0.1.0 binary; bump it in lockstep with any RPM
# upgrade or OpenBao will refuse to launch the plugin.
type: secret
command: vault-plugin-secrets-rancher
sha256: "d2b17f45b5fa9e6549443fec3ba22f17ef575d2d811c14324187dc9904ec574a"
+1
View File
@@ -70,6 +70,7 @@ inputs = {
pki_mount_only = local.config.pki_mount_only pki_mount_only = local.config.pki_mount_only
litellm_secret_backend = local.config.litellm_secret_backend litellm_secret_backend = local.config.litellm_secret_backend
litellm_secret_backend_role = local.config.litellm_secret_backend_role litellm_secret_backend_role = local.config.litellm_secret_backend_role
plugins = local.config.plugins
gpg_secret_backend = local.config.gpg_secret_backend gpg_secret_backend = local.config.gpg_secret_backend
gpg_key = local.config.gpg_key gpg_key = local.config.gpg_key
+14
View File
@@ -334,6 +334,18 @@ module "litellm_secret_backend_role" {
depends_on = [module.litellm_secret_backend] depends_on = [module.litellm_secret_backend]
} }
module "plugin" {
source = "./modules/plugin"
for_each = var.plugins
name = each.value.name
type = each.value.type
command = each.value.command
sha256 = each.value.sha256
plugin_version = each.value.version
}
module "gpg_secret_backend" { module "gpg_secret_backend" {
source = "./modules/gpg_secret_backend" source = "./modules/gpg_secret_backend"
@@ -342,6 +354,8 @@ module "gpg_secret_backend" {
path = each.key path = each.key
plugin = each.value.plugin plugin = each.value.plugin
description = each.value.description description = each.value.description
depends_on = [module.plugin]
} }
module "gpg_key" { module "gpg_key" {
@@ -0,0 +1,12 @@
# Registers ("imports") a plugin binary in the Vault/OpenBao plugin catalog so
# it can be mounted. The binary must already exist in the server
# plugin_directory (installed out of band, e.g. by Puppet); `command` is its
# filename there. The sha256 must match the on-disk binary or the server refuses
# to launch the plugin.
resource "vault_plugin" "this" {
type = var.type
name = var.name
command = coalesce(var.command, var.name)
sha256 = var.sha256
version = var.plugin_version
}
@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.10"
required_providers {
vault = {
source = "hashicorp/vault"
version = "5.6.0"
}
}
}
@@ -0,0 +1,27 @@
variable "name" {
description = "Name to register the plugin under in the catalog (also the mount type)"
type = string
}
variable "type" {
description = "Plugin type: secret, auth or database"
type = string
default = "secret"
}
variable "command" {
description = "Plugin binary filename relative to the server plugin_directory. Defaults to the plugin name."
type = string
default = null
}
variable "sha256" {
description = "SHA-256 of the installed plugin binary; must match the on-disk binary"
type = string
}
variable "plugin_version" {
description = "Optional plugin version to register the catalog entry under"
type = string
default = null
}
+12
View File
@@ -315,6 +315,18 @@ variable "litellm_secret_backend_role" {
default = {} default = {}
} }
variable "plugins" {
description = "Map of plugins to import (register) in the catalog, keyed by catalog name"
type = map(object({
name = string
type = optional(string, "secret")
command = optional(string)
sha256 = string
version = optional(string)
}))
default = {}
}
variable "gpg_secret_backend" { variable "gpg_secret_backend" {
description = "Map of GPG/OpenPGP secret engines to mount (path => registered plugin + description). The plugin is registered separately via config/plugins." description = "Map of GPG/OpenPGP secret engines to mount (path => registered plugin + description). The plugin is registered separately via config/plugins."
type = map(object({ type = map(object({
+35
View File
@@ -0,0 +1,35 @@
# Allow the vault deployer to import the gpg plugin and manage its OpenPGP keys.
#
# terraform-vault registers the plugin itself (vault_plugin -> sys/plugins/catalog,
# a sudo-protected path) and manages keys via the gpgvaultsecret provider, so the
# deployer needs catalog access on top of the mount access it already has
# (sys/mounts/*). Without this, apply 403s on the plugin registration and on
# gpg/keys writes.
---
rules:
# Import / register (and deregister) the gpg plugin in the catalog.
- path: "sys/plugins/catalog/secret/vault-plugin-secrets-gpg"
capabilities:
- create
- read
- update
- delete
- sudo
# Manage keys (create/rotate/config/delete) in the gpg mount.
- path: "gpg/keys/*"
capabilities:
- create
- read
- update
- delete
- list
- path: "gpg/keys"
capabilities:
- read
- list
auth:
approle:
- tf_vault
k8s/au/syd1:
- woodpecker_terraform_vault