resource "kubernetes_namespace" "reverse_proxy" { metadata { annotations = { name = "reverse-proxy" } labels = { name = "reverse-proxy" } name = "reverse-proxy" } } resource "kubernetes_service_account" "reverse-proxy_service_account" { metadata { name = "reverse-proxy-app-sa" namespace = kubernetes_namespace.reverse_proxy.metadata[0].name } automount_service_account_token = false } resource "kubernetes_service" "reverse-proxy" { for_each = var.services metadata { name = format("reverse-proxy-%s", replace(each.key, ".", "-")) namespace = kubernetes_namespace.reverse_proxy.metadata[0].name } spec { port { name = "http" port = 80 target_port = substr(format("http-%s", replace(each.key, ".", "-")), 0, 15) } dynamic "port" { for_each = each.value.tls ? [1] : [] content { name = "https" port = 443 target_port = substr(format("https-%s", replace(each.key, ".", "-")), 0, 15) } } selector = { app = "reverse-proxy" } cluster_ip = "None" } } resource "kubernetes_manifest" "internal_cert" { manifest = { apiVersion = "cert-manager.io/v1" kind = "Certificate" metadata = { name = "internal-cert" namespace = kubernetes_namespace.reverse_proxy.metadata[0].name } spec = { secretName = "internal-cert" issuerRef = { name = var.issuer_name kind = "ClusterIssuer" } commonName = "reverse-proxy.internal" dnsNames = flatten([for _, v in var.services : v.fqdn if v.tls == true]) } } } resource "kubernetes_config_map" "wireguard_config" { count = local.wireguard_config != "null" ? 1 : 0 metadata { name = "wireguard-config" namespace = kubernetes_namespace.reverse_proxy.metadata[0].name } data = { "wireguard.conf" = local.wireguard_config } } resource "kubernetes_config_map" "nginx_config" { metadata { name = "nginx-config" namespace = kubernetes_namespace.reverse_proxy.metadata[0].name } data = { "nginx.conf" = local.nginx_config } } resource "kubernetes_config_map" "nginx_default_config" { metadata { name = "nginx-default-config" namespace = kubernetes_namespace.reverse_proxy.metadata[0].name } data = local.services_config } resource "kubernetes_config_map" "auth_script" { metadata { name = "auth-script" namespace = kubernetes_namespace.reverse_proxy.metadata[0].name } data = { "auth_server.py" = local.auth_script } } resource "kubernetes_ingress_v1" "reverse-proxy_ingress" { for_each = var.services metadata { name = format("reverse-proxy-%s", replace(each.key, ".", "-")) namespace = kubernetes_namespace.reverse_proxy.metadata[0].name annotations = merge({ "kubernetes.io/ingress.class" = "public" "cert-manager.io/cluster-issuer" = "letsencrypt-prod" }, each.value.tls ? { "nginx.org/ssl-services" = format("reverse-proxy-%s", replace(each.key, ".", "-")) "nginx.org/ssl-redirect" = "true" "nginx.org/redirect-to-https" = "true" "nginx.ingress.kubernetes.io/backend-protocol" = "HTTPS" } : {}) } spec { tls { hosts = each.value.fqdn secret_name = format("reverse-proxy-%s-tls", replace(each.key, ".", "-")) } dynamic "rule" { for_each = each.value.fqdn content { host = rule.value http { path { backend { service { name = format("reverse-proxy-%s", replace(each.key, ".", "-")) port { name = each.value.tls ? "https" : "http" } } } } } } } } } resource "kubernetes_secret" "auth_token" { metadata { name = "auth-token-secret" namespace = kubernetes_namespace.reverse_proxy.metadata[0].name } data = { token = var.auth_token } type = "Opaque" } resource "kubernetes_deployment" "reverse_proxy" { metadata { name = "reverse-proxy" namespace = kubernetes_namespace.reverse_proxy.metadata[0].name annotations = { "security.alpha.kubernetes.io/unsafe-sysctls" = "net.ipv4.conf.all.src_valid_mark=1" } } spec { replicas = 1 selector { match_labels = { app = "reverse-proxy" } } strategy { type = "Recreate" } template { metadata { labels = { app = "reverse-proxy" } annotations = { "checksum/nginx" = local.nginx_config_checksum "checksum/services" = local.services_config_checksum "checksum/wireguard" = local.wireguard_config_checksum "checksum/auth_server" = local.auth_script_checksum } } spec { dynamic "container" { for_each = local.wireguard_config != "null" ? [1] : [] content { name = "wireguard" image = "linuxserver/wireguard:${local.wireguard_version}" resources { requests = { memory = "64Mi" } } liveness_probe { exec { command = [ "/bin/sh", "-c", "wg show | grep -q transfer" ] } initial_delay_seconds = 90 period_seconds = 90 } security_context { privileged = true capabilities { add = ["NET_ADMIN"] } } env { name = "PUID" value = "1000" } env { name = "PGID" value = "1000" } env { name = "TZ" value = "Europe/Berlin" } volume_mount { name = "wireguard-config" mount_path = "/config/wg_confs" read_only = true } volume_mount { name = "lib-modules" mount_path = "/lib/modules" read_only = true } } } container { name = "auth-sidecar" image = "python:3.11-slim" command = ["python", "/app/auth_server.py"] env { name = "AUTH_TOKEN" value_from { secret_key_ref { name = "auth-token-secret" key = "token" } } } volume_mount { name = "auth-script" mount_path = "/app" read_only = true } port { container_port = 5000 name = "auth" } } container { name = "reverse-proxy" image = "nginx:${var.tag}" image_pull_policy = "Always" resources { requests = { memory = "100Mi" } } readiness_probe { http_get { path = "/" port = 8080 } initial_delay_seconds = 5 period_seconds = 10 } liveness_probe { http_get { path = "/" port = 8080 } initial_delay_seconds = 5 period_seconds = 10 } security_context { allow_privilege_escalation = false } dynamic "port" { for_each = { for k, v in var.services : k => v.http_port } content { container_port = port.value name = substr(format("http-%s", replace(port.key, ".", "-")), 0, 15) } } dynamic "port" { for_each = { for k, v in var.services : k => v.https_port if v.tls } content { container_port = port.value name = substr(format("https-%s", replace(port.key, ".", "-")), 0, 15) } } volume_mount { name = "nginx" mount_path = "/etc/nginx/nginx.conf" sub_path = "nginx.conf" read_only = true } volume_mount { name = "services" mount_path = "/etc/nginx/conf.d/" read_only = true } dynamic "volume_mount" { for_each = length([for v in var.services : v.tls if v.tls == true]) > 0 ? [1] : [] content { name = "tls" mount_path = "/etc/ssl/certs/private" read_only = true } } } dynamic "volume" { for_each = local.wireguard_config != "null" ? [1] : [] content { name = "wireguard-config" config_map { name = kubernetes_config_map.wireguard_config[0].metadata[0].name } } } dynamic "volume" { for_each = local.wireguard_config != "null" ? [1] : [] content { name = "lib-modules" host_path { path = "/lib/modules" } } } volume { name = "auth-script" config_map { name = kubernetes_config_map.auth_script.metadata[0].name } } volume { name = "nginx" config_map { name = kubernetes_config_map.nginx_config.metadata[0].name } } volume { name = "services" config_map { name = kubernetes_config_map.nginx_default_config.metadata[0].name } } dynamic "volume" { for_each = length([for v in var.services : v.tls if v.tls == true]) > 0 ? [1] : [] content { name = "tls" secret { secret_name = kubernetes_manifest.internal_cert.manifest["metadata"]["name"] items { key = "tls.crt" path = "tls.crt" } items { key = "tls.key" path = "tls.key" } } } } } } } lifecycle { ignore_changes = [spec[0].template[0].metadata[0].annotations["kubectl.kubernetes.io/restartedAt"]] } }