Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e7a052293 | |||
| ba5f49f358 | |||
| 92b8fdd0dd | |||
| d61e985ef2 | |||
| 0e6da5cdd3 | |||
| c0cc74927c | |||
| 31f32aba0f | |||
| 96a6a7d728 | |||
| bf9c785281 | |||
| d82580f1af | |||
| 2c27395613 | |||
| d289775e38 | |||
| 31424ea6ff | |||
| 1fa5900787 |
@@ -7,8 +7,9 @@ steps:
|
|||||||
image: git.unkin.net/unkin/almalinux9-opentofu:20260606
|
image: git.unkin.net/unkin/almalinux9-opentofu:20260606
|
||||||
environment:
|
environment:
|
||||||
VAULT_AUTH_METHOD: kubernetes
|
VAULT_AUTH_METHOD: kubernetes
|
||||||
|
VAULT_VERSION: "1.20.0"
|
||||||
commands:
|
commands:
|
||||||
- dnf install vault -y
|
- curl -fsSL -o /tmp/vault.zip "https://artifactapi.k8s.syd1.au.unkin.net/api/v1/remote/hashicorp-releases/vault/$${VAULT_VERSION}/vault_$${VAULT_VERSION}_linux_amd64.zip" && python3 -m zipfile -e /tmp/vault.zip /tmp/ && install -m0755 /tmp/vault /usr/local/bin/vault && rm -f /tmp/vault.zip /tmp/vault /tmp/LICENSE.txt
|
||||||
- make plan
|
- make plan
|
||||||
- make apply
|
- make apply
|
||||||
backend_options:
|
backend_options:
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ steps:
|
|||||||
image: git.unkin.net/unkin/almalinux9-opentofu:20260606
|
image: git.unkin.net/unkin/almalinux9-opentofu:20260606
|
||||||
environment:
|
environment:
|
||||||
VAULT_AUTH_METHOD: kubernetes
|
VAULT_AUTH_METHOD: kubernetes
|
||||||
|
VAULT_VERSION: "1.20.0"
|
||||||
commands:
|
commands:
|
||||||
- dnf install vault -y
|
- curl -fsSL -o /tmp/vault.zip "https://artifactapi.k8s.syd1.au.unkin.net/api/v1/remote/hashicorp-releases/vault/$${VAULT_VERSION}/vault_$${VAULT_VERSION}_linux_amd64.zip" && python3 -m zipfile -e /tmp/vault.zip /tmp/ && install -m0755 /tmp/vault /usr/local/bin/vault && rm -f /tmp/vault.zip /tmp/vault /tmp/LICENSE.txt
|
||||||
- make plan
|
- make plan
|
||||||
backend_options:
|
backend_options:
|
||||||
kubernetes:
|
kubernetes:
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
token_ttl: 3600
|
||||||
|
token_max_ttl: 14400
|
||||||
|
bind_secret_id: false
|
||||||
|
token_bound_cidrs:
|
||||||
|
- "10.10.12.200/32"
|
||||||
|
use_deterministic_role_id: true
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
token_ttl: 120
|
||||||
|
token_max_ttl: 120
|
||||||
|
bind_secret_id: false
|
||||||
|
token_bound_cidrs:
|
||||||
|
- "10.10.12.200/32"
|
||||||
|
- "198.18.25.102/32"
|
||||||
|
- "198.18.26.91/32"
|
||||||
|
- "198.18.27.40/32"
|
||||||
|
use_deterministic_role_id: true
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
bound_service_account_names:
|
||||||
|
- logarchiver
|
||||||
|
bound_service_account_namespaces:
|
||||||
|
- logging
|
||||||
|
token_ttl: 600
|
||||||
|
token_max_ttl: 600
|
||||||
|
audience: vault
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
bound_service_account_names:
|
||||||
|
- terraform-enc
|
||||||
|
bound_service_account_namespaces:
|
||||||
|
- woodpecker
|
||||||
|
token_ttl: 600
|
||||||
|
token_max_ttl: 600
|
||||||
|
audience: https://kubernetes.default.svc.cluster.local
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
bound_service_account_names:
|
||||||
|
- terraform-infra
|
||||||
|
bound_service_account_namespaces:
|
||||||
|
- woodpecker
|
||||||
|
token_ttl: 600
|
||||||
|
token_max_ttl: 600
|
||||||
|
audience: https://kubernetes.default.svc.cluster.local
|
||||||
@@ -239,5 +239,18 @@ locals {
|
|||||||
})
|
})
|
||||||
if startswith(file_path, "rancher_secret_backend_role/")
|
if startswith(file_path, "rancher_secret_backend_role/")
|
||||||
}
|
}
|
||||||
|
gitea_secret_backend = {
|
||||||
|
for file_path, content in local.all_configs :
|
||||||
|
trimsuffix(basename(file_path), ".yaml") => content
|
||||||
|
if startswith(file_path, "gitea_secret_backend/")
|
||||||
|
}
|
||||||
|
gitea_secret_backend_role = {
|
||||||
|
for file_path, content in local.all_configs :
|
||||||
|
trimsuffix(replace(file_path, "gitea_secret_backend_role/", ""), ".yaml") => merge(content, {
|
||||||
|
name = trimsuffix(basename(file_path), ".yaml")
|
||||||
|
backend = dirname(replace(file_path, "gitea_secret_backend_role/", ""))
|
||||||
|
})
|
||||||
|
if startswith(file_path, "gitea_secret_backend_role/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
consul_roles:
|
||||||
|
- terraform-enc
|
||||||
|
ttl: 120
|
||||||
|
max_ttl: 300
|
||||||
|
datacenters: []
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
consul_roles:
|
||||||
|
- terraform-infra
|
||||||
|
ttl: 120
|
||||||
|
max_ttl: 300
|
||||||
|
datacenters: []
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Mounts the gitea token secrets engine at "gitea" and writes its config.
|
||||||
|
# The seeded site-admin credentials are sensitive and read from KV, not stored
|
||||||
|
# here:
|
||||||
|
# kv/service/vault/au/syd1/secret_backend/gitea/config
|
||||||
|
# -> keys: admin_username (required), admin_password (required)
|
||||||
|
# Populate that KV path with a purpose-built Gitea site-admin bot (2FA disabled)
|
||||||
|
# BEFORE applying, then run `vault write -f gitea/config/rotate-root` after the
|
||||||
|
# first apply so only Vault holds the admin password.
|
||||||
|
description: "Gitea ephemeral scoped access token engine"
|
||||||
|
gitea_url: "https://git.unkin.net"
|
||||||
|
request_timeout_seconds: 30
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Role minting ephemeral tokens for the teabot-implementer bot user.
|
||||||
|
# The implementer clones/pushes code and opens pull requests, so it gets write
|
||||||
|
# on repositories (clone + push + PR create) and write on issues (PR/issue
|
||||||
|
# comments). Read is implied by write. No admin/org/user-write scopes.
|
||||||
|
# read:user is required because tea (and most API clients) validate the login
|
||||||
|
# via GET /api/v1/user, which 403s without it (verified against a minted token).
|
||||||
|
# Reading gitea/creds/teabot-implementer mints a lease-bound token deleted from
|
||||||
|
# Gitea on revoke/expiry.
|
||||||
|
---
|
||||||
|
username: teabot-implementer
|
||||||
|
scopes:
|
||||||
|
- write:repository
|
||||||
|
- write:issue
|
||||||
|
- read:user
|
||||||
|
token_name_prefix: vault-teabot-implementer
|
||||||
|
ttl: 3600 # 1h
|
||||||
|
max_ttl: 14400 # 4h
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Role minting ephemeral tokens for the teabot-reviewer bot user.
|
||||||
|
# The reviewer reads code and posts pull-request reviews/comments, so it gets
|
||||||
|
# read on repositories (fetch diffs) and write on issues (PR reviews + issue/PR
|
||||||
|
# comments). No repository-write, admin, org, or user-write scopes.
|
||||||
|
# read:user is required because tea (and most API clients) validate the login
|
||||||
|
# via GET /api/v1/user, which 403s without it (verified against a minted token).
|
||||||
|
# Reading gitea/creds/teabot-reviewer mints a lease-bound token deleted from
|
||||||
|
# Gitea on revoke/expiry.
|
||||||
|
---
|
||||||
|
username: teabot-reviewer
|
||||||
|
scopes:
|
||||||
|
- read:repository
|
||||||
|
- write:issue
|
||||||
|
- read:user
|
||||||
|
token_name_prefix: vault-teabot-reviewer
|
||||||
|
ttl: 3600 # 1h
|
||||||
|
max_ttl: 14400 # 4h
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# config/gpg_key/gpg/logarchive.yaml
|
||||||
|
# OpenPGP key in the gpg engine for the logarchiver service. The private key
|
||||||
|
# stays in Vault; logarchiver reads only the exported public key
|
||||||
|
# (gpg/keys/logarchive) to encrypt archived logs, and retrieval delegates
|
||||||
|
# decryption back to gpg/decrypt/logarchive. Key name = "logarchive", backend = "gpg".
|
||||||
|
algorithm: rsa-4096
|
||||||
|
identity: "logarchive <logarchive@unkin.net>"
|
||||||
|
exportable: false
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
allowed_kubernetes_namespaces:
|
||||||
|
- "cert-manager"
|
||||||
|
kubernetes_role_type: "Role"
|
||||||
|
extra_labels: {}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
allowed_kubernetes_namespaces:
|
||||||
|
- "dhcp-system"
|
||||||
|
kubernetes_role_type: "Role"
|
||||||
|
extra_labels: {}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
allowed_kubernetes_namespaces:
|
||||||
|
- "bind-system"
|
||||||
|
service_account_name: "agent-dns"
|
||||||
|
extra_labels: {}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
allowed_kubernetes_namespaces:
|
||||||
|
- "cephrgw-system"
|
||||||
|
kubernetes_role_type: "Role"
|
||||||
|
extra_labels: {}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# config/plugins/vault-plugin-secrets-gitea.yaml
|
||||||
|
# Imports (registers) the gitea secrets plugin in the catalog. Filename =
|
||||||
|
# catalog name = mount type. The binary is installed on the OpenBao nodes by
|
||||||
|
# Puppet (openbao-plugin-secrets-gitea RPM ->
|
||||||
|
# /opt/openbao-plugins/vault-plugin-secrets-gitea).
|
||||||
|
#
|
||||||
|
# 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-gitea
|
||||||
|
sha256: "8f67fbc216effada5fd7399888a710b62fad83be0b31761a439e7dec3d56509b"
|
||||||
@@ -78,6 +78,9 @@ inputs = {
|
|||||||
rancher_secret_backend_service_account = local.config.rancher_secret_backend_service_account
|
rancher_secret_backend_service_account = local.config.rancher_secret_backend_service_account
|
||||||
rancher_secret_backend_role = local.config.rancher_secret_backend_role
|
rancher_secret_backend_role = local.config.rancher_secret_backend_role
|
||||||
|
|
||||||
|
gitea_secret_backend = local.config.gitea_secret_backend
|
||||||
|
gitea_secret_backend_role = local.config.gitea_secret_backend_role
|
||||||
|
|
||||||
# Pass policy maps to vault_cluster module
|
# Pass policy maps to vault_cluster module
|
||||||
policy_auth_map = local.policies.policy_auth_map
|
policy_auth_map = local.policies.policy_auth_map
|
||||||
policy_rules_map = local.policies.policy_rules_map
|
policy_rules_map = local.policies.policy_rules_map
|
||||||
|
|||||||
@@ -299,6 +299,7 @@ module "kubernetes_secret_backend_role" {
|
|||||||
allowed_kubernetes_namespaces = each.value.allowed_kubernetes_namespaces
|
allowed_kubernetes_namespaces = each.value.allowed_kubernetes_namespaces
|
||||||
kubernetes_role_type = each.value.kubernetes_role_type
|
kubernetes_role_type = each.value.kubernetes_role_type
|
||||||
extra_labels = each.value.extra_labels
|
extra_labels = each.value.extra_labels
|
||||||
|
service_account_name = each.value.service_account_name
|
||||||
|
|
||||||
depends_on = [module.kubernetes_secret_backend]
|
depends_on = [module.kubernetes_secret_backend]
|
||||||
}
|
}
|
||||||
@@ -421,6 +422,40 @@ module "rancher_secret_backend_role" {
|
|||||||
depends_on = [module.rancher_secret_backend_service_account]
|
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" {
|
module "vault_policy" {
|
||||||
source = "./modules/vault_policy"
|
source = "./modules/vault_policy"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
# The KV seed is a bootstrap credential: it is consumed only when the engine
|
||||||
|
# config is first created. After creation the live admin password is rotated
|
||||||
|
# in place (vault write -f gitea/config/rotate-root) and diverges from the
|
||||||
|
# seed, so re-reading the (possibly stale) KV value must never push it back.
|
||||||
|
# Ignoring the credential attributes makes this module create-only for them.
|
||||||
|
# (The sibling rancher/litellm seed modules do not yet do this and would
|
||||||
|
# re-push their seed on a subsequent apply.)
|
||||||
|
ignore_changes = [admin_username, admin_password]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -2,6 +2,10 @@ locals {
|
|||||||
# Auto-generate role rules path: resources/secret_backend/{backend_path}/roles/{role_name}.yaml
|
# Auto-generate role rules path: resources/secret_backend/{backend_path}/roles/{role_name}.yaml
|
||||||
role_rules_file = "resources/secret_backend/${var.backend}/roles/${var.name}.yaml"
|
role_rules_file = "resources/secret_backend/${var.backend}/roles/${var.name}.yaml"
|
||||||
|
|
||||||
|
# service_account_name mode mints tokens for a pre-existing SA, so the
|
||||||
|
# generated_role_rules / kubernetes_role_type binding fields must be unset.
|
||||||
|
use_service_account = var.service_account_name != null
|
||||||
|
|
||||||
# Auto-generate extra labels based on country/region and role name
|
# Auto-generate extra labels based on country/region and role name
|
||||||
auto_labels = merge(var.extra_labels, {
|
auto_labels = merge(var.extra_labels, {
|
||||||
vault-region = "${var.country}-${var.region}"
|
vault-region = "${var.country}-${var.region}"
|
||||||
@@ -13,7 +17,8 @@ resource "vault_kubernetes_secret_backend_role" "role" {
|
|||||||
backend = var.backend
|
backend = var.backend
|
||||||
name = var.name
|
name = var.name
|
||||||
allowed_kubernetes_namespaces = var.allowed_kubernetes_namespaces
|
allowed_kubernetes_namespaces = var.allowed_kubernetes_namespaces
|
||||||
kubernetes_role_type = var.kubernetes_role_type
|
kubernetes_role_type = local.use_service_account ? null : var.kubernetes_role_type
|
||||||
generated_role_rules = file("${path.module}/../../../../../../../../${local.role_rules_file}")
|
generated_role_rules = local.use_service_account ? null : file("${path.module}/../../../../../../../../${local.role_rules_file}")
|
||||||
|
service_account_name = var.service_account_name
|
||||||
extra_labels = local.auto_labels
|
extra_labels = local.auto_labels
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,4 +34,10 @@ variable "extra_labels" {
|
|||||||
description = "Additional labels to apply to generated Kubernetes objects"
|
description = "Additional labels to apply to generated Kubernetes objects"
|
||||||
type = map(string)
|
type = map(string)
|
||||||
default = {}
|
default = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "service_account_name" {
|
||||||
|
description = "Pre-existing service account to mint tokens for. When set, RBAC comes from that SA's own bindings instead of generated_role_rules."
|
||||||
|
type = string
|
||||||
|
default = null
|
||||||
}
|
}
|
||||||
@@ -285,6 +285,7 @@ variable "kubernetes_secret_backend_role" {
|
|||||||
allowed_kubernetes_namespaces = optional(list(string), ["*"])
|
allowed_kubernetes_namespaces = optional(list(string), ["*"])
|
||||||
kubernetes_role_type = optional(string, "Role")
|
kubernetes_role_type = optional(string, "Role")
|
||||||
extra_labels = optional(map(string), {})
|
extra_labels = optional(map(string), {})
|
||||||
|
service_account_name = optional(string)
|
||||||
}))
|
}))
|
||||||
default = {}
|
default = {}
|
||||||
}
|
}
|
||||||
@@ -388,6 +389,33 @@ variable "rancher_secret_backend_role" {
|
|||||||
default = {}
|
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" {
|
variable "policy_auth_map" {
|
||||||
description = "Map of auth mounts -> auth roles -> policy names"
|
description = "Map of auth mounts -> auth roles -> policy names"
|
||||||
type = map(map(list(string)))
|
type = map(map(list(string)))
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Allow the terragrunt-enc runner to generate credentials for the
|
||||||
|
# terraform-enc role in consul (used to lock/write its terragrunt state under
|
||||||
|
# infra/terraform/enc/ on the consul backend).
|
||||||
|
---
|
||||||
|
rules:
|
||||||
|
- path: "consul_root/au/syd1/creds/terraform-enc"
|
||||||
|
capabilities:
|
||||||
|
- read
|
||||||
|
|
||||||
|
auth:
|
||||||
|
approle:
|
||||||
|
- terraform_enc
|
||||||
|
k8s/au/syd1:
|
||||||
|
- woodpecker_terraform_enc
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
rules:
|
||||||
|
- path: "consul_root/au/syd1/creds/terraform-infra"
|
||||||
|
capabilities:
|
||||||
|
- read
|
||||||
|
|
||||||
|
auth:
|
||||||
|
approle:
|
||||||
|
- terraform_infra
|
||||||
|
k8s/au/syd1:
|
||||||
|
- woodpecker_terraform_infra
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Allow the vault deployer to manage the gitea token secrets engine: its
|
||||||
|
# connection config (seeded admin credentials), in-place root rotation, and
|
||||||
|
# token-minting roles.
|
||||||
|
#
|
||||||
|
# Scoped to gitea/* only, and deliberately excludes gitea/creds/* — minting
|
||||||
|
# tokens is for consumers, not the deployer. The plugin-catalog grant needed to
|
||||||
|
# import the plugin is the shared, sudo-protected wildcard in
|
||||||
|
# policies/sys/plugins/catalog/admin.yaml (already covers this plugin), and
|
||||||
|
# mounting the engine uses the deployer's existing sys/mounts/* access, so no
|
||||||
|
# new catalog/mount grant is added here (mirrors the rancher engine).
|
||||||
|
---
|
||||||
|
rules:
|
||||||
|
# Engine connection config (Gitea URL, TLS, seeded admin username/password).
|
||||||
|
- path: "gitea/config"
|
||||||
|
capabilities:
|
||||||
|
- create
|
||||||
|
- read
|
||||||
|
- update
|
||||||
|
- delete
|
||||||
|
# In-place rotation of the seeded admin password (write-only trigger).
|
||||||
|
- path: "gitea/config/rotate-root"
|
||||||
|
capabilities:
|
||||||
|
- create
|
||||||
|
- update
|
||||||
|
# Token-minting roles.
|
||||||
|
- path: "gitea/roles/*"
|
||||||
|
capabilities:
|
||||||
|
- create
|
||||||
|
- read
|
||||||
|
- update
|
||||||
|
- delete
|
||||||
|
- list
|
||||||
|
- path: "gitea/roles"
|
||||||
|
capabilities:
|
||||||
|
- read
|
||||||
|
- list
|
||||||
|
|
||||||
|
auth:
|
||||||
|
approle:
|
||||||
|
- tf_vault
|
||||||
|
k8s/au/syd1:
|
||||||
|
- woodpecker_terraform_vault
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Allow the logarchiver service (logging namespace, SA logarchiver) to read the
|
||||||
|
# logarchive public key. A plain read on gpg/keys/logarchive returns the armored
|
||||||
|
# public_key; no decrypt/export capability is granted (decrypt stays operator-only).
|
||||||
|
---
|
||||||
|
rules:
|
||||||
|
- path: "gpg/keys/logarchive"
|
||||||
|
capabilities:
|
||||||
|
- read
|
||||||
|
|
||||||
|
auth:
|
||||||
|
k8s/au/syd1:
|
||||||
|
- logging_logarchiver
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Allow access to agent-certs Kubernetes credentials
|
||||||
|
---
|
||||||
|
rules:
|
||||||
|
- path: "kubernetes/au/syd1/creds/agent-certs"
|
||||||
|
capabilities:
|
||||||
|
- update
|
||||||
|
|
||||||
|
auth:
|
||||||
|
ldap:
|
||||||
|
- kubernetes_au_syd1_cluster_operator
|
||||||
|
approle:
|
||||||
|
- agents
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Allow access to agent-dhcp Kubernetes credentials
|
||||||
|
---
|
||||||
|
rules:
|
||||||
|
- path: "kubernetes/au/syd1/creds/agent-dhcp"
|
||||||
|
capabilities:
|
||||||
|
- update
|
||||||
|
|
||||||
|
auth:
|
||||||
|
ldap:
|
||||||
|
- kubernetes_au_syd1_cluster_operator
|
||||||
|
approle:
|
||||||
|
- agents
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Allow access to agent-dns Kubernetes credentials
|
||||||
|
---
|
||||||
|
rules:
|
||||||
|
- path: "kubernetes/au/syd1/creds/agent-dns"
|
||||||
|
capabilities:
|
||||||
|
- update
|
||||||
|
|
||||||
|
auth:
|
||||||
|
ldap:
|
||||||
|
- kubernetes_au_syd1_cluster_operator
|
||||||
|
approle:
|
||||||
|
- agents
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Allow access to agent-storage Kubernetes credentials
|
||||||
|
---
|
||||||
|
rules:
|
||||||
|
- path: "kubernetes/au/syd1/creds/agent-storage"
|
||||||
|
capabilities:
|
||||||
|
- update
|
||||||
|
|
||||||
|
auth:
|
||||||
|
ldap:
|
||||||
|
- kubernetes_au_syd1_cluster_operator
|
||||||
|
approle:
|
||||||
|
- agents
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Allow the agents AppRole to manage the kubernetes KV subtree (no delete)
|
||||||
|
---
|
||||||
|
rules:
|
||||||
|
- path: "kv/data/kubernetes/*"
|
||||||
|
capabilities:
|
||||||
|
- create
|
||||||
|
- read
|
||||||
|
- update
|
||||||
|
- list
|
||||||
|
- path: "kv/metadata/kubernetes/*"
|
||||||
|
capabilities:
|
||||||
|
- read
|
||||||
|
- list
|
||||||
|
|
||||||
|
auth:
|
||||||
|
approle:
|
||||||
|
- agents
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Allow the terragrunt-enc runner to read the encapi environment secret
|
||||||
|
# (ENCAPI_WRITE_TOKEN), so `make apply` can write ENC data (statuses, roles,
|
||||||
|
# nodes) to encapi via the encapi Terraform provider.
|
||||||
|
---
|
||||||
|
rules:
|
||||||
|
- path: "kv/data/kubernetes/namespace/encapi/default/environment"
|
||||||
|
capabilities:
|
||||||
|
- read
|
||||||
|
|
||||||
|
auth:
|
||||||
|
approle:
|
||||||
|
- terraform_enc
|
||||||
|
k8s/au/syd1:
|
||||||
|
- woodpecker_terraform_enc
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Allow the terraform-infra runner to read the NetBox + KeaAPI tokens
|
||||||
|
# (netbox_token / kea_token fields) used by the netbox and kea providers.
|
||||||
|
---
|
||||||
|
rules:
|
||||||
|
- path: "kv/data/service/terraform/infra"
|
||||||
|
capabilities:
|
||||||
|
- read
|
||||||
|
# vault_kv_secret_v2 (providers.tf data source) reads the kv-v2 metadata path
|
||||||
|
# on every plan/apply; a 403 here fails the plan.
|
||||||
|
- path: "kv/metadata/service/terraform/infra"
|
||||||
|
capabilities:
|
||||||
|
- read
|
||||||
|
|
||||||
|
auth:
|
||||||
|
approle:
|
||||||
|
- terraform_infra
|
||||||
|
k8s/au/syd1:
|
||||||
|
- woodpecker_terraform_infra
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Allow terraform-git to seed (write once) the gitea secrets engine's admin
|
||||||
|
# credentials. terraform-git creates the gitea-vault-admin site-admin bot and
|
||||||
|
# writes its generated password here as admin_username + admin_password; the
|
||||||
|
# vault gitea engine (managed by the tf_vault deployer) reads it at gitea/config
|
||||||
|
# creation time. Read is already granted to the deployer via
|
||||||
|
# policies/kv/service/vault/secret_backends_read.yaml, so this only adds the
|
||||||
|
# write side for terraform-git's own identity.
|
||||||
|
---
|
||||||
|
rules:
|
||||||
|
# delete is required for taint recovery and destroy (the resource got tainted
|
||||||
|
# by pipeline 107's failed post-create read and replace = delete+create).
|
||||||
|
# The seed's write-once semantics are enforced by lifecycle ignore_changes in
|
||||||
|
# terraform-git, not by withholding delete here.
|
||||||
|
- path: "kv/data/service/vault/au/syd1/secret_backend/gitea/config"
|
||||||
|
capabilities:
|
||||||
|
- create
|
||||||
|
- read
|
||||||
|
- update
|
||||||
|
- delete
|
||||||
|
# vault_kv_secret_v2 also reads the kv-v2 metadata path on every plan/apply
|
||||||
|
# (403 here broke the terraform-git main apply, pipeline 107).
|
||||||
|
- path: "kv/metadata/service/vault/au/syd1/secret_backend/gitea/config"
|
||||||
|
capabilities:
|
||||||
|
- read
|
||||||
|
- delete
|
||||||
|
|
||||||
|
auth:
|
||||||
|
approle:
|
||||||
|
- terraform_git
|
||||||
|
k8s/au/syd1:
|
||||||
|
- woodpecker_terraform_git
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
key_prefix "infra/terraform/enc/" {
|
||||||
|
policy = "write"
|
||||||
|
}
|
||||||
|
|
||||||
|
session_prefix "" {
|
||||||
|
policy = "write"
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
key_prefix "infra/terraform/infra/" {
|
||||||
|
policy = "write"
|
||||||
|
}
|
||||||
|
|
||||||
|
session_prefix "" {
|
||||||
|
policy = "write"
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- "cert-manager.io"
|
||||||
|
- "acme.cert-manager.io"
|
||||||
|
resources:
|
||||||
|
- "*"
|
||||||
|
verbs:
|
||||||
|
- "*"
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- "secrets"
|
||||||
|
verbs:
|
||||||
|
- "get"
|
||||||
|
- "list"
|
||||||
|
- "watch"
|
||||||
|
- "delete"
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- "pods"
|
||||||
|
verbs:
|
||||||
|
- "get"
|
||||||
|
- "list"
|
||||||
|
- "watch"
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- "pods/log"
|
||||||
|
verbs:
|
||||||
|
- "get"
|
||||||
|
- "list"
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- "kea.unkin.net"
|
||||||
|
resources:
|
||||||
|
- "*"
|
||||||
|
verbs:
|
||||||
|
- "*"
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- "pods"
|
||||||
|
- "services"
|
||||||
|
- "configmaps"
|
||||||
|
- "events"
|
||||||
|
verbs:
|
||||||
|
- "get"
|
||||||
|
- "list"
|
||||||
|
- "watch"
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- "pods/log"
|
||||||
|
verbs:
|
||||||
|
- "get"
|
||||||
|
- "list"
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- "ceph.unkin.net"
|
||||||
|
resources:
|
||||||
|
- "*"
|
||||||
|
verbs:
|
||||||
|
- "*"
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- "pods"
|
||||||
|
verbs:
|
||||||
|
- "get"
|
||||||
|
- "list"
|
||||||
|
- "watch"
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- "pods/log"
|
||||||
|
verbs:
|
||||||
|
- "get"
|
||||||
|
- "list"
|
||||||
Reference in New Issue
Block a user