authentik: add Grafana OAuth2/OIDC provider #2
@@ -4,6 +4,10 @@ VAULT_AUTH_METHOD ?= approle
|
||||
VAULT_K8S_ROLE ?= woodpecker_terraform_authentik
|
||||
VAULT_K8S_MOUNT ?= auth/k8s/au/syd1
|
||||
VAULT_K8S_JWT_PATH ?= /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||
# kv-v2 location of the Authentik API token used by the provider.
|
||||
AUTHENTIK_TOKEN_KV_MOUNT ?= kv
|
||||
AUTHENTIK_TOKEN_KV_PATH ?= service/terraform/authentik
|
||||
AUTHENTIK_TOKEN_KV_FIELD ?= token
|
||||
|
||||
define vault_env
|
||||
@export VAULT_ADDR="https://vault.service.consul:8200" && \
|
||||
@@ -12,7 +16,8 @@ define vault_env
|
||||
else \
|
||||
export VAULT_TOKEN=$$(vault write -field=token auth/approle/login role_id=$$VAULT_ROLEID); \
|
||||
fi && \
|
||||
export CONSUL_HTTP_TOKEN=$$(vault read -field=token consul_root/au/syd1/creds/terraform-authentik)
|
||||
export CONSUL_HTTP_TOKEN=$$(vault read -field=token consul_root/au/syd1/creds/terraform-authentik) && \
|
||||
export TF_VAR_authentik_token=$$(vault kv get -mount=$(AUTHENTIK_TOKEN_KV_MOUNT) -field=$(AUTHENTIK_TOKEN_KV_FIELD) $(AUTHENTIK_TOKEN_KV_PATH))
|
||||
endef
|
||||
|
||||
init:
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# OAuth2/OIDC provider + application for the in-cluster Grafana
|
||||
# (grafana.k8s.syd1.au.unkin.net). client_secret is read from Vault, not committed.
|
||||
name: Grafana
|
||||
authorization_flow: default-provider-authorization-implicit-consent
|
||||
invalidation_flow: default-provider-invalidation-flow
|
||||
client_type: confidential
|
||||
client_id: grafana
|
||||
client_secret_vault:
|
||||
mount: kv
|
||||
path: kubernetes/namespace/grafana/default/oauth-credentials
|
||||
scope_mappings:
|
||||
- goauthentik.io/providers/oauth2/scope-openid
|
||||
- goauthentik.io/providers/oauth2/scope-email
|
||||
- goauthentik.io/providers/oauth2/scope-profile
|
||||
redirect_uris:
|
||||
- matching_mode: strict
|
||||
url: https://grafana.k8s.syd1.au.unkin.net/login/generic_oauth
|
||||
@@ -7,6 +7,13 @@ provider "authentik" {
|
||||
token = var.authentik_token
|
||||
}
|
||||
|
||||
# Reads client secrets seeded in Vault (kv-v2). Auth via VAULT_ADDR + VAULT_TOKEN
|
||||
# from the environment (set by the Makefile vault_env helper). skip_child_token
|
||||
# is required because the short-lived CI token cannot create child tokens.
|
||||
provider "vault" {
|
||||
skip_child_token = true
|
||||
}
|
||||
|
||||
variable "authentik_token" {
|
||||
type = string
|
||||
sensitive = true
|
||||
@@ -21,12 +28,8 @@ terraform {
|
||||
ca_file = "/etc/pki/tls/certs/ca-bundle.crt"
|
||||
}
|
||||
required_version = ">= 1.10"
|
||||
required_providers {
|
||||
authentik = {
|
||||
source = "goauthentik/authentik"
|
||||
version = "2026.5.0"
|
||||
}
|
||||
}
|
||||
# required_providers are declared in the module's versions.tf; declaring them
|
||||
# here as well is a duplicate configuration (OpenTofu permits only one).
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
Generated
+23
@@ -21,3 +21,26 @@ provider "registry.opentofu.org/goauthentik/authentik" {
|
||||
"zh:eb9c1fd3020cf61e9b7a6a38d2965f4b521495a9928705e963459a4af857f97d",
|
||||
]
|
||||
}
|
||||
|
||||
provider "registry.opentofu.org/hashicorp/vault" {
|
||||
version = "5.10.1"
|
||||
constraints = ">= 4.0.0"
|
||||
hashes = [
|
||||
"h1:wo5cTkl/1nlxMfdn1yEDIHNoRLMczuK6COH2Id4/zeY=",
|
||||
"zh:0abf976c01f0c0732d0ccc6481e52008be5ee9c8e3d9b5eba0573c640fcf7019",
|
||||
"zh:2aff4d7ee7ba9eb3de2cd5cda16ba92b4ec7a2b43232aec180984241a323b216",
|
||||
"zh:2cc186fd0bfc44e100a22b0b40ae8ddcd0ec210a53c1da65d310ee758b1d2b08",
|
||||
"zh:3f8fb8594736b34af4b26437dd4df4dd4042ad4905223995cfebb8a1f10682ec",
|
||||
"zh:47fb41b18b74073f557dbcd6aad2183e416293405ccd70c0691a279cfe97f8cd",
|
||||
"zh:517e2f2764d671c22d22def0384fdfc521b456458189189c0363375495d114dc",
|
||||
"zh:5a49a2003636f2b8a547d494a6c06d43d62a68299775305408c52eff22b1c11f",
|
||||
"zh:66d4e716920ada84b0c768f4aca4c8948388995462923349a01bd3818d82b618",
|
||||
"zh:7599f652e89a3f18fa4b76a59d115cc63255cc36ce6b273850509ba25031abca",
|
||||
"zh:9c3e38ae7e670de973b6255d7050f526cd2b3ca7c383d7ba7226fc204d97c507",
|
||||
"zh:d04b046023fa9fd69def678f27e001c298ea34fc99ba51f835cda82e496fdb57",
|
||||
"zh:d9acd8810f6660cd51bb4c25596632984ae18e93340c82a102d074c6eac95151",
|
||||
"zh:e161bcb9a22607270b980eeff2ba693335fb62d6978516dff93dd4c91cda99b3",
|
||||
"zh:ef47502f08cfcb5311b7b16a7905e0052bf28359e07cc00ed080ef454e0946cf",
|
||||
"zh:f0640ddb52e7e90c5006ff571f6ad0554e593665320c764c57a3d8b7ec31b490",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -20,18 +20,42 @@ resource "authentik_provider_saml" "this" {
|
||||
signing_kp = each.value.signing_kp
|
||||
}
|
||||
|
||||
# Resolve oauth2 flows by slug and scope mappings by managed identifier, and
|
||||
# read client secrets from Vault so nothing sensitive is committed.
|
||||
data "authentik_flow" "oauth2_authorization" {
|
||||
for_each = var.providers_oauth2
|
||||
slug = each.value.authorization_flow
|
||||
}
|
||||
|
||||
data "authentik_flow" "oauth2_invalidation" {
|
||||
for_each = var.providers_oauth2
|
||||
slug = each.value.invalidation_flow
|
||||
}
|
||||
|
||||
data "authentik_property_mapping_provider_scope" "oauth2" {
|
||||
for_each = { for k, v in var.providers_oauth2 : k => v if length(v.scope_mappings) > 0 }
|
||||
managed_list = each.value.scope_mappings
|
||||
}
|
||||
|
||||
data "vault_kv_secret_v2" "oauth2" {
|
||||
for_each = { for k, v in var.providers_oauth2 : k => v if v.client_secret_vault != null }
|
||||
mount = each.value.client_secret_vault.mount
|
||||
name = each.value.client_secret_vault.path
|
||||
}
|
||||
|
||||
resource "authentik_provider_oauth2" "this" {
|
||||
for_each = var.providers_oauth2
|
||||
|
||||
name = each.value.name
|
||||
authorization_flow = each.value.authorization_flow
|
||||
invalidation_flow = each.value.invalidation_flow
|
||||
authorization_flow = data.authentik_flow.oauth2_authorization[each.key].id
|
||||
invalidation_flow = data.authentik_flow.oauth2_invalidation[each.key].id
|
||||
client_type = each.value.client_type
|
||||
client_id = each.value.client_id
|
||||
client_secret = each.value.client_secret
|
||||
property_mappings = each.value.property_mappings
|
||||
client_secret = each.value.client_secret_vault != null ? data.vault_kv_secret_v2.oauth2[each.key].data["client_secret"] : null
|
||||
property_mappings = try(data.authentik_property_mapping_provider_scope.oauth2[each.key].ids, [])
|
||||
signing_key = each.value.signing_key
|
||||
access_token_validity = each.value.access_token_validity
|
||||
allowed_redirect_uris = each.value.redirect_uris
|
||||
}
|
||||
|
||||
resource "authentik_provider_ldap" "this" {
|
||||
|
||||
@@ -25,12 +25,23 @@ variable "providers_saml" {
|
||||
variable "providers_oauth2" {
|
||||
type = map(object({
|
||||
name = string
|
||||
authorization_flow = string
|
||||
invalidation_flow = string
|
||||
authorization_flow = string # flow slug, resolved to id via data.authentik_flow
|
||||
invalidation_flow = string # flow slug, resolved to id via data.authentik_flow
|
||||
client_type = optional(string, "confidential")
|
||||
client_id = string
|
||||
client_secret = optional(string, null)
|
||||
property_mappings = optional(list(string), [])
|
||||
# client_secret is never committed. Point at a Vault kv-v2 secret whose
|
||||
# `client_secret` key holds the value (seeded out of band); TF reads it.
|
||||
client_secret_vault = optional(object({
|
||||
mount = string
|
||||
path = string
|
||||
}), null)
|
||||
# Managed identifiers of scope property mappings (e.g.
|
||||
# goauthentik.io/providers/oauth2/scope-openid). Resolved to ids.
|
||||
scope_mappings = optional(list(string), [])
|
||||
redirect_uris = optional(list(object({
|
||||
matching_mode = optional(string, "strict")
|
||||
url = string
|
||||
})), [])
|
||||
signing_key = optional(string, null)
|
||||
access_token_validity = optional(string, "minutes=10")
|
||||
}))
|
||||
|
||||
@@ -5,5 +5,9 @@ terraform {
|
||||
source = "goauthentik/authentik"
|
||||
version = ">= 2026.5.0"
|
||||
}
|
||||
vault = {
|
||||
source = "hashicorp/vault"
|
||||
version = ">= 4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user