Files
terraform-vault/modules/vault_cluster/main.tf
T
unkin-agent b225ef6344
ci/woodpecker/pr/plan Pipeline failed
ci/woodpecker/pr/pre-commit Pipeline was successful
Add Authentik OIDC SSO as the default human login for OpenBao
Human access to OpenBao is LDAP-only today, so operators carry a second set of
credentials outside Authentik and group membership is maintained twice. This
makes Authentik SSO the offered default on the UI login page and gives
`bao login -method=oidc` a working CLI path, while approle and kubernetes (CI
and agents) plus the break-glass root path are untouched.

Add three modules mirroring the auth_ldap_* structure: auth_oidc_backend mounts
a vault_jwt_auth_backend of type oidc, auth_oidc_role creates the default login
role, and auth_oidc_group creates an external vault_identity_group plus its
group alias so IdP groups map onto policies.

Mount the backend at the literal path "oidc". The Authentik provider registers
strict redirect URIs containing /ui/vault/auth/oidc/oidc/callback, so the path
is load-bearing and must not be renamed.

Read client_id and client_secret from kv/service/authentik/oidc-vault, which
terraform-authentik generates and writes; nothing is seeded by hand.

Match groups on the ak_groups claim rather than groups, because Authentik's
default profile mapping only emits direct memberships and the estate nests
akP-* permission groups under akR-* roles.

Bind akP-vault-admin to global-root, the same policy the LDAP vault_admin group
already carries. Only akP-* permission groups are named in config or policy;
akR-* roles stay grouping-only.

Grant the deployer auth/oidc/* and identity group management, both of which it
currently lacks. AppRole capabilities are fixed at login, so these land in an
apply before the resources that need them.
2026-08-30 21:49:14 +10:00

686 lines
23 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_oidc_backend" {
source = "./modules/auth_oidc_backend"
for_each = var.auth_oidc_backend
path = each.key
description = each.value.description
oidc_discovery_url = each.value.oidc_discovery_url
client_secret_mount = each.value.client_secret_mount
client_secret_path = each.value.client_secret_path
default_role = each.value.default_role
listing_visibility = each.value.listing_visibility
default_lease_ttl = each.value.default_lease_ttl
max_lease_ttl = each.value.max_lease_ttl
}
module "auth_oidc_role" {
source = "./modules/auth_oidc_role"
for_each = var.auth_oidc_role
backend = each.value.backend
role_name = each.value.role_name
user_claim = each.value.user_claim
groups_claim = each.value.groups_claim
oidc_scopes = each.value.oidc_scopes
bound_audiences = each.value.bound_audiences
allowed_redirect_uris = each.value.allowed_redirect_uris
token_ttl = each.value.token_ttl
token_max_ttl = each.value.token_max_ttl
# Human authorization comes from the external identity groups below, so a
# login role carrying no policies is the normal case.
token_policies = try(var.policy_auth_map[each.value.backend][each.value.role_name], [])
depends_on = [module.auth_oidc_backend]
}
module "auth_oidc_group" {
source = "./modules/auth_oidc_group"
for_each = var.auth_oidc_group
groupname = each.value.groupname
policies = var.policy_auth_map[each.value.backend][each.value.groupname]
mount_accessor = module.auth_oidc_backend[each.value.backend].accessor
depends_on = [module.auth_oidc_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 "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
methods = each.value.methods
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"
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 "netbox_secret_backend" {
source = "./modules/netbox_secret_backend"
for_each = var.netbox_secret_backend
path = each.key
plugin = each.value.plugin
description = each.value.description
netbox_url = each.value.netbox_url
token_version = each.value.token_version
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]
}
# Dedicated engine role that mints an ephemeral, user-admin-capable token for the
# pre-existing NetBox superuser named on each backend (user_mgmt_username).
# netbox_user_management reads netbox/creds/vault-user-mgmt from it, so it
# authenticates with a short-lived Vault-minted token derived from the single
# static admin token - never a second static credential, and unaffected by
# rotation of the engine's admin seed. Created before user management so the role
# exists when it reads creds.
module "netbox_user_mgmt_role" {
source = "./modules/netbox_secret_backend_role"
for_each = { for k, v in var.netbox_secret_backend : k => v if v.user_mgmt_username != null }
backend = each.key
name = "vault-user-mgmt"
netbox_username = each.value.user_mgmt_username
write_enabled = true
description = "Ephemeral user-admin token for netbox_user_management (Vault-minted per apply)"
ttl = 600
max_ttl = 1200
depends_on = [module.netbox_secret_backend]
}
# Declaratively manage the NetBox service users + object permissions the engine
# roles mint tokens for, authenticating with the Vault-minted user-admin token
# above (mirrors consul_acl_management). Consumes the SAME role config as
# netbox_secret_backend_role: one file per identity, filename-derived username,
# inline permissions.
module "netbox_user_management" {
source = "./modules/netbox_user_management"
country = var.country
region = var.region
netbox_backends = var.netbox_secret_backend
netbox_roles = var.netbox_secret_backend_role
netbox_backend_aliases = var.netbox_backend_aliases
# This module declares its own netbox provider, so it is a legacy module and
# cannot take depends_on. Ordering vs the vault-user-mgmt role is not needed on
# steady state (the role pre-exists, so reading its creds succeeds regardless);
# on first enablement the role must be created first via the one-time targeted
# bootstrap documented in config/netbox_secret_backend/netbox.yaml.
}
module "netbox_secret_backend_role" {
source = "./modules/netbox_secret_backend_role"
for_each = var.netbox_secret_backend_role
backend = each.value.backend
name = each.value.name
netbox_username = each.value.netbox_username
netbox_user_id = each.value.netbox_user_id
write_enabled = each.value.write_enabled
description = each.value.description
ttl = each.value.ttl
max_ttl = each.value.max_ttl
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"
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
}