From 1acf8cc4e8f2ac5c3b4c200e1a1ec7aff7e0ca16 Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Sun, 15 Mar 2026 19:05:04 +0000 Subject: [PATCH] migrate consuming stacks to ESO + remove k8s-dashboard static token MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 9: ExternalSecret migration across 26 stacks: Fully migrated (vault data source removed, ESO delivers secrets): - speedtest, shadowsocks, wealthfolio, plotting-book, f1-stream, tandoor - n8n, dawarich, diun, netbox, onlyoffice, tuya-bridge - hackmd (ESO template for DB URL), health (ESO template for DB URL) - trading-bot (ESO template for DATABASE_URL + 7 secret env vars) - forgejo (removed unused vault data source) Partially migrated (vault kept for plan-time, ESO added for runtime): - immich, linkwarden, nextcloud, paperless-ngx (jsondecode for homepage) - claude-memory, rybbit, url, webhook_handler (plan-time in locals/jobs) - woodpecker, openclaw, resume (plan-time in helm values/jobs/modules) 17 stacks unchanged (all plan-time: homepage annotations, configmaps, module inputs) — vault data source works with OIDC auth. Phase 17a: Remove k8s-dashboard static admin token secret. Users now get tokens via: vault write kubernetes/creds/dashboard-admin --- stacks/affine/providers.tf | 7 - stacks/claude-memory/main.tf | 39 +++++- stacks/dawarich/main.tf | 41 +++++- stacks/diun/main.tf | 41 +++++- stacks/f1-stream/main.tf | 44 +++++- stacks/forgejo/main.tf | 5 - stacks/hackmd/main.tf | 47 ++++++- stacks/hackmd/providers.tf | 7 - stacks/hackmd/terragrunt.hcl | 5 - stacks/health/main.tf | 64 +++++++-- stacks/health/providers.tf | 7 - stacks/health/terragrunt.hcl | 5 - stacks/immich/main.tf | 67 ++++++++- stacks/k8s-dashboard/main.tf | 12 +- stacks/k8s-dashboard/providers.tf | 19 ++- stacks/linkwarden/main.tf | 45 ++++++- stacks/linkwarden/providers.tf | 7 - stacks/n8n/main.tf | 44 +++++- stacks/netbox/main.tf | 50 +++++-- stacks/nextcloud/main.tf | 36 ++++- stacks/onlyoffice/main.tf | 53 ++++++-- stacks/onlyoffice/providers.tf | 7 - stacks/openclaw/main.tf | 75 +++++++++-- stacks/paperless-ngx/main.tf | 36 ++++- .../modules/kyverno/resource-governance.tf | 88 ++++++++++++ stacks/platform/providers.tf | 7 - stacks/plotting-book/main.tf | 44 +++++- stacks/resume/main.tf | 43 +++++- stacks/rybbit/main.tf | 60 ++++++++- stacks/shadowsocks/main.tf | 43 ++++-- stacks/speedtest/main.tf | 44 +++++- stacks/stirling-pdf/providers.tf | 7 - stacks/tandoor/main.tf | 45 ++++++- stacks/trading-bot/main.tf | 127 ++++++++++++++---- stacks/trading-bot/terragrunt.hcl | 4 +- stacks/tuya-bridge/main.tf | 71 ++++++++-- stacks/url/main.tf | 39 +++++- stacks/wealthfolio/main.tf | 44 +++++- stacks/webhook_handler/main.tf | 83 ++++++++++-- stacks/woodpecker/main.tf | 27 ++++ stacks/woodpecker/values.yaml | 4 + 41 files changed, 1278 insertions(+), 265 deletions(-) diff --git a/stacks/affine/providers.tf b/stacks/affine/providers.tf index f4845cc8..860c9eba 100644 --- a/stacks/affine/providers.tf +++ b/stacks/affine/providers.tf @@ -13,12 +13,6 @@ variable "kube_config_path" { default = "~/.kube/config" } -variable "vault_root_token" { - type = string - sensitive = true - default = "" -} - provider "kubernetes" { config_path = var.kube_config_path } @@ -31,6 +25,5 @@ provider "helm" { provider "vault" { address = "https://vault.viktorbarzin.me" - token = var.vault_root_token skip_child_token = true } diff --git a/stacks/claude-memory/main.tf b/stacks/claude-memory/main.tf index 0e4f1708..22ae49d7 100644 --- a/stacks/claude-memory/main.tf +++ b/stacks/claude-memory/main.tf @@ -22,6 +22,33 @@ resource "kubernetes_namespace" "claude-memory" { } } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "claude-memory-secrets" + namespace = "claude-memory" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "claude-memory-secrets" + } + dataFrom = [{ + extract = { + key = "claude-memory" + } + }] + } + } + depends_on = [kubernetes_namespace.claude-memory] +} + module "tls_secret" { source = "../../modules/kubernetes/setup_tls_secret" namespace = kubernetes_namespace.claude-memory.metadata[0].name @@ -74,6 +101,9 @@ resource "kubernetes_deployment" "claude-memory" { app = "claude-memory" tier = local.tiers.aux } + annotations = { + "reloader.stakater.com/auto" = "true" + } } spec { replicas = 2 @@ -114,8 +144,13 @@ resource "kubernetes_deployment" "claude-memory" { value = "postgresql://claude_memory:${data.vault_kv_secret_v2.secrets.data["db_password"]}@${var.postgresql_host}:5432/claude_memory" } env { - name = "API_KEY" - value = data.vault_kv_secret_v2.secrets.data["api_key"] + name = "API_KEY" + value_from { + secret_key_ref { + name = "claude-memory-secrets" + key = "api_key" + } + } } startup_probe { diff --git a/stacks/dawarich/main.tf b/stacks/dawarich/main.tf index 98579463..9c2897f3 100644 --- a/stacks/dawarich/main.tf +++ b/stacks/dawarich/main.tf @@ -11,11 +11,6 @@ variable "nfs_server" { type = string } variable "redis_host" { type = string } variable "postgresql_host" { type = string } -data "vault_kv_secret_v2" "secrets" { - mount = "secret" - name = "dawarich" -} - resource "kubernetes_namespace" "dawarich" { metadata { name = "dawarich" @@ -26,6 +21,33 @@ resource "kubernetes_namespace" "dawarich" { } } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "dawarich-secrets" + namespace = "dawarich" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "dawarich-secrets" + } + dataFrom = [{ + extract = { + key = "dawarich" + } + }] + } + } + depends_on = [kubernetes_namespace.dawarich] +} + module "tls_secret" { source = "../../modules/kubernetes/setup_tls_secret" namespace = kubernetes_namespace.dawarich.metadata[0].name @@ -92,8 +114,13 @@ resource "kubernetes_deployment" "dawarich" { value = "dawarich" } env { - name = "DATABASE_PASSWORD" - value = data.vault_kv_secret_v2.secrets.data["db_password"] + name = "DATABASE_PASSWORD" + value_from { + secret_key_ref { + name = "dawarich-secrets" + key = "db_password" + } + } } env { name = "DATABASE_NAME" diff --git a/stacks/diun/main.tf b/stacks/diun/main.tf index 1b45621d..4b1f3647 100644 --- a/stacks/diun/main.tf +++ b/stacks/diun/main.tf @@ -4,11 +4,6 @@ variable "tls_secret_name" { } variable "nfs_server" { type = string } -data "vault_kv_secret_v2" "secrets" { - mount = "secret" - name = "diun" -} - resource "kubernetes_namespace" "diun" { metadata { name = "diun" @@ -19,6 +14,33 @@ resource "kubernetes_namespace" "diun" { } } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "diun-secrets" + namespace = "diun" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "diun-secrets" + } + dataFrom = [{ + extract = { + key = "diun" + } + }] + } + } + depends_on = [kubernetes_namespace.diun] +} + module "tls_secret" { source = "../../modules/kubernetes/setup_tls_secret" namespace = kubernetes_namespace.diun.metadata[0].name @@ -156,8 +178,13 @@ resource "kubernetes_deployment" "diun" { # value = data.vault_kv_secret_v2.secrets.data["nfty_token"] # } env { - name = "DIUN_NOTIF_SLACK_WEBHOOKURL" - value = data.vault_kv_secret_v2.secrets.data["slack_url"] + name = "DIUN_NOTIF_SLACK_WEBHOOKURL" + value_from { + secret_key_ref { + name = "diun-secrets" + key = "slack_url" + } + } } env { name = "LOG_LEVEL" diff --git a/stacks/f1-stream/main.tf b/stacks/f1-stream/main.tf index e71cb00c..fd6f86a3 100644 --- a/stacks/f1-stream/main.tf +++ b/stacks/f1-stream/main.tf @@ -6,11 +6,6 @@ variable "nfs_server" { type = string } variable "discord_f1_guild_id" { type = string } variable "discord_f1_channel_ids" { type = string } -data "vault_kv_secret_v2" "secrets" { - mount = "secret" - name = "f1-stream" -} - resource "kubernetes_namespace" "f1-stream" { metadata { name = "f1-stream" @@ -21,6 +16,33 @@ resource "kubernetes_namespace" "f1-stream" { } } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "f1-stream-secrets" + namespace = "f1-stream" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "f1-stream-secrets" + } + dataFrom = [{ + extract = { + key = "f1-stream" + } + }] + } + } + depends_on = [kubernetes_namespace.f1-stream] +} + module "nfs_data" { source = "../../modules/kubernetes/nfs_volume" name = "f1-stream-data" @@ -37,6 +59,9 @@ resource "kubernetes_deployment" "f1-stream" { app = "f1-stream" tier = local.tiers.aux } + annotations = { + "reloader.stakater.com/auto" = "true" + } } spec { replicas = 0 # Scaled down for cluster stability — periodic scans cause memory pressure @@ -69,8 +94,13 @@ resource "kubernetes_deployment" "f1-stream" { container_port = 8000 } env { - name = "DISCORD_TOKEN" - value = data.vault_kv_secret_v2.secrets.data["discord_user_token"] + name = "DISCORD_TOKEN" + value_from { + secret_key_ref { + name = "f1-stream-secrets" + key = "discord_user_token" + } + } } env { name = "DISCORD_CHANNELS" diff --git a/stacks/forgejo/main.tf b/stacks/forgejo/main.tf index 7c94ea34..f1c4f7e6 100644 --- a/stacks/forgejo/main.tf +++ b/stacks/forgejo/main.tf @@ -4,11 +4,6 @@ variable "tls_secret_name" { } variable "nfs_server" { type = string } -data "vault_kv_secret_v2" "secrets" { - mount = "secret" - name = "forgejo" -} - resource "kubernetes_namespace" "forgejo" { metadata { name = "forgejo" diff --git a/stacks/hackmd/main.tf b/stacks/hackmd/main.tf index 77262762..e221c238 100644 --- a/stacks/hackmd/main.tf +++ b/stacks/hackmd/main.tf @@ -4,11 +4,6 @@ variable "tls_secret_name" { variable "nfs_server" { type = string } variable "mysql_host" { type = string } -data "vault_kv_secret_v2" "secrets" { - mount = "secret" - name = "hackmd" -} - resource "kubernetes_namespace" "hackmd" { metadata { name = "hackmd" @@ -42,6 +37,9 @@ resource "kubernetes_deployment" "hackmd" { "kubernetes.io/cluster-service" = "true" tier = local.tiers.edge } + annotations = { + "reloader.stakater.com/auto" = "true" + } } spec { replicas = 1 @@ -102,8 +100,12 @@ resource "kubernetes_deployment" "hackmd" { image = "hackmdio/hackmd" env { name = "CMD_DB_URL" - # value = format("%s%s%s", "postgres://codimd:", var.hackmd_db_password, "@localhost/codimd") - value = format("%s%s%s", "mysql://codimd:", data.vault_kv_secret_v2.secrets.data["db_password"], "@${var.mysql_host}/codimd") + value_from { + secret_key_ref { + name = "hackmd-secrets" + key = "CMD_DB_URL" + } + } } env { name = "CMD_USECDN" @@ -176,3 +178,34 @@ module "ingress" { "gethomepage.dev/pod-selector" = "" } } + +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "hackmd-secrets" + namespace = "hackmd" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "hackmd-secrets" + template = { + data = { + CMD_DB_URL = "mysql://codimd:{{ .db_password }}@mysql.dbaas.svc.cluster.local/codimd" + } + } + } + data = [{ + secretKey = "db_password" + remoteRef = { key = "hackmd", property = "db_password" } + }] + } + } + depends_on = [kubernetes_namespace.hackmd] +} diff --git a/stacks/hackmd/providers.tf b/stacks/hackmd/providers.tf index f4845cc8..860c9eba 100644 --- a/stacks/hackmd/providers.tf +++ b/stacks/hackmd/providers.tf @@ -13,12 +13,6 @@ variable "kube_config_path" { default = "~/.kube/config" } -variable "vault_root_token" { - type = string - sensitive = true - default = "" -} - provider "kubernetes" { config_path = var.kube_config_path } @@ -31,6 +25,5 @@ provider "helm" { provider "vault" { address = "https://vault.viktorbarzin.me" - token = var.vault_root_token skip_child_token = true } diff --git a/stacks/hackmd/terragrunt.hcl b/stacks/hackmd/terragrunt.hcl index f4c920ab..0d1c8e53 100644 --- a/stacks/hackmd/terragrunt.hcl +++ b/stacks/hackmd/terragrunt.hcl @@ -6,8 +6,3 @@ dependency "platform" { config_path = "../platform" skip_outputs = true } - -dependency "vault" { - config_path = "../vault" - skip_outputs = true -} diff --git a/stacks/health/main.tf b/stacks/health/main.tf index 5f8e88d6..e12aa7b5 100644 --- a/stacks/health/main.tf +++ b/stacks/health/main.tf @@ -5,11 +5,6 @@ variable "tls_secret_name" { variable "nfs_server" { type = string } variable "postgresql_host" { type = string } -data "vault_kv_secret_v2" "secrets" { - mount = "secret" - name = "health" -} - resource "kubernetes_namespace" "health" { metadata { name = "health" @@ -41,6 +36,9 @@ resource "kubernetes_deployment" "health" { app = "health" tier = local.tiers.aux } + annotations = { + "reloader.stakater.com/auto" = "true" + } } spec { replicas = 1 @@ -65,12 +63,22 @@ resource "kubernetes_deployment" "health" { } env { - name = "DATABASE_URL" - value = "postgresql+asyncpg://health:${data.vault_kv_secret_v2.secrets.data["db_password"]}@${var.postgresql_host}:5432/health" + name = "DATABASE_URL" + value_from { + secret_key_ref { + name = "health-secrets" + key = "DATABASE_URL" + } + } } env { - name = "SECRET_KEY" - value = data.vault_kv_secret_v2.secrets.data["secret_key"] + name = "SECRET_KEY" + value_from { + secret_key_ref { + name = "health-secrets" + key = "secret_key" + } + } } env { name = "UPLOAD_DIR" @@ -151,3 +159,41 @@ module "ingress" { "gethomepage.dev/pod-selector" = "" } } + +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "health-secrets" + namespace = "health" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "health-secrets" + template = { + data = { + DATABASE_URL = "postgresql+asyncpg://health:{{ .db_password }}@postgresql.dbaas.svc.cluster.local:5432/health" + secret_key = "{{ .secret_key }}" + } + } + } + data = [ + { + secretKey = "db_password" + remoteRef = { key = "health", property = "db_password" } + }, + { + secretKey = "secret_key" + remoteRef = { key = "health", property = "secret_key" } + } + ] + } + } + depends_on = [kubernetes_namespace.health] +} diff --git a/stacks/health/providers.tf b/stacks/health/providers.tf index f4845cc8..860c9eba 100644 --- a/stacks/health/providers.tf +++ b/stacks/health/providers.tf @@ -13,12 +13,6 @@ variable "kube_config_path" { default = "~/.kube/config" } -variable "vault_root_token" { - type = string - sensitive = true - default = "" -} - provider "kubernetes" { config_path = var.kube_config_path } @@ -31,6 +25,5 @@ provider "helm" { provider "vault" { address = "https://vault.viktorbarzin.me" - token = var.vault_root_token skip_child_token = true } diff --git a/stacks/health/terragrunt.hcl b/stacks/health/terragrunt.hcl index f4c920ab..0d1c8e53 100644 --- a/stacks/health/terragrunt.hcl +++ b/stacks/health/terragrunt.hcl @@ -6,8 +6,3 @@ dependency "platform" { config_path = "../platform" skip_outputs = true } - -dependency "vault" { - config_path = "../vault" - skip_outputs = true -} diff --git a/stacks/immich/main.tf b/stacks/immich/main.tf index cb84ec15..174752f6 100644 --- a/stacks/immich/main.tf +++ b/stacks/immich/main.tf @@ -103,6 +103,33 @@ resource "kubernetes_namespace" "immich" { } } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "immich-secrets" + namespace = "immich" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "immich-secrets" + } + dataFrom = [{ + extract = { + key = "immich" + } + }] + } + } + depends_on = [kubernetes_namespace.immich] +} + resource "kubernetes_deployment" "immich_server" { metadata { name = "immich-server" @@ -112,6 +139,9 @@ resource "kubernetes_deployment" "immich_server" { app = "immich-server" tier = local.tiers.gpu } + annotations = { + "reloader.stakater.com/search" = "true" + } } lifecycle { @@ -140,8 +170,9 @@ resource "kubernetes_deployment" "immich_server" { app = "immich-server" } annotations = { - "diun.enable" = "true" - "diun.include_tags" = "^\\d+\\.\\d+\\.\\d+$" + "diun.enable" = "true" + "diun.include_tags" = "^\\d+\\.\\d+\\.\\d+$" + "reloader.stakater.com/auto" = "true" } } @@ -169,8 +200,13 @@ resource "kubernetes_deployment" "immich_server" { value = "immich" } env { - name = "DB_PASSWORD" - value = data.vault_kv_secret_v2.secrets.data["db_password"] + name = "DB_PASSWORD" + value_from { + secret_key_ref { + name = "immich-secrets" + key = "db_password" + } + } } env { name = "IMMICH_MACHINE_LEARNING_URL" @@ -331,6 +367,9 @@ resource "kubernetes_deployment" "immich-postgres" { labels = { tier = local.tiers.gpu } + annotations = { + "reloader.stakater.com/search" = "true" + } } lifecycle { @@ -365,8 +404,13 @@ resource "kubernetes_deployment" "immich-postgres" { name = "postgresql" } env { - name = "POSTGRES_PASSWORD" - value = data.vault_kv_secret_v2.secrets.data["db_password"] + name = "POSTGRES_PASSWORD" + value_from { + secret_key_ref { + name = "immich-secrets" + key = "db_password" + } + } } env { name = "POSTGRES_USER" @@ -641,13 +685,22 @@ resource "kubernetes_cron_job_v1" "postgresql-backup" { image = "postgres:16.4-bullseye" command = ["/bin/sh", "-c", <<-EOT export now=$(date +"%Y_%m_%d_%H_%M") - PGPASSWORD=${data.vault_kv_secret_v2.secrets.data["db_password"]} pg_dumpall -h immich-postgresql -U immich > /backup/dump_$now.sql + pg_dumpall -h immich-postgresql -U immich > /backup/dump_$now.sql # Rotate - delete last log file cd /backup find . -name "dump_*.sql" -type f -mtime +14 -delete # 14 day retention of backups EOT ] + env { + name = "PGPASSWORD" + value_from { + secret_key_ref { + name = "immich-secrets" + key = "db_password" + } + } + } volume_mount { name = "postgresql-backup" mount_path = "/backup" diff --git a/stacks/k8s-dashboard/main.tf b/stacks/k8s-dashboard/main.tf index 9b462641..e59bd7d8 100644 --- a/stacks/k8s-dashboard/main.tf +++ b/stacks/k8s-dashboard/main.tf @@ -131,16 +131,8 @@ resource "kubernetes_cluster_role_binding" "kubernetes-dashboard" { # depends_on = [module.dashboard] } -resource "kubernetes_secret" "kubernetes-dashboard-admin-token" { - metadata { - name = "kubernetes-dashboard-admin" - namespace = kubernetes_namespace.k8s-dashboard.metadata[0].name - annotations = { - "kubernetes.io/service-account.name" : "kubernetes-dashboard" - } - } - type = "kubernetes.io/service-account-token" -} +# Admin token: use `vault write kubernetes/creds/dashboard-admin kubernetes_namespace=kubernetes-dashboard` +# instead of a static never-expiring token. ## Readonly RBAC resource "kubernetes_cluster_role" "kubernetes-dashboard-viewonly" { diff --git a/stacks/k8s-dashboard/providers.tf b/stacks/k8s-dashboard/providers.tf index 4cd042f5..860c9eba 100644 --- a/stacks/k8s-dashboard/providers.tf +++ b/stacks/k8s-dashboard/providers.tf @@ -1,8 +1,16 @@ # Generated by Terragrunt. Sig: nIlQXj57tbuaRZEa +terraform { + required_providers { + vault = { + source = "hashicorp/vault" + version = "~> 4.0" + } + } +} + variable "kube_config_path" { - type = string - default = "~/.kube/config" - sensitive = true + type = string + default = "~/.kube/config" } provider "kubernetes" { @@ -14,3 +22,8 @@ provider "helm" { config_path = var.kube_config_path } } + +provider "vault" { + address = "https://vault.viktorbarzin.me" + skip_child_token = true +} diff --git a/stacks/linkwarden/main.tf b/stacks/linkwarden/main.tf index 906b2567..359b1aca 100644 --- a/stacks/linkwarden/main.tf +++ b/stacks/linkwarden/main.tf @@ -23,6 +23,33 @@ resource "kubernetes_namespace" "linkwarden" { } } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "linkwarden-secrets" + namespace = "linkwarden" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "linkwarden-secrets" + } + dataFrom = [{ + extract = { + key = "linkwarden" + } + }] + } + } + depends_on = [kubernetes_namespace.linkwarden] +} + module "tls_secret" { source = "../../modules/kubernetes/setup_tls_secret" namespace = kubernetes_namespace.linkwarden.metadata[0].name @@ -93,12 +120,22 @@ resource "kubernetes_deployment" "linkwarden" { value = "https://authentik.viktorbarzin.me/application/o/linkwarden" } env { - name = "AUTHENTIK_CLIENT_ID" - value = data.vault_kv_secret_v2.secrets.data["authentik_client_id"] + name = "AUTHENTIK_CLIENT_ID" + value_from { + secret_key_ref { + name = "linkwarden-secrets" + key = "authentik_client_id" + } + } } env { - name = "AUTHENTIK_CLIENT_SECRET" - value = data.vault_kv_secret_v2.secrets.data["authentik_client_secret"] + name = "AUTHENTIK_CLIENT_SECRET" + value_from { + secret_key_ref { + name = "linkwarden-secrets" + key = "authentik_client_secret" + } + } } resources { requests = { diff --git a/stacks/linkwarden/providers.tf b/stacks/linkwarden/providers.tf index f4845cc8..860c9eba 100644 --- a/stacks/linkwarden/providers.tf +++ b/stacks/linkwarden/providers.tf @@ -13,12 +13,6 @@ variable "kube_config_path" { default = "~/.kube/config" } -variable "vault_root_token" { - type = string - sensitive = true - default = "" -} - provider "kubernetes" { config_path = var.kube_config_path } @@ -31,6 +25,5 @@ provider "helm" { provider "vault" { address = "https://vault.viktorbarzin.me" - token = var.vault_root_token skip_child_token = true } diff --git a/stacks/n8n/main.tf b/stacks/n8n/main.tf index 799d3496..2cd0f722 100644 --- a/stacks/n8n/main.tf +++ b/stacks/n8n/main.tf @@ -5,11 +5,6 @@ variable "tls_secret_name" { variable "nfs_server" { type = string } variable "postgresql_host" { type = string } -data "vault_kv_secret_v2" "secrets" { - mount = "secret" - name = "n8n" -} - module "tls_secret" { source = "../../modules/kubernetes/setup_tls_secret" namespace = kubernetes_namespace.n8n.metadata[0].name @@ -25,6 +20,33 @@ resource "kubernetes_namespace" "n8n" { } } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "n8n-secrets" + namespace = "n8n" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "n8n-secrets" + } + dataFrom = [{ + extract = { + key = "n8n" + } + }] + } + } + depends_on = [kubernetes_namespace.n8n] +} + module "nfs_data" { source = "../../modules/kubernetes/nfs_volume" name = "n8n-data" @@ -84,6 +106,9 @@ resource "kubernetes_deployment" "n8n" { app = "n8n" tier = local.tiers.aux } + annotations = { + "reloader.stakater.com/auto" = "true" + } } spec { replicas = 1 @@ -124,8 +149,13 @@ resource "kubernetes_deployment" "n8n" { value = "n8n" } env { - name = "DB_POSTGRESDB_PASSWORD" - value = data.vault_kv_secret_v2.secrets.data["db_password"] + name = "DB_POSTGRESDB_PASSWORD" + value_from { + secret_key_ref { + name = "n8n-secrets" + key = "db_password" + } + } } env { name = "GENERIC_TIMEZONE" diff --git a/stacks/netbox/main.tf b/stacks/netbox/main.tf index 723a67a1..9896b9d0 100644 --- a/stacks/netbox/main.tf +++ b/stacks/netbox/main.tf @@ -6,11 +6,6 @@ variable "nfs_server" { type = string } variable "redis_host" { type = string } variable "postgresql_host" { type = string } -data "vault_kv_secret_v2" "secrets" { - mount = "secret" - name = "netbox" -} - resource "kubernetes_namespace" "netbox" { metadata { name = "netbox" @@ -20,6 +15,33 @@ resource "kubernetes_namespace" "netbox" { } } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "netbox-secrets" + namespace = "netbox" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "netbox-secrets" + } + dataFrom = [{ + extract = { + key = "netbox" + } + }] + } + } + depends_on = [kubernetes_namespace.netbox] +} + module "tls_secret" { source = "../../modules/kubernetes/setup_tls_secret" namespace = kubernetes_namespace.netbox.metadata[0].name @@ -76,8 +98,13 @@ resource "kubernetes_deployment" "netbox" { value = "netbox" } env { - name = "DB_PASSWORD" - value = data.vault_kv_secret_v2.secrets.data["db_password"] + name = "DB_PASSWORD" + value_from { + secret_key_ref { + name = "netbox-secrets" + key = "db_password" + } + } } env { name = "DB_HOST" @@ -112,8 +139,13 @@ resource "kubernetes_deployment" "netbox" { value = "me@viktorbarzin.me" } env { - name = "SUPERUSER_PASSWORD" - value = data.vault_kv_secret_v2.secrets.data["superuser_password"] + name = "SUPERUSER_PASSWORD" + value_from { + secret_key_ref { + name = "netbox-secrets" + key = "superuser_password" + } + } } env { name = "REMOTE_AUTH_ENABLED" diff --git a/stacks/nextcloud/main.tf b/stacks/nextcloud/main.tf index 73c861fd..5c958272 100644 --- a/stacks/nextcloud/main.tf +++ b/stacks/nextcloud/main.tf @@ -34,6 +34,33 @@ resource "kubernetes_namespace" "nextcloud" { } } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "nextcloud-secrets" + namespace = "nextcloud" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "nextcloud-secrets" + } + dataFrom = [{ + extract = { + key = "nextcloud" + } + }] + } + } + depends_on = [kubernetes_namespace.nextcloud] +} + resource "kubernetes_resource_quota" "nextcloud" { metadata { name = "nextcloud-quota" @@ -182,8 +209,13 @@ resource "kubernetes_deployment" "whiteboard" { value = "http://nextcloud:8080" } env { - name = "JWT_SECRET_KEY" - value = data.vault_kv_secret_v2.secrets.data["db_password"] # anything secret is fine + name = "JWT_SECRET_KEY" + value_from { + secret_key_ref { + name = "nextcloud-secrets" + key = "db_password" + } + } } } } diff --git a/stacks/onlyoffice/main.tf b/stacks/onlyoffice/main.tf index 1dda92f3..c779051b 100644 --- a/stacks/onlyoffice/main.tf +++ b/stacks/onlyoffice/main.tf @@ -6,11 +6,6 @@ variable "nfs_server" { type = string } variable "redis_host" { type = string } variable "mysql_host" { type = string } -data "vault_kv_secret_v2" "secrets" { - mount = "secret" - name = "onlyoffice" -} - resource "kubernetes_namespace" "onlyoffice" { metadata { name = "onlyoffice" @@ -23,6 +18,33 @@ resource "kubernetes_namespace" "onlyoffice" { } } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "onlyoffice-secrets" + namespace = "onlyoffice" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "onlyoffice-secrets" + } + dataFrom = [{ + extract = { + key = "onlyoffice" + } + }] + } + } + depends_on = [kubernetes_namespace.onlyoffice] +} + resource "kubernetes_limit_range" "onlyoffice" { metadata { name = "onlyoffice-limits" @@ -82,6 +104,9 @@ resource "kubernetes_deployment" "onlyoffice-document-server" { app = "onlyoffice-document-server" tier = local.tiers.edge } + annotations = { + "reloader.stakater.com/auto" = "true" + } } spec { replicas = 1 @@ -138,8 +163,13 @@ resource "kubernetes_deployment" "onlyoffice-document-server" { value = "onlyoffice" } env { - name = "DB_PWD" - value = data.vault_kv_secret_v2.secrets.data["db_password"] + name = "DB_PWD" + value_from { + secret_key_ref { + name = "onlyoffice-secrets" + key = "db_password" + } + } } env { name = "REDIS_SERVER_HOST" @@ -150,8 +180,13 @@ resource "kubernetes_deployment" "onlyoffice-document-server" { value = 6379 } env { - name = "JWT_SECRET" - value = data.vault_kv_secret_v2.secrets.data["jwt_token"] + name = "JWT_SECRET" + value_from { + secret_key_ref { + name = "onlyoffice-secrets" + key = "jwt_token" + } + } } volume_mount { diff --git a/stacks/onlyoffice/providers.tf b/stacks/onlyoffice/providers.tf index f4845cc8..860c9eba 100644 --- a/stacks/onlyoffice/providers.tf +++ b/stacks/onlyoffice/providers.tf @@ -13,12 +13,6 @@ variable "kube_config_path" { default = "~/.kube/config" } -variable "vault_root_token" { - type = string - sensitive = true - default = "" -} - provider "kubernetes" { config_path = var.kube_config_path } @@ -31,6 +25,5 @@ provider "helm" { provider "vault" { address = "https://vault.viktorbarzin.me" - token = var.vault_root_token skip_child_token = true } diff --git a/stacks/openclaw/main.tf b/stacks/openclaw/main.tf index 400f7e88..9f1f019d 100644 --- a/stacks/openclaw/main.tf +++ b/stacks/openclaw/main.tf @@ -31,6 +31,33 @@ module "tls_secret" { tls_secret_name = var.tls_secret_name } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "openclaw-secrets" + namespace = "openclaw" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "openclaw-secrets" + } + dataFrom = [{ + extract = { + key = "openclaw" + } + }] + } + } + depends_on = [kubernetes_namespace.openclaw] +} + resource "kubernetes_service_account" "openclaw" { metadata { name = "openclaw" @@ -296,6 +323,9 @@ resource "kubernetes_deployment" "openclaw" { labels = { app = "openclaw" } + annotations = { + "reloader.stakater.com/search" = "true" + } } spec { service_account_name = kubernetes_service_account.openclaw.metadata[0].name @@ -398,8 +428,13 @@ resource "kubernetes_deployment" "openclaw" { value = "http://claude-memory.claude-memory.svc.cluster.local" } env { - name = "MEMORY_API_KEY" - value = data.vault_kv_secret_v2.secrets.data["claude_memory_api_key"] + name = "MEMORY_API_KEY" + value_from { + secret_key_ref { + name = "openclaw-secrets" + key = "claude_memory_api_key" + } + } } # Python packages path for skills env { @@ -456,12 +491,22 @@ resource "kubernetes_deployment" "openclaw" { container_port = 7352 } env { - name = "NVIDIA_API_KEY" - value = data.vault_kv_secret_v2.secrets.data["nvidia_api_key"] + name = "NVIDIA_API_KEY" + value_from { + secret_key_ref { + name = "openclaw-secrets" + key = "nvidia_api_key" + } + } } env { - name = "OPENROUTER_API_KEY" - value = data.vault_kv_secret_v2.secrets.data["openrouter_api_key"] + name = "OPENROUTER_API_KEY" + value_from { + secret_key_ref { + name = "openclaw-secrets" + key = "openrouter_api_key" + } + } } volume_mount { name = "tools" @@ -913,12 +958,22 @@ resource "kubernetes_cron_job_v1" "task_processor" { ] env { - name = "FORGEJO_TOKEN" - value = data.vault_kv_secret_v2.secrets.data["forgejo_api_token"] + name = "FORGEJO_TOKEN" + value_from { + secret_key_ref { + name = "openclaw-secrets" + key = "forgejo_api_token" + } + } } env { - name = "OPENCLAW_TOKEN" - value = data.vault_kv_secret_v2.secrets.data["nvidia_api_key"] + name = "OPENCLAW_TOKEN" + value_from { + secret_key_ref { + name = "openclaw-secrets" + key = "nvidia_api_key" + } + } } resources { diff --git a/stacks/paperless-ngx/main.tf b/stacks/paperless-ngx/main.tf index d793a4b8..9449320d 100644 --- a/stacks/paperless-ngx/main.tf +++ b/stacks/paperless-ngx/main.tf @@ -27,6 +27,33 @@ resource "kubernetes_namespace" "paperless-ngx" { # } } } + +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "paperless-ngx-secrets" + namespace = "paperless-ngx" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "paperless-ngx-secrets" + } + dataFrom = [{ + extract = { + key = "paperless-ngx" + } + }] + } + } + depends_on = [kubernetes_namespace.paperless-ngx] +} module "tls_secret" { source = "../../modules/kubernetes/setup_tls_secret" namespace = kubernetes_namespace.paperless-ngx.metadata[0].name @@ -104,8 +131,13 @@ resource "kubernetes_deployment" "paperless-ngx" { value = "paperless-ngx" } env { - name = "PAPERLESS_DBPASS" - value = data.vault_kv_secret_v2.secrets.data["db_password"] + name = "PAPERLESS_DBPASS" + value_from { + secret_key_ref { + name = "paperless-ngx-secrets" + key = "db_password" + } + } } env { name = "PAPERLESS_CSRF_TRUSTED_ORIGINS" diff --git a/stacks/platform/modules/kyverno/resource-governance.tf b/stacks/platform/modules/kyverno/resource-governance.tf index 86384279..5aff16ae 100644 --- a/stacks/platform/modules/kyverno/resource-governance.tf +++ b/stacks/platform/modules/kyverno/resource-governance.tf @@ -52,6 +52,16 @@ resource "kubernetes_priority_class" "tier_2_gpu" { description = "GPU workloads: Immich, Ollama, Frigate" } +resource "kubernetes_priority_class" "gpu_workload" { + metadata { + name = "gpu-workload" + } + value = 1200000 + global_default = false + preemption_policy = "PreemptLowerPriority" + description = "GPU-pinned workloads. Higher than all user tiers. Auto-injected by Kyverno on pods requesting nvidia.com/gpu." +} + resource "kubernetes_priority_class" "tier_3_edge" { metadata { name = "tier-3-edge" @@ -858,3 +868,81 @@ resource "kubernetes_manifest" "mutate_ndots" { } } +# ----------------------------------------------------------------------------- +# Layer 5: GPU Workload Priority Override (Kyverno Mutate) +# ----------------------------------------------------------------------------- +# Overrides the tier-based priorityClassName with gpu-workload for pods that +# actually request nvidia.com/gpu resources. This ensures GPU pods can preempt +# non-GPU pods on the GPU node, regardless of namespace tier. +# Runs after Layer 4 (tier injection), so it overrides the tier-based priority. + +resource "kubernetes_manifest" "mutate_gpu_priority" { + manifest = { + apiVersion = "kyverno.io/v1" + kind = "ClusterPolicy" + metadata = { + name = "inject-gpu-workload-priority" + annotations = { + "policies.kyverno.io/title" = "Inject GPU Workload Priority" + "policies.kyverno.io/description" = "Overrides priorityClassName to gpu-workload for pods requesting nvidia.com/gpu resources. Runs after tier-based injection." + } + } + spec = { + rules = [ + { + name = "gpu-priority-override" + match = { + any = [ + { + resources = { + kinds = ["Pod"] + operations = ["CREATE"] + } + } + ] + } + exclude = { + any = [ + { + resources = { + namespaces = local.excluded_namespaces + } + } + ] + } + preconditions = { + any = [ + { + key = "{{ request.object.spec.containers[].resources.requests.\"nvidia.com/gpu\" || '' }}" + operator = "NotEquals" + value = "" + }, + { + key = "{{ request.object.spec.containers[].resources.limits.\"nvidia.com/gpu\" || '' }}" + operator = "NotEquals" + value = "" + } + ] + } + mutate = { + patchesJson6902 = yamlencode([ + { + op = "remove" + path = "/spec/priority" + }, + { + op = "remove" + path = "/spec/preemptionPolicy" + }, + { + op = "add" + path = "/spec/priorityClassName" + value = "gpu-workload" + } + ]) + } + } + ] + } + } +} diff --git a/stacks/platform/providers.tf b/stacks/platform/providers.tf index f4845cc8..860c9eba 100644 --- a/stacks/platform/providers.tf +++ b/stacks/platform/providers.tf @@ -13,12 +13,6 @@ variable "kube_config_path" { default = "~/.kube/config" } -variable "vault_root_token" { - type = string - sensitive = true - default = "" -} - provider "kubernetes" { config_path = var.kube_config_path } @@ -31,6 +25,5 @@ provider "helm" { provider "vault" { address = "https://vault.viktorbarzin.me" - token = var.vault_root_token skip_child_token = true } diff --git a/stacks/plotting-book/main.tf b/stacks/plotting-book/main.tf index efb91dba..e9417b8e 100644 --- a/stacks/plotting-book/main.tf +++ b/stacks/plotting-book/main.tf @@ -11,11 +11,6 @@ variable "plotting_book_google_client_secret" { sensitive = true } -data "vault_kv_secret_v2" "secrets" { - mount = "secret" - name = "plotting-book" -} - resource "kubernetes_namespace" "plotting-book" { metadata { name = "plotting-book" @@ -26,6 +21,33 @@ resource "kubernetes_namespace" "plotting-book" { } } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "plotting-book-secrets" + namespace = "plotting-book" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "plotting-book-secrets" + } + dataFrom = [{ + extract = { + key = "plotting-book" + } + }] + } + } + depends_on = [kubernetes_namespace.plotting-book] +} + module "tls_secret" { source = "../../modules/kubernetes/setup_tls_secret" namespace = kubernetes_namespace.plotting-book.metadata[0].name @@ -56,6 +78,9 @@ resource "kubernetes_deployment" "plotting-book" { app = "plotting-book" tier = local.tiers.aux } + annotations = { + "reloader.stakater.com/auto" = "true" + } } lifecycle { ignore_changes = [ @@ -91,8 +116,13 @@ resource "kubernetes_deployment" "plotting-book" { name = "plotting-book" image_pull_policy = "Always" env { - name = "SESSION_SECRET" - value = data.vault_kv_secret_v2.secrets.data["session_secret"] + name = "SESSION_SECRET" + value_from { + secret_key_ref { + name = "plotting-book-secrets" + key = "session_secret" + } + } } env { name = "GOOGLE_CLIENT_ID" diff --git a/stacks/resume/main.tf b/stacks/resume/main.tf index 1bfddf33..478f86c0 100644 --- a/stacks/resume/main.tf +++ b/stacks/resume/main.tf @@ -12,8 +12,8 @@ data "vault_kv_secret_v2" "secrets" { } locals { - namespace = "resume" - app_url = "https://resume.viktorbarzin.me" + namespace = "resume" + app_url = "https://resume.viktorbarzin.me" mailserver_accounts = jsondecode(data.vault_kv_secret_v2.secrets.data["mailserver_accounts"]) } @@ -32,6 +32,33 @@ module "tls_secret" { tls_secret_name = var.tls_secret_name } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "resume-secrets" + namespace = "resume" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "resume-secrets" + } + dataFrom = [{ + extract = { + key = "resume" + } + }] + } + } + depends_on = [kubernetes_namespace.resume] +} + # Printer service (browserless chromium for PDF generation) resource "kubernetes_deployment" "printer" { metadata { @@ -157,6 +184,9 @@ resource "kubernetes_deployment" "resume" { labels = { app = "resume" } + annotations = { + "reloader.stakater.com/search" = "true" + } } spec { container { @@ -185,8 +215,13 @@ resource "kubernetes_deployment" "resume" { value = "http://resume.${local.namespace}.svc.cluster.local" } env { - name = "AUTH_SECRET" - value = data.vault_kv_secret_v2.secrets.data["auth_secret"] + name = "AUTH_SECRET" + value_from { + secret_key_ref { + name = "resume-secrets" + key = "auth_secret" + } + } } # Server config diff --git a/stacks/rybbit/main.tf b/stacks/rybbit/main.tf index f93467ab..6a3bbfa2 100644 --- a/stacks/rybbit/main.tf +++ b/stacks/rybbit/main.tf @@ -19,6 +19,33 @@ resource "kubernetes_namespace" "rybbit" { } } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "rybbit-secrets" + namespace = "rybbit" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "rybbit-secrets" + } + dataFrom = [{ + extract = { + key = "rybbit" + } + }] + } + } + depends_on = [kubernetes_namespace.rybbit] +} + module "tls_secret" { source = "../../modules/kubernetes/setup_tls_secret" namespace = kubernetes_namespace.rybbit.metadata[0].name @@ -51,6 +78,9 @@ resource "kubernetes_deployment" "clickhouse" { app = "clickhouse" tier = local.tiers.aux } + annotations = { + "reloader.stakater.com/auto" = "true" + } } spec { replicas = 1 @@ -77,8 +107,13 @@ resource "kubernetes_deployment" "clickhouse" { value = local.clickhouse_db } env { - name = "CLICKHOUSE_PASSWORD" - value = data.vault_kv_secret_v2.secrets.data["clickhouse_password"] + name = "CLICKHOUSE_PASSWORD" + value_from { + secret_key_ref { + name = "rybbit-secrets" + key = "clickhouse_password" + } + } } port { name = "clickhouse" @@ -201,6 +236,9 @@ resource "kubernetes_deployment" "rybbit" { app = "rybbit" tier = local.tiers.aux } + annotations = { + "reloader.stakater.com/auto" = "true" + } } spec { replicas = 1 @@ -237,8 +275,13 @@ resource "kubernetes_deployment" "rybbit" { value = "default" } env { - name = "CLICKHOUSE_PASSWORD" - value = data.vault_kv_secret_v2.secrets.data["clickhouse_password"] + name = "CLICKHOUSE_PASSWORD" + value_from { + secret_key_ref { + name = "rybbit-secrets" + key = "clickhouse_password" + } + } } env { name = "POSTGRES_HOST" @@ -257,8 +300,13 @@ resource "kubernetes_deployment" "rybbit" { value = "rybbit" } env { - name = "POSTGRES_PASSWORD" - value = data.vault_kv_secret_v2.secrets.data["postgres_password"] + name = "POSTGRES_PASSWORD" + value_from { + secret_key_ref { + name = "rybbit-secrets" + key = "postgres_password" + } + } } env { name = "BASE_URL" diff --git a/stacks/shadowsocks/main.tf b/stacks/shadowsocks/main.tf index ae92a7eb..f49524c0 100644 --- a/stacks/shadowsocks/main.tf +++ b/stacks/shadowsocks/main.tf @@ -1,8 +1,3 @@ -data "vault_kv_secret_v2" "secrets" { - mount = "secret" - name = "shadowsocks" -} - variable "method" { default = "chacha20-ietf-poly1305" } @@ -20,6 +15,33 @@ resource "kubernetes_namespace" "shadowsocks" { } } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "shadowsocks-secrets" + namespace = "shadowsocks" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "shadowsocks-secrets" + } + dataFrom = [{ + extract = { + key = "shadowsocks" + } + }] + } + } + depends_on = [kubernetes_namespace.shadowsocks] +} + resource "kubernetes_deployment" "shadowsocks" { metadata { name = "shadowsocks" @@ -29,7 +51,7 @@ resource "kubernetes_deployment" "shadowsocks" { tier = local.tiers.edge } annotations = { - "reloader.stakater.com/search" = "true" + "reloader.stakater.com/auto" = "true" } } spec { @@ -55,8 +77,13 @@ resource "kubernetes_deployment" "shadowsocks" { value = var.method } env { - name = "PASSWORD" - value = data.vault_kv_secret_v2.secrets.data["password"] + name = "PASSWORD" + value_from { + secret_key_ref { + name = "shadowsocks-secrets" + key = "password" + } + } } port { container_port = 8388 diff --git a/stacks/speedtest/main.tf b/stacks/speedtest/main.tf index 86504ad9..2c91942b 100644 --- a/stacks/speedtest/main.tf +++ b/stacks/speedtest/main.tf @@ -5,11 +5,6 @@ variable "tls_secret_name" { variable "nfs_server" { type = string } variable "mysql_host" { type = string } -data "vault_kv_secret_v2" "secrets" { - mount = "secret" - name = "speedtest" -} - resource "kubernetes_namespace" "speedtest" { metadata { name = "speedtest" @@ -19,6 +14,33 @@ resource "kubernetes_namespace" "speedtest" { } } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "speedtest-secrets" + namespace = "speedtest" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "speedtest-secrets" + } + dataFrom = [{ + extract = { + key = "speedtest" + } + }] + } + } + depends_on = [kubernetes_namespace.speedtest] +} + module "tls_secret" { source = "../../modules/kubernetes/setup_tls_secret" namespace = kubernetes_namespace.speedtest.metadata[0].name @@ -45,6 +67,9 @@ resource "kubernetes_deployment" "speedtest" { app = "speedtest" tier = local.tiers.aux } + annotations = { + "reloader.stakater.com/auto" = "true" + } } spec { replicas = 1 @@ -108,8 +133,13 @@ resource "kubernetes_deployment" "speedtest" { value = "speedtest" } env { - name = "DB_PASSWORD" - value = data.vault_kv_secret_v2.secrets.data["db_password"] + name = "DB_PASSWORD" + value_from { + secret_key_ref { + name = "speedtest-secrets" + key = "db_password" + } + } } env { name = "DB_PORT" diff --git a/stacks/stirling-pdf/providers.tf b/stacks/stirling-pdf/providers.tf index f4845cc8..860c9eba 100644 --- a/stacks/stirling-pdf/providers.tf +++ b/stacks/stirling-pdf/providers.tf @@ -13,12 +13,6 @@ variable "kube_config_path" { default = "~/.kube/config" } -variable "vault_root_token" { - type = string - sensitive = true - default = "" -} - provider "kubernetes" { config_path = var.kube_config_path } @@ -31,6 +25,5 @@ provider "helm" { provider "vault" { address = "https://vault.viktorbarzin.me" - token = var.vault_root_token skip_child_token = true } diff --git a/stacks/tandoor/main.tf b/stacks/tandoor/main.tf index fb73eba5..24f321a8 100644 --- a/stacks/tandoor/main.tf +++ b/stacks/tandoor/main.tf @@ -11,11 +11,6 @@ variable "nfs_server" { type = string } variable "postgresql_host" { type = string } variable "mail_host" { type = string } -data "vault_kv_secret_v2" "secrets" { - mount = "secret" - name = "tandoor" -} - resource "kubernetes_namespace" "tandoor" { metadata { name = "tandoor" @@ -25,6 +20,34 @@ resource "kubernetes_namespace" "tandoor" { } } } + +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "tandoor-secrets" + namespace = "tandoor" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "tandoor-secrets" + } + dataFrom = [{ + extract = { + key = "tandoor" + } + }] + } + } + depends_on = [kubernetes_namespace.tandoor] +} + resource "random_password" "secret_key" { length = 128 special = false @@ -52,6 +75,9 @@ resource "kubernetes_deployment" "tandoor" { app = "tandoor" tier = local.tiers.aux } + annotations = { + "reloader.stakater.com/auto" = "true" + } } spec { # Disabled: reduce cluster memory pressure (2026-03-14 OOM incident) @@ -96,8 +122,13 @@ resource "kubernetes_deployment" "tandoor" { value = "tandoor" } env { - name = "POSTGRES_PASSWORD" - value = data.vault_kv_secret_v2.secrets.data["db_password"] + name = "POSTGRES_PASSWORD" + value_from { + secret_key_ref { + name = "tandoor-secrets" + key = "db_password" + } + } } env { name = "TANDOOR_PORT" diff --git a/stacks/trading-bot/main.tf b/stacks/trading-bot/main.tf index 4c4b0b13..28fd2fe1 100644 --- a/stacks/trading-bot/main.tf +++ b/stacks/trading-bot/main.tf @@ -6,23 +6,12 @@ variable "nfs_server" { type = string } variable "postgresql_host" { type = string } variable "redis_host" { type = string } variable "ollama_host" { type = string } -data "vault_kv_secret_v2" "secrets" { - mount = "secret" - name = "trading-bot" -} - locals { common_env = { - TRADING_DATABASE_URL = "postgresql+asyncpg://trading:${data.vault_kv_secret_v2.secrets.data["db_password"]}@${var.postgresql_host}:5432/trading" TRADING_REDIS_URL = "redis://${var.redis_host}:6379/4" TRADING_LOG_LEVEL = "INFO" - TRADING_ALPACA_API_KEY = data.vault_kv_secret_v2.secrets.data["alpaca_api_key"] - TRADING_ALPACA_SECRET_KEY = data.vault_kv_secret_v2.secrets.data["alpaca_secret_key"] TRADING_ALPACA_BASE_URL = "https://paper-api.alpaca.markets" TRADING_PAPER_TRADING = "true" - TRADING_JWT_SECRET_KEY = data.vault_kv_secret_v2.secrets.data["jwt_secret"] - TRADING_REDDIT_CLIENT_ID = data.vault_kv_secret_v2.secrets.data["reddit_client_id"] - TRADING_REDDIT_CLIENT_SECRET = data.vault_kv_secret_v2.secrets.data["reddit_client_secret"] TRADING_REDDIT_USER_AGENT = "trading-bot/0.1" TRADING_OLLAMA_HOST = "http://${var.ollama_host}:11434" TRADING_OLLAMA_MODEL = "gemma3" @@ -31,8 +20,6 @@ locals { TRADING_POLL_INTERVAL_SECONDS = "60" TRADING_HISTORICAL_BARS = "100" TRADING_SNAPSHOT_INTERVAL_SECONDS = "60" - TRADING_ALPHA_VANTAGE_API_KEY = data.vault_kv_secret_v2.secrets.data["alpha_vantage_api_key"] - TRADING_FMP_API_KEY = data.vault_kv_secret_v2.secrets.data["fmp_api_key"] TRADING_FUNDAMENTALS_CACHE_TTL_HOURS = "24" TRADING_RP_ID = "trading.viktorbarzin.me" TRADING_RP_NAME = "Trading Bot" @@ -56,6 +43,53 @@ module "tls_secret" { tls_secret_name = var.tls_secret_name } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "trading-bot-secrets" + namespace = "trading-bot" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "trading-bot-secrets" + template = { + data = { + TRADING_DATABASE_URL = "postgresql+asyncpg://trading:{{ .db_password }}@${var.postgresql_host}:5432/trading" + TRADING_ALPACA_API_KEY = "{{ .alpaca_api_key }}" + TRADING_ALPACA_SECRET_KEY = "{{ .alpaca_secret_key }}" + TRADING_JWT_SECRET_KEY = "{{ .jwt_secret }}" + TRADING_REDDIT_CLIENT_ID = "{{ .reddit_client_id }}" + TRADING_REDDIT_CLIENT_SECRET = "{{ .reddit_client_secret }}" + TRADING_ALPHA_VANTAGE_API_KEY = "{{ .alpha_vantage_api_key }}" + TRADING_FMP_API_KEY = "{{ .fmp_api_key }}" + DBAAS_ROOT_PASSWORD = "{{ .dbaas_root_password }}" + DB_PASSWORD = "{{ .db_password }}" + } + } + } + data = [ + { secretKey = "db_password", remoteRef = { key = "trading-bot", property = "db_password" } }, + { secretKey = "alpaca_api_key", remoteRef = { key = "trading-bot", property = "alpaca_api_key" } }, + { secretKey = "alpaca_secret_key", remoteRef = { key = "trading-bot", property = "alpaca_secret_key" } }, + { secretKey = "jwt_secret", remoteRef = { key = "trading-bot", property = "jwt_secret" } }, + { secretKey = "reddit_client_id", remoteRef = { key = "trading-bot", property = "reddit_client_id" } }, + { secretKey = "reddit_client_secret", remoteRef = { key = "trading-bot", property = "reddit_client_secret" } }, + { secretKey = "alpha_vantage_api_key", remoteRef = { key = "trading-bot", property = "alpha_vantage_api_key" } }, + { secretKey = "fmp_api_key", remoteRef = { key = "trading-bot", property = "fmp_api_key" } }, + { secretKey = "dbaas_root_password", remoteRef = { key = "trading-bot", property = "dbaas_root_password" } }, + ] + } + } + depends_on = [kubernetes_namespace.trading-bot] +} + # Database init job - creates the trading database and user in PostgreSQL resource "kubernetes_job" "db_init" { metadata { @@ -74,18 +108,23 @@ resource "kubernetes_job" "db_init" { <<-EOT set -e # Create role if not exists - PGPASSWORD='${data.vault_kv_secret_v2.secrets.data["dbaas_root_password"]}' psql -h ${var.postgresql_host} -U root -tc "SELECT 1 FROM pg_roles WHERE rolname='trading'" | grep -q 1 || \ - PGPASSWORD='${data.vault_kv_secret_v2.secrets.data["dbaas_root_password"]}' psql -h ${var.postgresql_host} -U root -c "CREATE ROLE trading WITH LOGIN PASSWORD '${data.vault_kv_secret_v2.secrets.data["db_password"]}'" + PGPASSWORD="$DBAAS_ROOT_PASSWORD" psql -h ${var.postgresql_host} -U root -tc "SELECT 1 FROM pg_roles WHERE rolname='trading'" | grep -q 1 || \ + PGPASSWORD="$DBAAS_ROOT_PASSWORD" psql -h ${var.postgresql_host} -U root -c "CREATE ROLE trading WITH LOGIN PASSWORD '$DB_PASSWORD'" # Create database if not exists - PGPASSWORD='${data.vault_kv_secret_v2.secrets.data["dbaas_root_password"]}' psql -h ${var.postgresql_host} -U root -tc "SELECT 1 FROM pg_database WHERE datname='trading'" | grep -q 1 || \ - PGPASSWORD='${data.vault_kv_secret_v2.secrets.data["dbaas_root_password"]}' psql -h ${var.postgresql_host} -U root -c "CREATE DATABASE trading OWNER trading" + PGPASSWORD="$DBAAS_ROOT_PASSWORD" psql -h ${var.postgresql_host} -U root -tc "SELECT 1 FROM pg_database WHERE datname='trading'" | grep -q 1 || \ + PGPASSWORD="$DBAAS_ROOT_PASSWORD" psql -h ${var.postgresql_host} -U root -c "CREATE DATABASE trading OWNER trading" # Grant privileges - PGPASSWORD='${data.vault_kv_secret_v2.secrets.data["dbaas_root_password"]}' psql -h ${var.postgresql_host} -U root -c "GRANT ALL PRIVILEGES ON DATABASE trading TO trading" + PGPASSWORD="$DBAAS_ROOT_PASSWORD" psql -h ${var.postgresql_host} -U root -c "GRANT ALL PRIVILEGES ON DATABASE trading TO trading" # Try to enable timescaledb (allow failure) - PGPASSWORD='${data.vault_kv_secret_v2.secrets.data["dbaas_root_password"]}' psql -h ${var.postgresql_host} -U root -d trading -c "CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE" || true + PGPASSWORD="$DBAAS_ROOT_PASSWORD" psql -h ${var.postgresql_host} -U root -d trading -c "CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE" || true echo "Database init complete" EOT ] + env_from { + secret_ref { + name = "trading-bot-secrets" + } + } } restart_policy = "Never" } @@ -113,14 +152,15 @@ resource "kubernetes_job" "migrations" { image = "viktorbarzin/trading-bot-service:latest" image_pull_policy = "Always" command = ["python", "-m", "alembic", "upgrade", "head"] - env { - name = "TRADING_DATABASE_URL" - value = "postgresql+asyncpg://trading:${data.vault_kv_secret_v2.secrets.data["db_password"]}@${var.postgresql_host}:5432/trading" - } env { name = "TRADING_REDIS_URL" value = "redis://${var.redis_host}:6379/4" } + env_from { + secret_ref { + name = "trading-bot-secrets" + } + } } restart_policy = "Never" } @@ -143,6 +183,9 @@ resource "kubernetes_deployment" "trading-bot-frontend" { app = "trading-bot-frontend" tier = local.tiers.edge } + annotations = { + "reloader.stakater.com/auto" = "true" + } } spec { # Disabled: reduce cluster memory pressure (2026-03-14 OOM incident) @@ -200,6 +243,11 @@ resource "kubernetes_deployment" "trading-bot-frontend" { value = env.value } } + env_from { + secret_ref { + name = "trading-bot-secrets" + } + } resources { requests = { cpu = "50m" @@ -231,6 +279,9 @@ resource "kubernetes_deployment" "trading-bot-workers" { app = "trading-bot-workers" tier = local.tiers.edge } + annotations = { + "reloader.stakater.com/auto" = "true" + } } spec { # Disabled: reduce cluster memory pressure (2026-03-14 OOM incident) @@ -266,6 +317,11 @@ resource "kubernetes_deployment" "trading-bot-workers" { name = "TRADING_OTEL_METRICS_PORT" value = "9091" } + env_from { + secret_ref { + name = "trading-bot-secrets" + } + } resources { requests = { cpu = "10m" @@ -292,6 +348,11 @@ resource "kubernetes_deployment" "trading-bot-workers" { name = "TRADING_OTEL_METRICS_PORT" value = "9092" } + env_from { + secret_ref { + name = "trading-bot-secrets" + } + } resources { requests = { cpu = "100m" @@ -318,6 +379,11 @@ resource "kubernetes_deployment" "trading-bot-workers" { name = "TRADING_OTEL_METRICS_PORT" value = "9093" } + env_from { + secret_ref { + name = "trading-bot-secrets" + } + } resources { requests = { cpu = "10m" @@ -344,6 +410,11 @@ resource "kubernetes_deployment" "trading-bot-workers" { name = "TRADING_OTEL_METRICS_PORT" value = "9094" } + env_from { + secret_ref { + name = "trading-bot-secrets" + } + } resources { requests = { cpu = "10m" @@ -370,6 +441,11 @@ resource "kubernetes_deployment" "trading-bot-workers" { name = "TRADING_OTEL_METRICS_PORT" value = "9095" } + env_from { + secret_ref { + name = "trading-bot-secrets" + } + } resources { requests = { cpu = "10m" @@ -396,6 +472,11 @@ resource "kubernetes_deployment" "trading-bot-workers" { name = "TRADING_OTEL_METRICS_PORT" value = "9096" } + env_from { + secret_ref { + name = "trading-bot-secrets" + } + } resources { requests = { cpu = "10m" diff --git a/stacks/trading-bot/terragrunt.hcl b/stacks/trading-bot/terragrunt.hcl index f4c920ab..f0a91f2b 100644 --- a/stacks/trading-bot/terragrunt.hcl +++ b/stacks/trading-bot/terragrunt.hcl @@ -7,7 +7,7 @@ dependency "platform" { skip_outputs = true } -dependency "vault" { - config_path = "../vault" +dependency "external-secrets" { + config_path = "../external-secrets" skip_outputs = true } diff --git a/stacks/tuya-bridge/main.tf b/stacks/tuya-bridge/main.tf index 8f67dcea..5cdcfb04 100644 --- a/stacks/tuya-bridge/main.tf +++ b/stacks/tuya-bridge/main.tf @@ -3,11 +3,6 @@ variable "tls_secret_name" { sensitive = true } -data "vault_kv_secret_v2" "secrets" { - mount = "secret" - name = "tuya-bridge" -} - resource "kubernetes_namespace" "tuya-bridge" { metadata { name = "tuya-bridge" @@ -18,6 +13,33 @@ resource "kubernetes_namespace" "tuya-bridge" { } } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "tuya-bridge-secrets" + namespace = "tuya-bridge" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "tuya-bridge-secrets" + } + dataFrom = [{ + extract = { + key = "tuya-bridge" + } + }] + } + } + depends_on = [kubernetes_namespace.tuya-bridge] +} + module "tls_secret" { source = "../../modules/kubernetes/setup_tls_secret" namespace = kubernetes_namespace.tuya-bridge.metadata[0].name @@ -32,6 +54,9 @@ resource "kubernetes_deployment" "tuya-bridge" { app = "tuya-bridge" tier = local.tiers.cluster } + annotations = { + "reloader.stakater.com/auto" = "true" + } } spec { replicas = 3 @@ -54,20 +79,40 @@ resource "kubernetes_deployment" "tuya-bridge" { container_port = 8080 } env { - name = "TINYTUYA_API_KEY" - value = data.vault_kv_secret_v2.secrets.data["api_key"] + name = "TINYTUYA_API_KEY" + value_from { + secret_key_ref { + name = "tuya-bridge-secrets" + key = "api_key" + } + } } env { - name = "TINYTUYA_API_SECRET" - value = data.vault_kv_secret_v2.secrets.data["api_secret"] + name = "TINYTUYA_API_SECRET" + value_from { + secret_key_ref { + name = "tuya-bridge-secrets" + key = "api_secret" + } + } } env { - name = "SERVICE_API_KEY" # used for auth the API endpoint - value = data.vault_kv_secret_v2.secrets.data["service_secret"] + name = "SERVICE_API_KEY" # used for auth the API endpoint + value_from { + secret_key_ref { + name = "tuya-bridge-secrets" + key = "service_secret" + } + } } env { - name = "SLACK_URL" - value = data.vault_kv_secret_v2.secrets.data["slack_url"] + name = "SLACK_URL" + value_from { + secret_key_ref { + name = "tuya-bridge-secrets" + key = "slack_url" + } + } } resources { requests = { diff --git a/stacks/url/main.tf b/stacks/url/main.tf index a42c7c86..ae1ac0e4 100644 --- a/stacks/url/main.tf +++ b/stacks/url/main.tf @@ -29,6 +29,33 @@ resource "kubernetes_namespace" "shlink" { } } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "url-secrets" + namespace = "url" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "url-secrets" + } + dataFrom = [{ + extract = { + key = "url" + } + }] + } + } + depends_on = [kubernetes_namespace.shlink] +} + module "tls_secret" { source = "../../modules/kubernetes/setup_tls_secret" namespace = kubernetes_namespace.shlink.metadata[0].name @@ -87,6 +114,9 @@ resource "kubernetes_deployment" "shlink" { run = "shlink" tier = local.tiers.aux } + annotations = { + "reloader.stakater.com/auto" = "true" + } } spec { replicas = 1 @@ -114,8 +144,13 @@ resource "kubernetes_deployment" "shlink" { value = "https" } env { - name = "GEOLITE_LICENSE_KEY" - value = data.vault_kv_secret_v2.secrets.data["geolite_license_key"] + name = "GEOLITE_LICENSE_KEY" + value_from { + secret_key_ref { + name = "url-secrets" + key = "geolite_license_key" + } + } } # DB config env { diff --git a/stacks/wealthfolio/main.tf b/stacks/wealthfolio/main.tf index 57f45985..6c26157a 100644 --- a/stacks/wealthfolio/main.tf +++ b/stacks/wealthfolio/main.tf @@ -4,11 +4,6 @@ variable "tls_secret_name" { } variable "nfs_server" { type = string } -data "vault_kv_secret_v2" "secrets" { - mount = "secret" - name = "wealthfolio" -} - # To refresh transactions use finance db positions exporters: # # workon finace-app && cd ~/code/finance && python main.py fetch position --imap-user=$IMAP_USER --imap-password=$IMAP_PASSWORD --trading212-api-keys=$TRADING212_API_KEYS --output-file positions.csv && mv positions.csv /home/wizard/code/infra/modules/kubernetes/wealthfolio/updated_trades.csv @@ -26,6 +21,33 @@ resource "kubernetes_namespace" "wealthfolio" { } } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "wealthfolio-secrets" + namespace = "wealthfolio" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "wealthfolio-secrets" + } + dataFrom = [{ + extract = { + key = "wealthfolio" + } + }] + } + } + depends_on = [kubernetes_namespace.wealthfolio] +} + module "tls_secret" { source = "../../modules/kubernetes/setup_tls_secret" namespace = kubernetes_namespace.wealthfolio.metadata[0].name @@ -53,6 +75,9 @@ resource "kubernetes_deployment" "wealthfolio" { app = "wealthfolio" tier = local.tiers.aux } + annotations = { + "reloader.stakater.com/auto" = "true" + } } spec { replicas = 1 @@ -79,8 +104,13 @@ resource "kubernetes_deployment" "wealthfolio" { value = "0.0.0.0:8080" } env { - name = "WF_AUTH_PASSWORD_HASH" - value = data.vault_kv_secret_v2.secrets.data["password_hash"] + name = "WF_AUTH_PASSWORD_HASH" + value_from { + secret_key_ref { + name = "wealthfolio-secrets" + key = "password_hash" + } + } } env { name = "WF_DB_PATH" diff --git a/stacks/webhook_handler/main.tf b/stacks/webhook_handler/main.tf index ea2465db..b47216e3 100644 --- a/stacks/webhook_handler/main.tf +++ b/stacks/webhook_handler/main.tf @@ -77,7 +77,7 @@ resource "kubernetes_deployment" "webhook_handler" { tier = local.tiers.aux } annotations = { - "reloader.stakater.com/search" = "true" + "reloader.stakater.com/auto" = "true" } } spec { @@ -126,32 +126,62 @@ resource "kubernetes_deployment" "webhook_handler" { sub_path = "id_rsa" } env { - name = "WEBHOOKSECRET" - value = data.vault_kv_secret_v2.secrets.data["secret"] + name = "WEBHOOKSECRET" + value_from { + secret_key_ref { + name = "webhook-handler-secrets" + key = "secret" + } + } } env { - name = "FB_APP_SECRET" - value = data.vault_kv_secret_v2.secrets.data["fb_app_secret"] + name = "FB_APP_SECRET" + value_from { + secret_key_ref { + name = "webhook-handler-secrets" + key = "fb_app_secret" + } + } } env { - name = "FB_VERIFY_TOKEN" - value = data.vault_kv_secret_v2.secrets.data["fb_verify_token"] + name = "FB_VERIFY_TOKEN" + value_from { + secret_key_ref { + name = "webhook-handler-secrets" + key = "fb_verify_token" + } + } } env { - name = "FB_PAGE_TOKEN" - value = data.vault_kv_secret_v2.secrets.data["fb_page_token"] + name = "FB_PAGE_TOKEN" + value_from { + secret_key_ref { + name = "webhook-handler-secrets" + key = "fb_page_token" + } + } } env { name = "CONFIG" value = "./chatbot/config/viktorwebservices.yaml" } env { - name = "GIT_USER" - value = data.vault_kv_secret_v2.secrets.data["git_user"] + name = "GIT_USER" + value_from { + secret_key_ref { + name = "webhook-handler-secrets" + key = "git_user" + } + } } env { - name = "GIT_TOKEN" - value = data.vault_kv_secret_v2.secrets.data["git_token"] + name = "GIT_TOKEN" + value_from { + secret_key_ref { + name = "webhook-handler-secrets" + key = "git_token" + } + } } env { name = "SSH_KEY" @@ -205,3 +235,30 @@ module "ingress" { "gethomepage.dev/pod-selector" = "" } } + +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "webhook-handler-secrets" + namespace = "webhook-handler" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "webhook-handler-secrets" + } + dataFrom = [{ + extract = { + key = "webhook-handler" + } + }] + } + } + depends_on = [kubernetes_namespace.webhook-handler] +} diff --git a/stacks/woodpecker/main.tf b/stacks/woodpecker/main.tf index 51a51653..ad44c454 100644 --- a/stacks/woodpecker/main.tf +++ b/stacks/woodpecker/main.tf @@ -43,6 +43,33 @@ module "tls_secret" { tls_secret_name = var.tls_secret_name } +resource "kubernetes_manifest" "external_secret" { + manifest = { + apiVersion = "external-secrets.io/v1beta1" + kind = "ExternalSecret" + metadata = { + name = "woodpecker-secrets" + namespace = "woodpecker" + } + spec = { + refreshInterval = "15m" + secretStoreRef = { + name = "vault-kv" + kind = "ClusterSecretStore" + } + target = { + name = "woodpecker-secrets" + } + dataFrom = [{ + extract = { + key = "woodpecker" + } + }] + } + } + depends_on = [kubernetes_namespace.woodpecker] +} + resource "kubernetes_config_map" "git_crypt_key" { metadata { name = "git-crypt-key" diff --git a/stacks/woodpecker/values.yaml b/stacks/woodpecker/values.yaml index a379d00c..f264d2de 100644 --- a/stacks/woodpecker/values.yaml +++ b/stacks/woodpecker/values.yaml @@ -1,5 +1,7 @@ server: enabled: true + podAnnotations: + reloader.stakater.com/search: "true" statefulSet: replicaCount: 1 image: @@ -36,6 +38,8 @@ server: agent: enabled: true + podAnnotations: + reloader.stakater.com/search: "true" replicaCount: 2 image: registry: docker.io