gitea: add the gitea token secrets engine (mount, config, teabot roles)
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful

Why: teabot's implementer and reviewer bot users should mint ephemeral,
scoped Gitea tokens on demand instead of holding standing personal access
tokens (Gitea tokens never expire on their own). This registers and mounts
the new vault-plugin-secrets-gitea engine and declares its roles, mirroring
the rancher engine wiring.

Change:
- Register the plugin in the catalog (config/plugins/vault-plugin-secrets-gitea.yaml)
  pinned to the released v0.1.0 binary sha256.
- Add gitea_secret_backend + gitea_secret_backend_role modules and wire them
  through config.hcl, terragrunt.hcl, and vault_cluster variables/main, using
  the giteavaultsecret provider (terraform-unkin registry, v0.1.0).
- Mount the engine at gitea/ against https://git.unkin.net; seeded site-admin
  credentials are read from KV (service/vault/au/syd1/secret_backend/gitea/config).
- Add teabot-implementer (write:repository, write:issue) and teabot-reviewer
  (read:repository, write:issue) roles, ttl 1h / max_ttl 4h.

Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
This commit is contained in:
2026-07-27 19:05:08 +10:00
parent 31424ea6ff
commit a960757ab2
14 changed files with 270 additions and 0 deletions
+34
View File
@@ -421,6 +421,40 @@ module "rancher_secret_backend_role" {
depends_on = [module.rancher_secret_backend_service_account]
}
module "gitea_secret_backend" {
source = "./modules/gitea_secret_backend"
for_each = var.gitea_secret_backend
path = each.key
plugin = each.value.plugin
description = each.value.description
gitea_url = each.value.gitea_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 "gitea_secret_backend_role" {
source = "./modules/gitea_secret_backend_role"
for_each = var.gitea_secret_backend_role
backend = each.value.backend
name = each.value.name
username = each.value.username
scopes = each.value.scopes
token_name_prefix = each.value.token_name_prefix
ttl = each.value.ttl
max_ttl = each.value.max_ttl
depends_on = [module.gitea_secret_backend]
}
module "vault_policy" {
source = "./modules/vault_policy"
@@ -0,0 +1,23 @@
# Mounts the gitea secrets engine and writes its connection config via the
# giteavaultsecret provider. The plugin is registered ("imported") in the
# catalog separately (config/plugins/vault-plugin-secrets-gitea.yaml). The
# seeded site-admin credentials are sensitive and read from KV, not stored in
# git:
# kv/service/vault/<country>/<region>/secret_backend/<path>/config
# Expected keys: admin_username (required), admin_password (required).
data "vault_kv_secret_v2" "config" {
mount = "kv"
name = "service/vault/${var.country}/${var.region}/secret_backend/${var.path}/config"
}
resource "gitea_secret_backend" "this" {
path = var.path
plugin = var.plugin
description = var.description
gitea_url = var.gitea_url
admin_username = data.vault_kv_secret_v2.config.data["admin_username"]
admin_password = data.vault_kv_secret_v2.config.data["admin_password"]
ca_cert = var.ca_cert
tls_skip_verify = var.tls_skip_verify
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"
}
gitea = {
source = "artifactapi.k8s.syd1.au.unkin.net/terraform-unkin/giteavaultsecret"
version = "0.1.0"
}
}
}
@@ -0,0 +1,49 @@
variable "path" {
description = "Mount path of the gitea secrets engine (e.g. \"gitea\")"
type = string
}
variable "plugin" {
description = "Registered plugin name to mount (the catalog name = mount type)"
type = string
default = "vault-plugin-secrets-gitea"
}
variable "description" {
description = "Human-friendly description of the mount"
type = string
default = null
}
variable "gitea_url" {
description = "Base URL of the Gitea server (e.g. https://git.unkin.net)"
type = string
}
variable "country" {
description = "Country segment of the KV path holding the seeded admin credentials"
type = string
}
variable "region" {
description = "Region segment of the KV path holding the seeded admin credentials"
type = string
}
variable "ca_cert" {
description = "PEM CA certificate that signed the Gitea server's TLS cert (optional; omit to use the system trust store)"
type = string
default = null
}
variable "tls_skip_verify" {
description = "Skip TLS verification of the Gitea server (not recommended)"
type = bool
default = false
}
variable "request_timeout_seconds" {
description = "HTTP timeout in seconds for calls from the plugin to Gitea"
type = number
default = 30
}
@@ -0,0 +1,12 @@
# A role that mints short-lived, scoped gitea tokens for a target Gitea user.
# Reading gitea/creds/<name> produces a lease-bound token that is deleted from
# Gitea when the lease is revoked or reaches max_ttl.
resource "gitea_secret_backend_role" "this" {
backend = var.backend
name = var.name
username = var.username
scopes = var.scopes
token_name_prefix = var.token_name_prefix
ttl = var.ttl
max_ttl = var.max_ttl
}
@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.10"
required_providers {
gitea = {
source = "artifactapi.k8s.syd1.au.unkin.net/terraform-unkin/giteavaultsecret"
version = "0.1.0"
}
}
}
@@ -0,0 +1,37 @@
variable "backend" {
description = "Mount path of the gitea secrets engine this role belongs to"
type = string
}
variable "name" {
description = "Role name (read gitea/creds/<name> to mint a token)"
type = string
}
variable "username" {
description = "Target Gitea username the minted tokens belong to"
type = string
}
variable "scopes" {
description = "Gitea access-token scopes granted to minted tokens (write: implies read:)"
type = list(string)
}
variable "token_name_prefix" {
description = "Prefix for the generated Gitea token name (optional)"
type = string
default = null
}
variable "ttl" {
description = "Default lease TTL in seconds for minted tokens"
type = number
default = null
}
variable "max_ttl" {
description = "Maximum lease TTL in seconds for minted tokens"
type = number
default = null
}
+27
View File
@@ -388,6 +388,33 @@ variable "rancher_secret_backend_role" {
default = {}
}
variable "gitea_secret_backend" {
description = "Map of gitea token secret engines to create (mount + config; seeded admin creds read from KV)"
type = map(object({
plugin = optional(string, "vault-plugin-secrets-gitea")
description = optional(string)
gitea_url = string
ca_cert = optional(string)
tls_skip_verify = optional(bool, false)
request_timeout_seconds = optional(number, 30)
}))
default = {}
}
variable "gitea_secret_backend_role" {
description = "Map of gitea token-minting roles to create"
type = map(object({
name = string
backend = string
username = string
scopes = list(string)
token_name_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)))