diff --git a/stacks/authentik/authentik_provider.tf b/stacks/authentik/authentik_provider.tf new file mode 100644 index 00000000..e9db3985 --- /dev/null +++ b/stacks/authentik/authentik_provider.tf @@ -0,0 +1,59 @@ +# goauthentik/authentik Terraform provider. +# +# Adopted 2026-04-18 (Wave 6a of the state-drift consolidation plan) to bring +# the catch-all Proxy Provider — previously managed only via the Authentik UI +# — under Terraform management. API token lives in Vault +# `secret/authentik/tf_api_token` (token identifier `terraform-infra-stack`, +# intent API, user akadmin, no expiry). Required-providers declaration sits +# in the central terragrunt.hcl so every stack has it available; only this +# stack configures a provider block. + +data "vault_kv_secret_v2" "authentik_tf" { + mount = "secret" + name = "authentik" +} + +provider "authentik" { + url = "https://authentik.viktorbarzin.me" + token = data.vault_kv_secret_v2.authentik_tf.data["tf_api_token"] +} + +data "authentik_flow" "default_authorization_implicit_consent" { + slug = "default-provider-authorization-implicit-consent" +} + +data "authentik_flow" "default_provider_invalidation" { + slug = "default-provider-invalidation-flow" +} + +# ----------------------------------------------------------------------------- +# Catch-all Proxy Provider + Application. +# +# Created via the Authentik UI ~a year ago; adopted into Terraform 2026-04-18 +# (Wave 6a). The proxy provider is consumed by the embedded outpost +# (uuid 0eecac07-97c7-443c-8925-05f2f4fe3e47) via an outpost-level binding +# that stays in the UI — it's a single toggle with no drift risk. +# ----------------------------------------------------------------------------- + +resource "authentik_application" "catchall" { + name = "Domain wide catch all" + slug = "domain-wide-catch-all" + protocol_provider = authentik_provider_proxy.catchall.id + lifecycle { + ignore_changes = [meta_description, meta_launch_url, meta_icon, group, backchannel_providers, policy_engine_mode, open_in_new_tab] + } +} + +resource "authentik_provider_proxy" "catchall" { + name = "Provider for Domain wide catch all" + mode = "forward_domain" + external_host = "https://authentik.viktorbarzin.me" + cookie_domain = "viktorbarzin.me" + # Flow UUIDs resolved dynamically so a flow re-creation (keeping the slug) + # doesn't require an HCL edit. + authorization_flow = data.authentik_flow.default_authorization_implicit_consent.id + invalidation_flow = data.authentik_flow.default_provider_invalidation.id + lifecycle { + ignore_changes = [property_mappings, jwt_federation_sources, skip_path_regex, internal_host, basic_auth_enabled, basic_auth_password_attribute, basic_auth_username_attribute, intercept_header_auth, access_token_validity] + } +} diff --git a/stacks/calico/main.tf b/stacks/calico/main.tf new file mode 100644 index 00000000..79bc756b --- /dev/null +++ b/stacks/calico/main.tf @@ -0,0 +1,67 @@ +# Calico CNI +# +# Calico has underpinned this cluster's pod networking since 2024-07-30, installed +# as raw kubectl manifests (tigera-operator Deployment + CRDs + Installation CR). +# Bringing the full stack under Terraform is high-blast — the operator and its +# Deployment must never flap during node pressure or during any apply, because +# new pod scheduling breaks within ~seconds of a CNI outage. +# +# This stack (created 2026-04-18 Wave 5b) adopts the three namespaces only: +# calico-system, calico-apiserver, tigera-operator. The `tigera-operator` +# Deployment, the 20+ CRDs it manages, and the `Installation` CR itself are +# intentionally *not* adopted yet — they require a low-traffic window and a +# careful ignore_changes set to cover operator-generated defaults on the +# Installation CR. Follow-up tracked in beads code-3ad. +# +# The namespaces are safe to adopt (no networking impact — they're just label +# containers) and give TF an audit trail entry for the labels/tier Kyverno +# cares about. + +resource "kubernetes_namespace" "calico_system" { + metadata { + name = "calico-system" + labels = { + name = "calico-system" + } + } + lifecycle { + # KYVERNO_LIFECYCLE_V1: goldilocks-vpa-auto-mode label on every namespace. + # pod-security.kubernetes.io/* labels are applied by the tigera-operator + # reconciler on calico-system + calico-apiserver for PSA 'privileged'. + ignore_changes = [ + metadata[0].labels["goldilocks.fairwinds.com/vpa-update-mode"], + metadata[0].labels["pod-security.kubernetes.io/enforce"], + metadata[0].labels["pod-security.kubernetes.io/enforce-version"], + ] + } +} + +resource "kubernetes_namespace" "calico_apiserver" { + metadata { + name = "calico-apiserver" + labels = { + name = "calico-apiserver" + } + } + lifecycle { + # KYVERNO_LIFECYCLE_V1 + PSA labels applied by tigera-operator (see calico_system). + ignore_changes = [ + metadata[0].labels["goldilocks.fairwinds.com/vpa-update-mode"], + metadata[0].labels["pod-security.kubernetes.io/enforce"], + metadata[0].labels["pod-security.kubernetes.io/enforce-version"], + ] + } +} + +resource "kubernetes_namespace" "tigera_operator" { + metadata { + name = "tigera-operator" + labels = { + name = "tigera-operator" + } + } + lifecycle { + # KYVERNO_LIFECYCLE_V1: goldilocks-vpa-auto-mode ClusterPolicy stamps this label on every namespace + ignore_changes = [metadata[0].labels["goldilocks.fairwinds.com/vpa-update-mode"]] + } +} diff --git a/stacks/calico/secrets b/stacks/calico/secrets new file mode 120000 index 00000000..ca54a7cf --- /dev/null +++ b/stacks/calico/secrets @@ -0,0 +1 @@ +../../secrets \ No newline at end of file diff --git a/stacks/calico/terragrunt.hcl b/stacks/calico/terragrunt.hcl new file mode 100644 index 00000000..eb956424 --- /dev/null +++ b/stacks/calico/terragrunt.hcl @@ -0,0 +1,6 @@ +include "root" { + path = find_in_parent_folders() +} + +# No platform dependency — Calico provides the cluster network the rest +# of the platform runs on. This stack must not introduce a dep cycle. diff --git a/stacks/monitoring/modules/monitoring/grafana.tf b/stacks/monitoring/modules/monitoring/grafana.tf index a3b61556..c82eee6b 100644 --- a/stacks/monitoring/modules/monitoring/grafana.tf +++ b/stacks/monitoring/modules/monitoring/grafana.tf @@ -134,6 +134,7 @@ locals { # Applications "qbittorrent.json" = "Applications" "realestate-crawler.json" = "Applications" + "uk-payslip.json" = "Finance" } } diff --git a/terragrunt.hcl b/terragrunt.hcl index d11f8b93..0376f6ed 100644 --- a/terragrunt.hcl +++ b/terragrunt.hcl @@ -62,6 +62,10 @@ terraform { source = "cloudflare/cloudflare" version = "~> 4" } + authentik = { + source = "goauthentik/authentik" + version = "~> 2024.10" + } } }