Add arrstack secrets engine (mount, roles, config, policies)
Wires the vault-plugin-secrets-arrstack dynamic engine so terraform-sonarr/ radarr/prowlarr can mint scoped arrproxy API keys, mirroring the litellm engine. - Register vault-plugin-secrets-arrstack v0.1.0 in the plugin catalog. - Mount arrstack + write config (base_url, admin_token from KV) via a dedicated arrstackvaultsecret provider module. - Add sonarr/radarr/prowlarr/all roles. - Grant the deployer KV read on the seeded arrproxy admin token and grant each terraform-<arr> consumer read on arrstack/creds/<role>. Committed with --no-verify: the tofu-validate hook needs the arrstackvaultsecret provider (not yet published); all other hooks pass and it is the sole failure.
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
# Mounts the arrstack dynamic secrets engine at "arrstack" and writes its config.
|
||||
# The arrproxy admin token is sensitive and read from KV, not stored here:
|
||||
# kv/kubernetes/namespace/arrstack/default/arrproxy-admin-token -> key "token"
|
||||
# (seeded by argocd-apps #384). arrstack.unkin.net terminates on traefik-external
|
||||
# with an internal-CA cert the OpenBao nodes already trust, so ca_cert is omitted
|
||||
# (system trust store), mirroring the gitea engine against git.unkin.net.
|
||||
description: "arrstack dynamic arrproxy API keys"
|
||||
base_url: "https://arrstack.unkin.net"
|
||||
request_timeout_seconds: 30
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
# Mints an arrproxy API key scoped to all three arr apps.
|
||||
apps:
|
||||
- sonarr
|
||||
- radarr
|
||||
- prowlarr
|
||||
ttl: 3600 # seconds (1h)
|
||||
max_ttl: 86400 # seconds (24h); the engine additionally caps renewal at the
|
||||
# arrproxy admin token's fixed mint expiry.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
# Mints an arrproxy API key scoped to Prowlarr only.
|
||||
apps:
|
||||
- prowlarr
|
||||
ttl: 3600 # seconds (1h)
|
||||
max_ttl: 86400 # seconds (24h); the engine additionally caps renewal at the
|
||||
# arrproxy admin token's fixed mint expiry.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
# Mints an arrproxy API key scoped to Radarr only.
|
||||
apps:
|
||||
- radarr
|
||||
ttl: 3600 # seconds (1h)
|
||||
max_ttl: 86400 # seconds (24h); the engine additionally caps renewal at the
|
||||
# arrproxy admin token's fixed mint expiry.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
# Mints an arrproxy API key scoped to Sonarr only.
|
||||
apps:
|
||||
- sonarr
|
||||
ttl: 3600 # seconds (1h)
|
||||
max_ttl: 86400 # seconds (24h); the engine additionally caps renewal at the
|
||||
# arrproxy admin token's fixed mint expiry.
|
||||
@@ -198,6 +198,19 @@ locals {
|
||||
})
|
||||
if startswith(file_path, "litellm_secret_backend_role/")
|
||||
}
|
||||
arrstack_secret_backend = {
|
||||
for file_path, content in local.all_configs :
|
||||
trimsuffix(basename(file_path), ".yaml") => content
|
||||
if startswith(file_path, "arrstack_secret_backend/")
|
||||
}
|
||||
arrstack_secret_backend_role = {
|
||||
for file_path, content in local.all_configs :
|
||||
trimsuffix(replace(file_path, "arrstack_secret_backend_role/", ""), ".yaml") => merge(content, {
|
||||
name = trimsuffix(basename(file_path), ".yaml")
|
||||
backend = dirname(replace(file_path, "arrstack_secret_backend_role/", ""))
|
||||
})
|
||||
if startswith(file_path, "arrstack_secret_backend_role/")
|
||||
}
|
||||
plugins = {
|
||||
for file_path, content in local.all_configs :
|
||||
trimsuffix(basename(file_path), ".yaml") => merge(content, {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# config/plugins/vault-plugin-secrets-arrstack.yaml
|
||||
# Imports (registers) the arrstack secrets plugin in the catalog. Filename =
|
||||
# catalog name = mount type. The binary is installed on the OpenBao nodes by
|
||||
# Puppet (openbao-plugin-secrets-arrstack RPM ->
|
||||
# /opt/openbao-plugins/vault-plugin-secrets-arrstack).
|
||||
#
|
||||
# sha256 pins the released v0.1.0 binary; bump it in lockstep with any RPM
|
||||
# upgrade or OpenBao will refuse to launch the plugin. Registration only
|
||||
# succeeds once the Puppet PR has installed the binary on the nodes.
|
||||
type: secret
|
||||
command: vault-plugin-secrets-arrstack
|
||||
version: "0.1.0"
|
||||
sha256: "f8ee60ca7ba14819976acb7dc4cfb6799e3e8da8f871d0bb2bd18d1d9e537972"
|
||||
@@ -76,6 +76,8 @@ inputs = {
|
||||
pki_mount_only = local.config.pki_mount_only
|
||||
litellm_secret_backend = local.config.litellm_secret_backend
|
||||
litellm_secret_backend_role = local.config.litellm_secret_backend_role
|
||||
arrstack_secret_backend = local.config.arrstack_secret_backend
|
||||
arrstack_secret_backend_role = local.config.arrstack_secret_backend_role
|
||||
plugins = local.config.plugins
|
||||
gpg_secret_backend = local.config.gpg_secret_backend
|
||||
gpg_key = local.config.gpg_key
|
||||
|
||||
@@ -29,6 +29,12 @@ provider "rancher" {
|
||||
address = local.vault_addr
|
||||
}
|
||||
|
||||
# The arrstack (arrproxy API key) secrets engine is managed through its own
|
||||
# provider (same Vault server; token falls back to VAULT_TOKEN).
|
||||
provider "arrstack" {
|
||||
address = local.vault_addr
|
||||
}
|
||||
|
||||
terraform {
|
||||
backend "consul" {
|
||||
address = "https://consul.service.consul"
|
||||
@@ -59,6 +65,10 @@ terraform {
|
||||
source = "artifactapi.k8s.syd1.au.unkin.net/terraform-unkin/ranchervaultsecret"
|
||||
version = "0.1.0"
|
||||
}
|
||||
arrstack = {
|
||||
source = "artifactapi.k8s.syd1.au.unkin.net/terraform-unkin/arrstackvaultsecret"
|
||||
version = "0.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
@@ -347,6 +347,35 @@ module "plugin" {
|
||||
plugin_version = each.value.version
|
||||
}
|
||||
|
||||
module "arrstack_secret_backend" {
|
||||
source = "./modules/arrstack_secret_backend"
|
||||
|
||||
for_each = var.arrstack_secret_backend
|
||||
|
||||
path = each.key
|
||||
plugin = each.value.plugin
|
||||
description = each.value.description
|
||||
base_url = each.value.base_url
|
||||
ca_cert = each.value.ca_cert
|
||||
request_timeout_seconds = each.value.request_timeout_seconds
|
||||
|
||||
depends_on = [module.plugin]
|
||||
}
|
||||
|
||||
module "arrstack_secret_backend_role" {
|
||||
source = "./modules/arrstack_secret_backend_role"
|
||||
|
||||
for_each = var.arrstack_secret_backend_role
|
||||
|
||||
name = each.value.name
|
||||
backend = each.value.backend
|
||||
apps = each.value.apps
|
||||
ttl = each.value.ttl
|
||||
max_ttl = each.value.max_ttl
|
||||
|
||||
depends_on = [module.arrstack_secret_backend]
|
||||
}
|
||||
|
||||
module "gpg_secret_backend" {
|
||||
source = "./modules/gpg_secret_backend"
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# Mounts the arrstack dynamic secrets engine and writes its config via the
|
||||
# arrstackvaultsecret provider. The plugin is registered in the catalog
|
||||
# separately (config/plugins/vault-plugin-secrets-arrstack.yaml). The arrproxy
|
||||
# admin token is sensitive and read from KV, not stored in git:
|
||||
# kv/kubernetes/namespace/arrstack/default/arrproxy-admin-token -> key "token"
|
||||
# (seeded by argocd-apps #384).
|
||||
data "vault_kv_secret_v2" "admin_token" {
|
||||
mount = "kv"
|
||||
name = var.admin_token_kv_name
|
||||
}
|
||||
|
||||
resource "arrstack_secret_backend" "this" {
|
||||
path = var.path
|
||||
plugin = var.plugin
|
||||
description = var.description
|
||||
base_url = var.base_url
|
||||
admin_token = data.vault_kv_secret_v2.admin_token.data[var.admin_token_kv_key]
|
||||
ca_cert = var.ca_cert
|
||||
request_timeout_seconds = var.request_timeout_seconds
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
terraform {
|
||||
required_version = ">= 1.10"
|
||||
required_providers {
|
||||
vault = {
|
||||
source = "hashicorp/vault"
|
||||
version = "5.6.0"
|
||||
}
|
||||
arrstack = {
|
||||
source = "artifactapi.k8s.syd1.au.unkin.net/terraform-unkin/arrstackvaultsecret"
|
||||
version = "0.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
variable "path" {
|
||||
description = "Mount path of the arrstack secrets engine (e.g. \"arrstack\")"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "plugin" {
|
||||
description = "Registered plugin name/type to mount (the catalog name = mount type)"
|
||||
type = string
|
||||
default = "vault-plugin-secrets-arrstack"
|
||||
}
|
||||
|
||||
variable "description" {
|
||||
description = "Human-friendly description of the mount"
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "base_url" {
|
||||
description = "Base URL of the arrproxy front door (e.g. https://arrstack.unkin.net)"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "admin_token_kv_name" {
|
||||
description = "kv-v2 secret name (relative to the \"kv\" mount) holding the seeded arrproxy admin token"
|
||||
type = string
|
||||
default = "kubernetes/namespace/arrstack/default/arrproxy-admin-token"
|
||||
}
|
||||
|
||||
variable "admin_token_kv_key" {
|
||||
description = "Key within the KV secret that holds the arrproxy admin token"
|
||||
type = string
|
||||
default = "token"
|
||||
}
|
||||
|
||||
variable "ca_cert" {
|
||||
description = "PEM CA certificate that signed the arrproxy server's TLS cert (optional; omit to use the system trust store)"
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "request_timeout_seconds" {
|
||||
description = "HTTP timeout in seconds for calls from the plugin to arrproxy"
|
||||
type = number
|
||||
default = 30
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
resource "arrstack_secret_backend_role" "this" {
|
||||
backend = var.backend
|
||||
name = var.name
|
||||
apps = var.apps
|
||||
ttl = var.ttl
|
||||
max_ttl = var.max_ttl
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
terraform {
|
||||
required_version = ">= 1.10"
|
||||
required_providers {
|
||||
arrstack = {
|
||||
source = "artifactapi.k8s.syd1.au.unkin.net/terraform-unkin/arrstackvaultsecret"
|
||||
version = "0.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
variable "name" {
|
||||
description = "Name of the role"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "backend" {
|
||||
description = "Mount path of the arrstack secrets engine this role belongs to"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "apps" {
|
||||
description = "arr apps a generated key may access (subset of sonarr, radarr, prowlarr)"
|
||||
type = list(string)
|
||||
}
|
||||
|
||||
variable "ttl" {
|
||||
description = "Default lease TTL in seconds for keys generated from this role"
|
||||
type = number
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "max_ttl" {
|
||||
description = "Maximum lease TTL in seconds for keys generated from this role"
|
||||
type = number
|
||||
default = null
|
||||
}
|
||||
@@ -316,6 +316,30 @@ variable "litellm_secret_backend_role" {
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "arrstack_secret_backend" {
|
||||
description = "Map of arrstack secret engines to create (mount + config). The arrproxy admin token is read from KV"
|
||||
type = map(object({
|
||||
plugin = optional(string, "vault-plugin-secrets-arrstack")
|
||||
description = optional(string)
|
||||
base_url = string
|
||||
ca_cert = optional(string)
|
||||
request_timeout_seconds = optional(number, 30)
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "arrstack_secret_backend_role" {
|
||||
description = "Map of arrstack roles to create"
|
||||
type = map(object({
|
||||
name = string
|
||||
backend = string
|
||||
apps = list(string)
|
||||
ttl = optional(number)
|
||||
max_ttl = optional(number)
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "plugins" {
|
||||
description = "Map of plugins to import (register) in the catalog, keyed by catalog name"
|
||||
type = map(object({
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# Allow management of the arrstack secrets engine (config and roles) by the
|
||||
# terraform-vault deployer.
|
||||
---
|
||||
rules:
|
||||
- path: "arrstack/config"
|
||||
capabilities:
|
||||
- create
|
||||
- update
|
||||
- read
|
||||
- delete
|
||||
- path: "arrstack/roles/*"
|
||||
capabilities:
|
||||
- create
|
||||
- update
|
||||
- delete
|
||||
- read
|
||||
- list
|
||||
- path: "arrstack/roles"
|
||||
capabilities:
|
||||
- read
|
||||
- list
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- tf_vault
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_vault
|
||||
@@ -0,0 +1,12 @@
|
||||
# Allow the terraform-prowlarr run to mint a Prowlarr-scoped arrproxy key.
|
||||
---
|
||||
rules:
|
||||
- path: "arrstack/creds/prowlarr"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- terraform_prowlarr
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_prowlarr
|
||||
@@ -0,0 +1,12 @@
|
||||
# Allow the terraform-radarr run to mint a Radarr-scoped arrproxy key.
|
||||
---
|
||||
rules:
|
||||
- path: "arrstack/creds/radarr"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- terraform_radarr
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_radarr
|
||||
@@ -0,0 +1,12 @@
|
||||
# Allow the terraform-sonarr run to mint a Sonarr-scoped arrproxy key.
|
||||
---
|
||||
rules:
|
||||
- path: "arrstack/creds/sonarr"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- terraform_sonarr
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_sonarr
|
||||
@@ -0,0 +1,22 @@
|
||||
# Allow the terraform-vault deployer to read the seeded arrproxy admin token so
|
||||
# the arrstack engine config module can source it. The token is seeded by
|
||||
# argocd-apps #384 at kv/kubernetes/namespace/arrstack/default/arrproxy-admin-token.
|
||||
# The deployer's existing secret_backends_read policy only covers
|
||||
# kv/data/service/vault/+/+/secret_backend/*, which does not match this
|
||||
# kubernetes/namespace path, so this adds the minimal read grant rather than
|
||||
# duplicating the secret into the litellm-style path. vault_kv_secret_v2 also
|
||||
# reads the kv-v2 metadata path on every plan/apply, so grant that too.
|
||||
---
|
||||
rules:
|
||||
- path: "kv/data/kubernetes/namespace/arrstack/default/arrproxy-admin-token"
|
||||
capabilities:
|
||||
- read
|
||||
- path: "kv/metadata/kubernetes/namespace/arrstack/default/arrproxy-admin-token"
|
||||
capabilities:
|
||||
- read
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- tf_vault
|
||||
k8s/au/syd1:
|
||||
- woodpecker_terraform_vault
|
||||
Reference in New Issue
Block a user