Added Authentik TF code
All checks were successful
Check with kubeconform / lint (push) Successful in 12s

This commit is contained in:
AB from home.homenet
2025-09-16 15:28:42 +03:00
parent b1183896f9
commit 4ffc42af97
15 changed files with 475 additions and 14 deletions

View File

@@ -7,7 +7,8 @@
"Bash(\"C:\\Users\\ab\\AppData\\Local\\Microsoft\\WinGet\\Packages\\Hashicorp.Terraform_Microsoft.Winget.Source_8wekyb3d8bbwe\\terraform.exe\" apply -auto-approve)", "Bash(\"C:\\Users\\ab\\AppData\\Local\\Microsoft\\WinGet\\Packages\\Hashicorp.Terraform_Microsoft.Winget.Source_8wekyb3d8bbwe\\terraform.exe\" apply -auto-approve)",
"Bash(\"C:\\Users\\ab\\AppData\\Local\\Microsoft\\WinGet\\Packages\\Hashicorp.Terraform_Microsoft.Winget.Source_8wekyb3d8bbwe\\terraform.exe\" apply -auto-approve -lock=false)", "Bash(\"C:\\Users\\ab\\AppData\\Local\\Microsoft\\WinGet\\Packages\\Hashicorp.Terraform_Microsoft.Winget.Source_8wekyb3d8bbwe\\terraform.exe\" apply -auto-approve -lock=false)",
"Bash(\"C:\\Users\\ab\\AppData\\Local\\Microsoft\\WinGet\\Packages\\Hashicorp.Terraform_Microsoft.Winget.Source_8wekyb3d8bbwe\\terraform.exe\" plan -lock=false)", "Bash(\"C:\\Users\\ab\\AppData\\Local\\Microsoft\\WinGet\\Packages\\Hashicorp.Terraform_Microsoft.Winget.Source_8wekyb3d8bbwe\\terraform.exe\" plan -lock=false)",
"Bash(\"C:\\Users\\ab\\AppData\\Local\\Microsoft\\WinGet\\Packages\\Hashicorp.Terraform_Microsoft.Winget.Source_8wekyb3d8bbwe\\terraform.exe\" apply -replace=\"authentik_outpost.outposts[\"\"kubernetes-outpost\"\"]\" -auto-approve -lock=false)" "Bash(\"C:\\Users\\ab\\AppData\\Local\\Microsoft\\WinGet\\Packages\\Hashicorp.Terraform_Microsoft.Winget.Source_8wekyb3d8bbwe\\terraform.exe\" apply -replace=\"authentik_outpost.outposts[\"\"kubernetes-outpost\"\"]\" -auto-approve -lock=false)",
"Bash(terraform plan:*)"
], ],
"deny": [], "deny": [],
"ask": [] "ask": []

View File

@@ -5,7 +5,6 @@ provider "registry.terraform.io/goauthentik/authentik" {
version = "2025.8.1" version = "2025.8.1"
constraints = ">= 2023.10.0, 2025.8.1" constraints = ">= 2023.10.0, 2025.8.1"
hashes = [ hashes = [
"h1:L3Fh0LyQ066laexCAeqLd+AVuSPDemwCmYgq1Bges6c=",
"h1:R3h8ADB0Kkv/aoY0AaHkBiX2/P4+GnW8sSgkN30kJfQ=", "h1:R3h8ADB0Kkv/aoY0AaHkBiX2/P4+GnW8sSgkN30kJfQ=",
"zh:0c3f1083fd48f20ed06959401ff1459fbb5d454d81c8175b5b6d321b308c0be3", "zh:0c3f1083fd48f20ed06959401ff1459fbb5d454d81c8175b5b6d321b308c0be3",
"zh:21c6d93f8d26e688da38a660d121b5624e3597c426c671289f31a17a9771abbf", "zh:21c6d93f8d26e688da38a660d121b5624e3597c426c671289f31a17a9771abbf",
@@ -28,7 +27,6 @@ provider "registry.terraform.io/hashicorp/random" {
version = "3.7.2" version = "3.7.2"
constraints = ">= 3.5.0" constraints = ">= 3.5.0"
hashes = [ hashes = [
"h1:0hcNr59VEJbhZYwuDE/ysmyTS0evkfcLarlni+zATPM=",
"h1:356j/3XnXEKr9nyicLUufzoF4Yr6hRy481KIxRVpK0c=", "h1:356j/3XnXEKr9nyicLUufzoF4Yr6hRy481KIxRVpK0c=",
"zh:14829603a32e4bc4d05062f059e545a91e27ff033756b48afbae6b3c835f508f", "zh:14829603a32e4bc4d05062f059e545a91e27ff033756b48afbae6b3c835f508f",
"zh:1527fb07d9fea400d70e9e6eb4a2b918d5060d604749b6f1c361518e7da546dc", "zh:1527fb07d9fea400d70e9e6eb4a2b918d5060d604749b6f1c361518e7da546dc",

View File

@@ -11,15 +11,63 @@ data "authentik_flow" "default_invalidation_flow" {
slug = var.default_invalidation_flow slug = var.default_invalidation_flow
} }
resource "authentik_group" "groups" { # Root groups (without parent)
for_each = var.groups resource "authentik_group" "root_groups" {
for_each = {
for k, v in var.groups : k => v
if v.parent == null
}
name = each.value.name name = each.value.name
is_superuser = each.value.is_superuser is_superuser = each.value.is_superuser
parent = each.value.parent
attributes = jsonencode(each.value.attributes) attributes = jsonencode(each.value.attributes)
} }
# Child groups (with parent)
resource "authentik_group" "child_groups" {
for_each = {
for k, v in var.groups : k => v
if v.parent != null
}
name = each.value.name
is_superuser = each.value.is_superuser
parent = authentik_group.root_groups[each.value.parent].id
attributes = jsonencode(each.value.attributes)
depends_on = [authentik_group.root_groups]
}
# Auto-created groups for proxy applications
resource "authentik_group" "proxy_app_groups" {
for_each = {
for k, v in var.proxy_applications : k => v
if v.create_group == true
}
name = "TF-${each.value.name} Users"
is_superuser = false
attributes = jsonencode({
notes = "Auto-created for ${each.value.name} application"
app_slug = each.value.slug
})
}
# Auto-created groups for OAuth applications
resource "authentik_group" "oauth_app_groups" {
for_each = {
for k, v in var.oauth_applications : k => v
if v.create_group == true
}
name = "TF-${each.value.name} Users"
is_superuser = false
attributes = jsonencode({
notes = "Auto-created for ${each.value.name} application"
app_slug = each.value.slug
})
}
resource "authentik_certificate_key_pair" "certificates" { resource "authentik_certificate_key_pair" "certificates" {
for_each = var.certificates for_each = var.certificates
@@ -92,6 +140,16 @@ module "oauth_applications" {
meta_description = each.value.meta_description meta_description = each.value.meta_description
meta_launch_url = each.value.meta_launch_url meta_launch_url = each.value.meta_launch_url
meta_icon = each.value.meta_icon meta_icon = each.value.meta_icon
scope_mappings = each.value.scope_mappings
# Access control - only pass explicitly defined groups
access_groups = [
for group_key in each.value.access_groups :
try(
authentik_group.root_groups[group_key].id,
authentik_group.child_groups[group_key].id
)
]
} }
module "proxy_applications" { module "proxy_applications" {
@@ -119,6 +177,76 @@ module "proxy_applications" {
meta_description = each.value.meta_description meta_description = each.value.meta_description
meta_launch_url = each.value.meta_launch_url meta_launch_url = each.value.meta_launch_url
meta_icon = each.value.meta_icon meta_icon = each.value.meta_icon
# Access control - only pass explicitly defined groups
access_groups = [
for group_key in each.value.access_groups :
try(
authentik_group.root_groups[group_key].id,
authentik_group.child_groups[group_key].id
)
]
}
# Binding auto-created groups to their applications
resource "authentik_policy_binding" "auto_group_bindings" {
for_each = {
for k, v in var.proxy_applications : k => v
if v.create_group == true
}
target = module.proxy_applications[each.key].application_uuid
group = authentik_group.proxy_app_groups[each.key].id
order = 100
depends_on = [
module.proxy_applications,
authentik_group.proxy_app_groups
]
}
# Binding auto-created groups to their OAuth applications
resource "authentik_policy_binding" "oauth_auto_group_bindings" {
for_each = {
for k, v in var.oauth_applications : k => v
if v.create_group == true
}
target = module.oauth_applications[each.key].application_uuid
group = authentik_group.oauth_app_groups[each.key].id
order = 100
depends_on = [
module.oauth_applications,
authentik_group.oauth_app_groups
]
}
module "saml_applications" {
source = "./modules/saml-provider"
for_each = var.saml_applications
name = each.value.name
app_name = each.value.name
app_slug = each.value.slug
app_group = each.value.group
authorization_flow = try(authentik_flow.flows[each.value.authorization_flow].id, data.authentik_flow.default_authorization_flow.id)
invalidation_flow = data.authentik_flow.default_invalidation_flow.id
acs_url = each.value.acs_url
issuer = each.value.issuer
audience = each.value.audience
sp_binding = each.value.sp_binding
signing_key = each.value.signing_key
property_mappings = [for pm in each.value.property_mappings : authentik_property_mapping_provider_saml.saml_mappings[pm].id]
name_id_mapping = each.value.name_id_mapping != null ? authentik_property_mapping_provider_saml.saml_mappings[each.value.name_id_mapping].id : null
assertion_valid_not_before = each.value.assertion_valid_not_before
assertion_valid_not_on_or_after = each.value.assertion_valid_not_on_or_after
session_valid_not_on_or_after = each.value.session_valid_not_on_or_after
policy_engine_mode = each.value.policy_engine_mode
meta_description = each.value.meta_description
meta_launch_url = each.value.meta_launch_url
meta_icon = each.value.meta_icon
} }
locals { locals {

View File

@@ -11,6 +11,30 @@ terraform {
} }
} }
# Get all available scope mappings
data "authentik_property_mapping_provider_scope" "all_scopes" {
managed_list = [
"goauthentik.io/providers/oauth2/scope-email",
"goauthentik.io/providers/oauth2/scope-openid",
"goauthentik.io/providers/oauth2/scope-profile"
]
}
# Filter scope mappings based on requested scopes
locals {
scope_name_mapping = {
"openid" = "goauthentik.io/providers/oauth2/scope-openid"
"profile" = "goauthentik.io/providers/oauth2/scope-profile"
"email" = "goauthentik.io/providers/oauth2/scope-email"
}
selected_scope_ids = [
for scope in var.scope_mappings :
data.authentik_property_mapping_provider_scope.all_scopes.ids[index(data.authentik_property_mapping_provider_scope.all_scopes.managed_list, local.scope_name_mapping[scope])]
if contains(keys(local.scope_name_mapping), scope)
]
}
resource "random_password" "client_secret" { resource "random_password" "client_secret" {
count = var.client_secret == null ? 1 : 0 count = var.client_secret == null ? 1 : 0
length = 40 length = 40
@@ -25,8 +49,19 @@ resource "authentik_provider_oauth2" "provider" {
authorization_flow = var.authorization_flow authorization_flow = var.authorization_flow
invalidation_flow = var.invalidation_flow invalidation_flow = var.invalidation_flow
include_claims_in_id_token = var.include_claims_in_id_token include_claims_in_id_token = var.include_claims_in_id_token
access_code_validity = var.access_code_validity
access_token_validity = var.access_token_validity
refresh_token_validity = var.refresh_token_validity
signing_key = var.signing_key
property_mappings = var.property_mappings allowed_redirect_uris = [
for uri in var.redirect_uris : {
matching_mode = "strict"
url = uri
}
]
property_mappings = length(var.property_mappings) > 0 ? var.property_mappings : local.selected_scope_ids
} }
resource "random_id" "client_id" { resource "random_id" "client_id" {
@@ -57,3 +92,12 @@ resource "authentik_policy_binding" "app_access" {
negate = lookup(each.value, "negate", false) negate = lookup(each.value, "negate", false)
failure_result = lookup(each.value, "failure_result", true) failure_result = lookup(each.value, "failure_result", true)
} }
# Binding groups to the application
resource "authentik_policy_binding" "group_bindings" {
for_each = { for idx, group_id in var.access_groups : idx => group_id }
target = authentik_application.app.uuid
group = each.value
order = 10 + each.key
}

View File

@@ -10,7 +10,7 @@ output "application_id" {
output "application_uuid" { output "application_uuid" {
description = "UUID of the application" description = "UUID of the application"
value = authentik_application.app.id value = authentik_application.app.uuid
} }
output "client_id" { output "client_id" {

View File

@@ -136,3 +136,15 @@ variable "access_policies" {
})) }))
default = {} default = {}
} }
variable "access_groups" {
description = "List of group IDs that have access to the application"
type = list(string)
default = []
}
variable "scope_mappings" {
description = "List of scope mappings for the OAuth provider"
type = list(string)
default = ["openid", "profile", "email"]
}

View File

@@ -47,3 +47,12 @@ resource "authentik_policy_binding" "app_access" {
negate = lookup(each.value, "negate", false) negate = lookup(each.value, "negate", false)
failure_result = lookup(each.value, "failure_result", true) failure_result = lookup(each.value, "failure_result", true)
} }
# Binding groups to the application
resource "authentik_policy_binding" "group_bindings" {
for_each = { for idx, group_id in var.access_groups : idx => group_id }
target = authentik_application.app.uuid
group = each.value
order = 10 + each.key
}

View File

@@ -10,7 +10,7 @@ output "application_id" {
output "application_uuid" { output "application_uuid" {
description = "UUID of the application" description = "UUID of the application"
value = authentik_application.app.id value = authentik_application.app.uuid
} }
output "application_slug" { output "application_slug" {

View File

@@ -143,3 +143,9 @@ variable "access_policies" {
})) }))
default = {} default = {}
} }
variable "access_groups" {
description = "List of group IDs that have access to the application"
type = list(string)
default = []
}

View File

@@ -0,0 +1,53 @@
terraform {
required_providers {
authentik = {
source = "goauthentik/authentik"
version = ">= 2023.10.0"
}
}
}
data "authentik_certificate_key_pair" "default" {
name = "authentik Self-signed Certificate"
}
resource "authentik_provider_saml" "provider" {
name = var.name
authorization_flow = var.authorization_flow
invalidation_flow = var.invalidation_flow
acs_url = var.acs_url
issuer = var.issuer
audience = var.audience
sp_binding = var.sp_binding
signing_kp = var.signing_key != null ? var.signing_key : data.authentik_certificate_key_pair.default.id
property_mappings = var.property_mappings
name_id_mapping = var.name_id_mapping
assertion_valid_not_before = var.assertion_valid_not_before
assertion_valid_not_on_or_after = var.assertion_valid_not_on_or_after
session_valid_not_on_or_after = var.session_valid_not_on_or_after
}
resource "authentik_application" "app" {
name = var.app_name
slug = var.app_slug
protocol_provider = authentik_provider_saml.provider.id
group = var.app_group
policy_engine_mode = var.policy_engine_mode
meta_description = var.meta_description
meta_launch_url = var.meta_launch_url
meta_icon = var.meta_icon
}
resource "authentik_policy_binding" "app_access" {
for_each = var.access_policies
target = authentik_application.app.id
policy = each.value.policy_id
order = each.value.order
enabled = lookup(each.value, "enabled", true)
timeout = lookup(each.value, "timeout", 30)
negate = lookup(each.value, "negate", false)
failure_result = lookup(each.value, "failure_result", true)
}

View File

@@ -0,0 +1,24 @@
output "provider_id" {
description = "ID of the SAML provider"
value = authentik_provider_saml.provider.id
}
output "application_id" {
description = "ID of the application"
value = authentik_application.app.id
}
output "provider_name" {
description = "Name of the SAML provider"
value = authentik_provider_saml.provider.name
}
output "acs_url" {
description = "Assertion Consumer Service URL"
value = authentik_provider_saml.provider.acs_url
}
output "issuer" {
description = "SAML Issuer"
value = authentik_provider_saml.provider.issuer
}

View File

@@ -0,0 +1,124 @@
variable "name" {
description = "Name of the SAML provider"
type = string
}
variable "app_name" {
description = "Name of the application"
type = string
}
variable "app_slug" {
description = "Slug of the application"
type = string
}
variable "app_group" {
description = "Group of the application"
type = string
default = ""
}
variable "authorization_flow" {
description = "Authorization flow ID"
type = string
}
variable "invalidation_flow" {
description = "Invalidation flow ID"
type = string
}
variable "acs_url" {
description = "Assertion Consumer Service URL"
type = string
}
variable "issuer" {
description = "SAML Issuer"
type = string
}
variable "audience" {
description = "SAML Audience"
type = string
}
variable "sp_binding" {
description = "Service Provider binding (post or redirect)"
type = string
default = "post"
}
variable "signing_key" {
description = "Certificate key pair ID for signing"
type = string
default = null
}
variable "property_mappings" {
description = "List of property mapping IDs"
type = list(string)
default = []
}
variable "name_id_mapping" {
description = "Property mapping ID for NameID"
type = string
default = null
}
variable "assertion_valid_not_before" {
description = "Assertion valid not before"
type = string
default = "minutes=-5"
}
variable "assertion_valid_not_on_or_after" {
description = "Assertion valid not on or after"
type = string
default = "minutes=5"
}
variable "session_valid_not_on_or_after" {
description = "Session valid not on or after"
type = string
default = "minutes=86400"
}
variable "policy_engine_mode" {
description = "Policy engine mode"
type = string
default = "all"
}
variable "meta_description" {
description = "Application description"
type = string
default = ""
}
variable "meta_launch_url" {
description = "Application launch URL"
type = string
default = ""
}
variable "meta_icon" {
description = "Application icon URL"
type = string
default = ""
}
variable "access_policies" {
description = "Access policies for the application"
type = map(object({
policy_id = string
order = number
enabled = optional(bool, true)
timeout = optional(number, 30)
negate = optional(bool, false)
failure_result = optional(bool, true)
}))
default = {}
}

View File

@@ -38,12 +38,36 @@ output "outposts" {
output "groups" { output "groups" {
description = "Groups details" description = "Groups details"
value = { value = merge(
for k, v in authentik_group.groups : k => { {
id = v.id for k, v in authentik_group.root_groups : k => {
name = v.name id = v.id
name = v.name
}
},
{
for k, v in authentik_group.child_groups : k => {
id = v.id
name = v.name
}
},
{
for k, v in authentik_group.proxy_app_groups : k => {
id = v.id
name = v.name
auto_created = true
type = "proxy"
}
},
{
for k, v in authentik_group.oauth_app_groups : k => {
id = v.id
name = v.name
auto_created = true
type = "oauth"
}
} }
} )
} }
output "flows" { output "flows" {

View File

@@ -0,0 +1,8 @@
terraform {
cloud {
organization = "ultradesu"
workspaces {
name = "Authentik"
}
}
}

View File

@@ -19,6 +19,9 @@ variable "oauth_applications" {
authorization_flow = optional(string, null) authorization_flow = optional(string, null)
signing_key = optional(string, null) signing_key = optional(string, null)
outpost = optional(string, null) outpost = optional(string, null)
create_group = optional(bool, false)
access_groups = optional(list(string), [])
scope_mappings = optional(list(string), ["openid", "profile", "email"])
})) }))
default = {} default = {}
} }
@@ -45,6 +48,33 @@ variable "proxy_applications" {
authorization_flow = optional(string, null) authorization_flow = optional(string, null)
skip_path_regex = optional(string, "") skip_path_regex = optional(string, "")
outpost = optional(string, null) outpost = optional(string, null)
create_group = optional(bool, false)
access_groups = optional(list(string), [])
}))
default = {}
}
variable "saml_applications" {
description = "Map of SAML applications"
type = map(object({
name = string
slug = string
group = optional(string, "")
policy_engine_mode = optional(string, "all")
meta_description = optional(string, "")
meta_launch_url = optional(string, "")
meta_icon = optional(string, "")
acs_url = string
issuer = string
audience = string
sp_binding = optional(string, "post")
signing_key = optional(string, null)
property_mappings = optional(list(string), [])
name_id_mapping = optional(string, null)
assertion_valid_not_before = optional(string, "minutes=-5")
assertion_valid_not_on_or_after = optional(string, "minutes=5")
session_valid_not_on_or_after = optional(string, "minutes=86400")
authorization_flow = optional(string, null)
})) }))
default = {} default = {}
} }