0e6da5cdd3
ci/woodpecker/push/apply Pipeline was successful
## Why Agentic workloads currently need cluster-admin/root kubeconfig contexts to do routine per-domain work. This adds domain-scoped, Vault-issued kubernetes credentials plus an `agents` AppRole so agents get least-privilege access instead of escalating. ## Changes - Add `kubernetes_secret_backend_role` configs `agent-dhcp`, `agent-dns`, `agent-certs`, `agent-storage` (au/syd1): - **agent-dhcp** (Role, ns `dhcp-system`): full verbs on `kea.unkin.net` CRDs; get/list/watch pods/services/configmaps/events + pods/log. - **agent-dns** (`service_account_name` mode): mints tokens for the static `agent-dns` SA (argocd-apps#332) whose per-namespace RoleBindings confine access to bind-system/bind-internal/bind-external/externaldns. `allowed_kubernetes_namespaces: [bind-system]` (the SA's namespace). - **agent-certs** (Role, ns `cert-manager`): full verbs on `cert-manager.io` + `acme.cert-manager.io` (closes the orders/challenges debugging gap); get/list/watch/delete secrets; get/list/watch pods + pods/log. Secret delete confined to `cert-manager`. - **agent-storage** (Role, ns `cephrgw-system`): full verbs on `ceph.unkin.net` CRDs (buckets/bucketaccesses/objectstoreusers); get/list/watch pods + pods/log. - Extend the `kubernetes_secret_backend_role` module with an optional `service_account_name`; when set, `generated_role_rules`/`kubernetes_role_type` are omitted (the SA's own bindings supply RBAC). - Add creds policies for each role, bound to the `kubernetes_au_syd1_cluster_operator` ldap group (human kubectl use) and the `agents` AppRole (programmatic use). - Add the `agents` AppRole (mirrors the certmanager approle schema): `bind_secret_id: false` (role_id-only login), `token_bound_cidrs: [10.10.12.200/32]` (agent workstation wg0 addr), deterministic role_id, 1h/4h TTLs. - Add `policies/kv/kubernetes/agents.yaml` granting the AppRole create/read/update/list on `kv/data/kubernetes/*` + read/list on `kv/metadata/kubernetes/*` (no delete). ## Ordering argocd-apps#332 (the `agent-dns` SA + ClusterRole + per-namespace RoleBindings) must sync **before** the `agent-dns` creds here are usable — Vault mints tokens for a service account that must already exist. https://claude.ai/code/session_01JUoARVdmhxKQHyyyp1pxeT Reviewed-on: #109 Co-authored-by: Ben Vincent <ben@unkin.net> Co-committed-by: Ben Vincent <ben@unkin.net>
490 lines
16 KiB
Terraform
490 lines
16 KiB
Terraform
module "auth_approle_backend" {
|
|
source = "./modules/auth_approle_backend"
|
|
|
|
for_each = var.auth_approle_backend
|
|
|
|
path = each.key
|
|
listing_visibility = each.value.listing_visibility
|
|
default_lease_ttl = each.value.default_lease_ttl
|
|
max_lease_ttl = each.value.max_lease_ttl
|
|
}
|
|
|
|
module "auth_approle_role" {
|
|
source = "./modules/auth_approle_role"
|
|
|
|
for_each = var.auth_approle_role
|
|
|
|
country = var.country
|
|
region = var.region
|
|
approle_name = each.value.approle_name
|
|
mount_path = each.value.mount_path
|
|
token_policies = var.policy_auth_map[each.value.mount_path][each.value.approle_name]
|
|
token_ttl = each.value.token_ttl
|
|
token_max_ttl = each.value.token_max_ttl
|
|
bind_secret_id = each.value.bind_secret_id
|
|
secret_id_ttl = each.value.secret_id_ttl
|
|
token_bound_cidrs = each.value.token_bound_cidrs
|
|
alias_metadata = each.value.alias_metadata
|
|
use_deterministic_role_id = each.value.use_deterministic_role_id
|
|
|
|
depends_on = [module.auth_approle_backend]
|
|
}
|
|
|
|
module "auth_ldap_backend" {
|
|
source = "./modules/auth_ldap_backend"
|
|
|
|
for_each = var.auth_ldap_backend
|
|
|
|
country = var.country
|
|
region = var.region
|
|
path = each.key
|
|
userdn = each.value.userdn
|
|
userattr = each.value.userattr
|
|
upndomain = each.value.upndomain
|
|
discoverdn = each.value.discoverdn
|
|
groupdn = each.value.groupdn
|
|
groupfilter = each.value.groupfilter
|
|
groupattr = each.value.groupattr
|
|
alias_metadata = each.value.alias_metadata
|
|
username_as_alias = each.value.username_as_alias
|
|
listing_visibility = each.value.listing_visibility
|
|
default_lease_ttl = each.value.default_lease_ttl
|
|
max_lease_ttl = each.value.max_lease_ttl
|
|
}
|
|
|
|
module "auth_ldap_group" {
|
|
source = "./modules/auth_ldap_group"
|
|
|
|
for_each = var.auth_ldap_group
|
|
|
|
groupname = each.value.groupname
|
|
backend = each.value.backend
|
|
policies = var.policy_auth_map[each.value.backend][each.value.groupname]
|
|
|
|
depends_on = [module.auth_ldap_backend]
|
|
}
|
|
|
|
module "auth_kubernetes_backend" {
|
|
source = "./modules/auth_kubernetes_backend"
|
|
|
|
for_each = var.auth_kubernetes_backend
|
|
|
|
country = var.country
|
|
region = var.region
|
|
path = each.key
|
|
kubernetes_host = each.value.kubernetes_host
|
|
disable_iss_validation = each.value.disable_iss_validation
|
|
use_annotations_as_alias_metadata = each.value.use_annotations_as_alias_metadata
|
|
listing_visibility = each.value.listing_visibility
|
|
default_lease_ttl = each.value.default_lease_ttl
|
|
max_lease_ttl = each.value.max_lease_ttl
|
|
}
|
|
|
|
module "auth_kubernetes_role" {
|
|
source = "./modules/auth_kubernetes_role"
|
|
|
|
for_each = var.auth_kubernetes_role
|
|
|
|
role_name = each.value.role_name
|
|
backend = each.value.backend
|
|
bound_service_account_names = each.value.bound_service_account_names
|
|
bound_service_account_namespaces = each.value.bound_service_account_namespaces
|
|
token_ttl = each.value.token_ttl
|
|
token_max_ttl = each.value.token_max_ttl
|
|
token_policies = var.policy_auth_map[each.value.backend][each.value.role_name]
|
|
audience = each.value.audience
|
|
|
|
depends_on = [module.auth_kubernetes_backend]
|
|
}
|
|
|
|
module "kv_secret_backend" {
|
|
source = "./modules/kv_secret_backend"
|
|
|
|
for_each = var.kv_secret_backend
|
|
|
|
path = each.key
|
|
type = each.value.type
|
|
description = each.value.description
|
|
kv_version = each.value.version
|
|
max_versions = each.value.max_versions
|
|
}
|
|
|
|
module "transit_secret_backend" {
|
|
source = "./modules/transit_secret_backend"
|
|
|
|
for_each = var.transit_secret_backend
|
|
|
|
path = each.key
|
|
description = each.value.description
|
|
default_lease_ttl_seconds = each.value.default_lease_ttl_seconds
|
|
max_lease_ttl_seconds = each.value.max_lease_ttl_seconds
|
|
}
|
|
|
|
module "transit_secret_backend_key" {
|
|
source = "./modules/transit_secret_backend_key"
|
|
|
|
for_each = var.transit_secret_backend_key
|
|
|
|
name = each.value.name
|
|
backend = each.value.backend
|
|
type = each.value.type
|
|
deletion_allowed = each.value.deletion_allowed
|
|
derived = each.value.derived
|
|
exportable = each.value.exportable
|
|
allow_plaintext_backup = each.value.allow_plaintext_backup
|
|
auto_rotate_period = each.value.auto_rotate_period
|
|
|
|
depends_on = [module.transit_secret_backend]
|
|
}
|
|
|
|
module "ssh_secret_backend" {
|
|
source = "./modules/ssh_secret_backend"
|
|
|
|
for_each = var.ssh_secret_backend
|
|
|
|
path = each.key
|
|
description = each.value.description
|
|
max_lease_ttl_seconds = each.value.max_lease_ttl_seconds
|
|
generate_signing_key = each.value.generate_signing_key
|
|
key_type = each.value.key_type
|
|
}
|
|
|
|
module "ssh_secret_backend_role" {
|
|
source = "./modules/ssh_secret_backend_role"
|
|
|
|
for_each = var.ssh_secret_backend_role
|
|
|
|
name = each.value.name
|
|
backend = each.value.backend
|
|
key_type = each.value.key_type
|
|
algorithm_signer = each.value.algorithm_signer
|
|
ttl = each.value.ttl
|
|
allow_host_certificates = each.value.allow_host_certificates
|
|
allow_user_certificates = each.value.allow_user_certificates
|
|
allowed_domains = each.value.allowed_domains
|
|
allow_subdomains = each.value.allow_subdomains
|
|
allow_bare_domains = each.value.allow_bare_domains
|
|
|
|
depends_on = [module.ssh_secret_backend]
|
|
}
|
|
|
|
module "pki_secret_backend" {
|
|
source = "./modules/pki_secret_backend"
|
|
|
|
for_each = var.pki_secret_backend
|
|
|
|
path = each.key
|
|
description = each.value.description
|
|
max_lease_ttl_seconds = each.value.max_lease_ttl_seconds
|
|
common_name = each.value.common_name
|
|
issuer_name = each.value.issuer_name
|
|
ttl = each.value.ttl
|
|
format = each.value.format
|
|
issuing_certificates = each.value.issuing_certificates
|
|
crl_distribution_points = each.value.crl_distribution_points
|
|
ocsp_servers = each.value.ocsp_servers
|
|
enable_templating = each.value.enable_templating
|
|
default_follows_latest_issuer = each.value.default_follows_latest_issuer
|
|
crl_expiry = each.value.crl_expiry
|
|
crl_disable = each.value.crl_disable
|
|
ocsp_disable = each.value.ocsp_disable
|
|
auto_rebuild = each.value.auto_rebuild
|
|
enable_delta = each.value.enable_delta
|
|
delta_rebuild_interval = each.value.delta_rebuild_interval
|
|
}
|
|
|
|
module "pki_secret_backend_role" {
|
|
source = "./modules/pki_secret_backend_role"
|
|
|
|
for_each = var.pki_secret_backend_role
|
|
|
|
name = each.value.name
|
|
backend = each.value.backend
|
|
allow_ip_sans = each.value.allow_ip_sans
|
|
allowed_domains = each.value.allowed_domains
|
|
allow_subdomains = each.value.allow_subdomains
|
|
allow_glob_domains = each.value.allow_glob_domains
|
|
allow_bare_domains = each.value.allow_bare_domains
|
|
enforce_hostnames = each.value.enforce_hostnames
|
|
allow_any_name = each.value.allow_any_name
|
|
max_ttl = each.value.max_ttl
|
|
key_bits = each.value.key_bits
|
|
country = each.value.country
|
|
use_csr_common_name = each.value.use_csr_common_name
|
|
use_csr_sans = each.value.use_csr_sans
|
|
|
|
depends_on = [module.pki_secret_backend]
|
|
}
|
|
|
|
module "consul_secret_backend" {
|
|
source = "./modules/consul_secret_backend"
|
|
|
|
for_each = var.consul_secret_backend
|
|
|
|
country = var.country
|
|
region = var.region
|
|
path = each.key
|
|
description = each.value.description
|
|
address = each.value.address
|
|
bootstrap = each.value.bootstrap
|
|
scheme = each.value.scheme
|
|
ca_cert = each.value.ca_cert
|
|
client_cert = each.value.client_cert
|
|
client_key = each.value.client_key
|
|
default_lease_ttl_seconds = each.value.default_lease_ttl_seconds
|
|
max_lease_ttl_seconds = each.value.max_lease_ttl_seconds
|
|
}
|
|
|
|
# Create data sources for consul backend tokens
|
|
data "vault_kv_secret_v2" "consul_backend_configs" {
|
|
for_each = {
|
|
for k, v in var.consul_secret_backend : k => v
|
|
if !v.bootstrap
|
|
}
|
|
|
|
mount = "kv"
|
|
name = "service/vault/${var.country}/${var.region}/secret_backend/${each.key}"
|
|
}
|
|
|
|
# Create Consul ACL management module
|
|
module "consul_acl_management" {
|
|
source = "./modules/consul_acl_management"
|
|
|
|
country = var.country
|
|
region = var.region
|
|
consul_backends = var.consul_secret_backend
|
|
consul_roles = var.consul_secret_backend_role
|
|
consul_backend_aliases = var.consul_backend_aliases
|
|
}
|
|
|
|
# Create consul secret backend roles (Vault resources only)
|
|
module "consul_secret_backend_role" {
|
|
source = "./modules/consul_secret_backend_role"
|
|
|
|
for_each = var.consul_secret_backend_role
|
|
|
|
name = each.value.name
|
|
backend = each.value.backend
|
|
ttl = each.value.ttl
|
|
max_ttl = each.value.max_ttl
|
|
local = each.value.local
|
|
|
|
depends_on = [module.consul_secret_backend, module.consul_acl_management]
|
|
}
|
|
|
|
module "kubernetes_secret_backend" {
|
|
source = "./modules/kubernetes_secret_backend"
|
|
|
|
for_each = var.kubernetes_secret_backend
|
|
|
|
country = var.country
|
|
region = var.region
|
|
path = each.key
|
|
description = each.value.description
|
|
default_lease_ttl_seconds = each.value.default_lease_ttl_seconds
|
|
max_lease_ttl_seconds = each.value.max_lease_ttl_seconds
|
|
kubernetes_host = each.value.kubernetes_host
|
|
disable_local_ca_jwt = each.value.disable_local_ca_jwt
|
|
}
|
|
|
|
module "kubernetes_secret_backend_role" {
|
|
source = "./modules/kubernetes_secret_backend_role"
|
|
|
|
for_each = var.kubernetes_secret_backend_role
|
|
|
|
country = var.country
|
|
region = var.region
|
|
name = each.value.name
|
|
backend = each.value.backend
|
|
allowed_kubernetes_namespaces = each.value.allowed_kubernetes_namespaces
|
|
kubernetes_role_type = each.value.kubernetes_role_type
|
|
extra_labels = each.value.extra_labels
|
|
service_account_name = each.value.service_account_name
|
|
|
|
depends_on = [module.kubernetes_secret_backend]
|
|
}
|
|
|
|
module "litellm_secret_backend" {
|
|
source = "./modules/litellm_secret_backend"
|
|
|
|
for_each = var.litellm_secret_backend
|
|
|
|
country = var.country
|
|
region = var.region
|
|
path = each.key
|
|
plugin = each.value.plugin
|
|
description = each.value.description
|
|
base_url = each.value.base_url
|
|
request_timeout_seconds = each.value.request_timeout_seconds
|
|
}
|
|
|
|
module "litellm_secret_backend_role" {
|
|
source = "./modules/litellm_secret_backend_role"
|
|
|
|
for_each = var.litellm_secret_backend_role
|
|
|
|
name = each.value.name
|
|
backend = each.value.backend
|
|
models = each.value.models
|
|
max_budget = each.value.max_budget
|
|
key_alias_prefix = each.value.key_alias_prefix
|
|
ttl = each.value.ttl
|
|
max_ttl = each.value.max_ttl
|
|
metadata = each.value.metadata
|
|
|
|
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" {
|
|
source = "./modules/gpg_secret_backend"
|
|
|
|
for_each = var.gpg_secret_backend
|
|
|
|
path = each.key
|
|
plugin = each.value.plugin
|
|
description = each.value.description
|
|
|
|
depends_on = [module.plugin]
|
|
}
|
|
|
|
module "gpg_key" {
|
|
source = "./modules/gpg_key"
|
|
|
|
for_each = var.gpg_key
|
|
|
|
backend = each.value.backend
|
|
name = each.value.name
|
|
algorithm = each.value.algorithm
|
|
identity = each.value.identity
|
|
exportable = each.value.exportable
|
|
deletion_allowed = each.value.deletion_allowed
|
|
min_decryption_version = each.value.min_decryption_version
|
|
|
|
depends_on = [module.gpg_secret_backend]
|
|
}
|
|
|
|
module "rancher_secret_backend" {
|
|
source = "./modules/rancher_secret_backend"
|
|
|
|
for_each = var.rancher_secret_backend
|
|
|
|
path = each.key
|
|
plugin = each.value.plugin
|
|
description = each.value.description
|
|
rancher_url = each.value.rancher_url
|
|
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 "rancher_secret_backend_service_account" {
|
|
source = "./modules/rancher_secret_backend_service_account"
|
|
|
|
for_each = var.rancher_secret_backend_service_account
|
|
|
|
backend = each.value.backend
|
|
name = each.value.name
|
|
country = var.country
|
|
region = var.region
|
|
token_ttl = each.value.token_ttl
|
|
rotation_period = each.value.rotation_period
|
|
|
|
depends_on = [module.rancher_secret_backend]
|
|
}
|
|
|
|
module "rancher_secret_backend_role" {
|
|
source = "./modules/rancher_secret_backend_role"
|
|
|
|
for_each = var.rancher_secret_backend_role
|
|
|
|
backend = each.value.backend
|
|
name = each.value.name
|
|
service_account = each.value.service_account
|
|
cluster_name = each.value.cluster_name
|
|
description = each.value.description
|
|
ttl = each.value.ttl
|
|
max_ttl = each.value.max_ttl
|
|
|
|
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"
|
|
|
|
for_each = var.policy_rules_map
|
|
|
|
policy_name = each.key
|
|
policy_rules = each.value
|
|
}
|
|
|
|
module "pki_mount_only" {
|
|
source = "./modules/pki_mount_only"
|
|
|
|
for_each = var.pki_mount_only
|
|
|
|
path = each.key
|
|
description = each.value.description
|
|
max_lease_ttl_seconds = each.value.max_lease_ttl_seconds
|
|
issuing_certificates = each.value.issuing_certificates
|
|
crl_distribution_points = each.value.crl_distribution_points
|
|
ocsp_servers = each.value.ocsp_servers
|
|
enable_templating = each.value.enable_templating
|
|
default_issuer_ref = each.value.default_issuer_ref
|
|
default_follows_latest_issuer = each.value.default_follows_latest_issuer
|
|
crl_expiry = each.value.crl_expiry
|
|
crl_disable = each.value.crl_disable
|
|
ocsp_disable = each.value.ocsp_disable
|
|
auto_rebuild = each.value.auto_rebuild
|
|
enable_delta = each.value.enable_delta
|
|
delta_rebuild_interval = each.value.delta_rebuild_interval
|
|
}
|
|
|