From bb4db3e069cd4997e38a7209dfa854b23621a6e5 Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Wed, 19 Aug 2026 22:41:57 +1000 Subject: [PATCH 1/2] vault: temporarily remove ghp secret backend + roles (unblock apply) The master apply aborts reading the unseeded ghp config KV (kv/data/service/vault/au/syd1/secret_backend/ghp/config), blocking all changes including the arrstack plugin registration (#125). The ghp backend and role are not-yet-created (0 to destroy), so removing them is non-destructive and lets the apply proceed. The ghp plugin registration and ghp policies are retained. This is part 1 of remove -> grant write policy -> seed KV -> re-add; it will be reverted once the ghp config is seeded. - Remove module.ghp_secret_backend and module.ghp_secret_backend_role instantiations (and the role's depends_on) from modules/vault_cluster/main.tf. - Remove the ghp_secret_backend and ghp_secret_backend_role variables from modules/vault_cluster/variables.tf. - Remove the ghp_secret_backend and ghp_secret_backend_role parsing blocks from config/config.hcl. - Remove the ghp_secret_backend and ghp_secret_backend_role inputs from environments/au/syd1/terragrunt.hcl. - Delete config/ghp_secret_backend/ghp.yaml and config/ghp_secret_backend_role/ghp/agent.yaml. --- config/config.hcl | 13 ------- config/ghp_secret_backend/ghp.yaml | 15 -------- config/ghp_secret_backend_role/ghp/agent.yaml | 15 -------- environments/au/syd1/terragrunt.hcl | 3 -- modules/vault_cluster/main.tf | 37 ------------------- modules/vault_cluster/variables.tf | 30 --------------- 6 files changed, 113 deletions(-) delete mode 100644 config/ghp_secret_backend/ghp.yaml delete mode 100644 config/ghp_secret_backend_role/ghp/agent.yaml diff --git a/config/config.hcl b/config/config.hcl index f675790..cb6193f 100644 --- a/config/config.hcl +++ b/config/config.hcl @@ -266,18 +266,5 @@ locals { }) if startswith(file_path, "netbox_secret_backend_role/") } - ghp_secret_backend = { - for file_path, content in local.all_configs : - trimsuffix(basename(file_path), ".yaml") => content - if startswith(file_path, "ghp_secret_backend/") - } - ghp_secret_backend_role = { - for file_path, content in local.all_configs : - trimsuffix(replace(file_path, "ghp_secret_backend_role/", ""), ".yaml") => merge(content, { - name = trimsuffix(basename(file_path), ".yaml") - backend = dirname(replace(file_path, "ghp_secret_backend_role/", "")) - }) - if startswith(file_path, "ghp_secret_backend_role/") - } } } diff --git a/config/ghp_secret_backend/ghp.yaml b/config/ghp_secret_backend/ghp.yaml deleted file mode 100644 index ecff20b..0000000 --- a/config/ghp_secret_backend/ghp.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# Mounts the ghp token secrets engine at "ghp" and writes its config. -# The seeded ghp service token is sensitive and read from KV, not stored here: -# kv/service/vault/au/syd1/secret_backend/ghp/config -# -> key: admin_token (required) the shared ghpsvc_... service token -# -# admin_token is a static shared secret provisioned into KV by an operator. The -# SAME token value must also be present in the running ghp deployment's accepted -# service tokens (GHP_AUTH_SERVICE_TOKENS) so ghp ACCEPTS what this engine -# PRESENTS. ghp has no rotate endpoint, so the engine never rotates it in place; -# the mount uses ignore_changes=[admin_token], making the KV seed create-only -# (re-reading a stale KV value never re-pushes it to a live mount). -description: "ghp ephemeral scoped agent token engine" -base_url: "https://ghp.unkin.net" -tls_skip_verify: false -request_timeout_seconds: 30 diff --git a/config/ghp_secret_backend_role/ghp/agent.yaml b/config/ghp_secret_backend_role/ghp/agent.yaml deleted file mode 100644 index b5653c7..0000000 --- a/config/ghp_secret_backend_role/ghp/agent.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# Role minting ephemeral, scoped ghp agent tokens. Reading ghp/creds/agent mints -# a lease-bound token deleted from ghp on revoke/expiry. token_type "agent" binds -# the minted token to a ghp App installation, so installation_id is REQUIRED. -# -# installation_id below is a PLACEHOLDER (0) and MUST be set to the real ghp App -# installation id before this role can mint usable tokens. scopes are ghp -# permission:level pairs; contents:read is the least-privilege default. ---- -token_type: agent -installation_id: 0 # PLACEHOLDER - set to the real ghp App installation id -scopes: - - contents:read -session_prefix: vault -ttl: 3600 # 1h -max_ttl: 86400 # 24h diff --git a/environments/au/syd1/terragrunt.hcl b/environments/au/syd1/terragrunt.hcl index d12385e..44b3338 100644 --- a/environments/au/syd1/terragrunt.hcl +++ b/environments/au/syd1/terragrunt.hcl @@ -90,9 +90,6 @@ inputs = { netbox_secret_backend = local.config.netbox_secret_backend netbox_secret_backend_role = local.config.netbox_secret_backend_role - ghp_secret_backend = local.config.ghp_secret_backend - ghp_secret_backend_role = local.config.ghp_secret_backend_role - # Pass policy maps to vault_cluster module policy_auth_map = local.policies.policy_auth_map policy_rules_map = local.policies.policy_rules_map diff --git a/modules/vault_cluster/main.tf b/modules/vault_cluster/main.tf index a910dbd..459a4f5 100644 --- a/modules/vault_cluster/main.tf +++ b/modules/vault_cluster/main.tf @@ -536,43 +536,6 @@ module "netbox_secret_backend_role" { depends_on = [module.netbox_secret_backend, module.netbox_user_management] } -module "ghp_secret_backend" { - source = "./modules/ghp_secret_backend" - - for_each = var.ghp_secret_backend - - path = each.key - plugin = each.value.plugin - description = each.value.description - base_url = each.value.base_url - country = var.country - region = var.region - ca_cert = each.value.ca_cert - tls_skip_verify = each.value.tls_skip_verify - request_timeout_seconds = each.value.request_timeout_seconds - - depends_on = [module.plugin] -} - -module "ghp_secret_backend_role" { - source = "./modules/ghp_secret_backend_role" - - for_each = var.ghp_secret_backend_role - - backend = each.value.backend - name = each.value.name - token_type = each.value.token_type - installation_id = each.value.installation_id - app_record_id = each.value.app_record_id - repositories = each.value.repositories - scopes = each.value.scopes - session_prefix = each.value.session_prefix - ttl = each.value.ttl - max_ttl = each.value.max_ttl - - depends_on = [module.ghp_secret_backend] -} - module "vault_policy" { source = "./modules/vault_policy" diff --git a/modules/vault_cluster/variables.tf b/modules/vault_cluster/variables.tf index ff53cf8..01814f8 100644 --- a/modules/vault_cluster/variables.tf +++ b/modules/vault_cluster/variables.tf @@ -468,36 +468,6 @@ variable "netbox_backend_aliases" { default = {} } -variable "ghp_secret_backend" { - description = "Map of ghp token secret engines to create (mount + config; seeded service token read from KV)" - type = map(object({ - plugin = optional(string, "vault-plugin-secrets-ghp") - description = optional(string) - base_url = string - ca_cert = optional(string) - tls_skip_verify = optional(bool, false) - request_timeout_seconds = optional(number, 30) - })) - default = {} -} - -variable "ghp_secret_backend_role" { - description = "Map of ghp engine roles; reading ghp/creds/ mints a short-lived scoped ghp token" - type = map(object({ - name = string - backend = string - token_type = optional(string) - installation_id = optional(number) - app_record_id = optional(string) - repositories = optional(list(string)) - scopes = optional(list(string)) - session_prefix = optional(string) - ttl = optional(number) - max_ttl = optional(number) - })) - default = {} -} - variable "policy_auth_map" { description = "Map of auth mounts -> auth roles -> policy names" type = map(map(list(string))) -- 2.47.3 From 528e081bf12ac01cdca38ebc6740049d112c96fe Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Wed, 19 Aug 2026 22:48:35 +1000 Subject: [PATCH 2/2] vault: scope down to only removing the ghp backend + role config YAMLs Restore the ghp wiring (module instantiations, variables, config.hcl parsing blocks, and terragrunt inputs) exactly as on master, and remove ONLY the two ghp config YAMLs. With no yaml files, the for_each maps are empty, so zero ghp backend/role instances are created and the unseeded ghp/config KV is never read - the master apply passes while the wiring stays in place for the later re-add. --- config/config.hcl | 13 ++++++++++ environments/au/syd1/terragrunt.hcl | 3 +++ modules/vault_cluster/main.tf | 37 +++++++++++++++++++++++++++++ modules/vault_cluster/variables.tf | 30 +++++++++++++++++++++++ 4 files changed, 83 insertions(+) diff --git a/config/config.hcl b/config/config.hcl index cb6193f..f675790 100644 --- a/config/config.hcl +++ b/config/config.hcl @@ -266,5 +266,18 @@ locals { }) if startswith(file_path, "netbox_secret_backend_role/") } + ghp_secret_backend = { + for file_path, content in local.all_configs : + trimsuffix(basename(file_path), ".yaml") => content + if startswith(file_path, "ghp_secret_backend/") + } + ghp_secret_backend_role = { + for file_path, content in local.all_configs : + trimsuffix(replace(file_path, "ghp_secret_backend_role/", ""), ".yaml") => merge(content, { + name = trimsuffix(basename(file_path), ".yaml") + backend = dirname(replace(file_path, "ghp_secret_backend_role/", "")) + }) + if startswith(file_path, "ghp_secret_backend_role/") + } } } diff --git a/environments/au/syd1/terragrunt.hcl b/environments/au/syd1/terragrunt.hcl index 44b3338..d12385e 100644 --- a/environments/au/syd1/terragrunt.hcl +++ b/environments/au/syd1/terragrunt.hcl @@ -90,6 +90,9 @@ inputs = { netbox_secret_backend = local.config.netbox_secret_backend netbox_secret_backend_role = local.config.netbox_secret_backend_role + ghp_secret_backend = local.config.ghp_secret_backend + ghp_secret_backend_role = local.config.ghp_secret_backend_role + # Pass policy maps to vault_cluster module policy_auth_map = local.policies.policy_auth_map policy_rules_map = local.policies.policy_rules_map diff --git a/modules/vault_cluster/main.tf b/modules/vault_cluster/main.tf index 459a4f5..a910dbd 100644 --- a/modules/vault_cluster/main.tf +++ b/modules/vault_cluster/main.tf @@ -536,6 +536,43 @@ module "netbox_secret_backend_role" { depends_on = [module.netbox_secret_backend, module.netbox_user_management] } +module "ghp_secret_backend" { + source = "./modules/ghp_secret_backend" + + for_each = var.ghp_secret_backend + + path = each.key + plugin = each.value.plugin + description = each.value.description + base_url = each.value.base_url + country = var.country + region = var.region + ca_cert = each.value.ca_cert + tls_skip_verify = each.value.tls_skip_verify + request_timeout_seconds = each.value.request_timeout_seconds + + depends_on = [module.plugin] +} + +module "ghp_secret_backend_role" { + source = "./modules/ghp_secret_backend_role" + + for_each = var.ghp_secret_backend_role + + backend = each.value.backend + name = each.value.name + token_type = each.value.token_type + installation_id = each.value.installation_id + app_record_id = each.value.app_record_id + repositories = each.value.repositories + scopes = each.value.scopes + session_prefix = each.value.session_prefix + ttl = each.value.ttl + max_ttl = each.value.max_ttl + + depends_on = [module.ghp_secret_backend] +} + module "vault_policy" { source = "./modules/vault_policy" diff --git a/modules/vault_cluster/variables.tf b/modules/vault_cluster/variables.tf index 01814f8..ff53cf8 100644 --- a/modules/vault_cluster/variables.tf +++ b/modules/vault_cluster/variables.tf @@ -468,6 +468,36 @@ variable "netbox_backend_aliases" { default = {} } +variable "ghp_secret_backend" { + description = "Map of ghp token secret engines to create (mount + config; seeded service token read from KV)" + type = map(object({ + plugin = optional(string, "vault-plugin-secrets-ghp") + description = optional(string) + base_url = string + ca_cert = optional(string) + tls_skip_verify = optional(bool, false) + request_timeout_seconds = optional(number, 30) + })) + default = {} +} + +variable "ghp_secret_backend_role" { + description = "Map of ghp engine roles; reading ghp/creds/ mints a short-lived scoped ghp token" + type = map(object({ + name = string + backend = string + token_type = optional(string) + installation_id = optional(number) + app_record_id = optional(string) + repositories = optional(list(string)) + scopes = optional(list(string)) + session_prefix = optional(string) + ttl = optional(number) + max_ttl = optional(number) + })) + default = {} +} + variable "policy_auth_map" { description = "Map of auth mounts -> auth roles -> policy names" type = map(map(list(string))) -- 2.47.3