migrate consuming stacks to ESO + remove k8s-dashboard static token

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
This commit is contained in:
Viktor Barzin 2026-03-15 19:05:04 +00:00
parent cfc30b62e8
commit 1acf8cc4e8
41 changed files with 1278 additions and 265 deletions

View file

@ -13,12 +13,6 @@ variable "kube_config_path" {
default = "~/.kube/config" default = "~/.kube/config"
} }
variable "vault_root_token" {
type = string
sensitive = true
default = ""
}
provider "kubernetes" { provider "kubernetes" {
config_path = var.kube_config_path config_path = var.kube_config_path
} }
@ -31,6 +25,5 @@ provider "helm" {
provider "vault" { provider "vault" {
address = "https://vault.viktorbarzin.me" address = "https://vault.viktorbarzin.me"
token = var.vault_root_token
skip_child_token = true skip_child_token = true
} }

View file

@ -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" { module "tls_secret" {
source = "../../modules/kubernetes/setup_tls_secret" source = "../../modules/kubernetes/setup_tls_secret"
namespace = kubernetes_namespace.claude-memory.metadata[0].name namespace = kubernetes_namespace.claude-memory.metadata[0].name
@ -74,6 +101,9 @@ resource "kubernetes_deployment" "claude-memory" {
app = "claude-memory" app = "claude-memory"
tier = local.tiers.aux tier = local.tiers.aux
} }
annotations = {
"reloader.stakater.com/auto" = "true"
}
} }
spec { spec {
replicas = 2 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" value = "postgresql://claude_memory:${data.vault_kv_secret_v2.secrets.data["db_password"]}@${var.postgresql_host}:5432/claude_memory"
} }
env { env {
name = "API_KEY" name = "API_KEY"
value = data.vault_kv_secret_v2.secrets.data["api_key"] value_from {
secret_key_ref {
name = "claude-memory-secrets"
key = "api_key"
}
}
} }
startup_probe { startup_probe {

View file

@ -11,11 +11,6 @@ variable "nfs_server" { type = string }
variable "redis_host" { type = string } variable "redis_host" { type = string }
variable "postgresql_host" { type = string } variable "postgresql_host" { type = string }
data "vault_kv_secret_v2" "secrets" {
mount = "secret"
name = "dawarich"
}
resource "kubernetes_namespace" "dawarich" { resource "kubernetes_namespace" "dawarich" {
metadata { metadata {
name = "dawarich" 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" { module "tls_secret" {
source = "../../modules/kubernetes/setup_tls_secret" source = "../../modules/kubernetes/setup_tls_secret"
namespace = kubernetes_namespace.dawarich.metadata[0].name namespace = kubernetes_namespace.dawarich.metadata[0].name
@ -92,8 +114,13 @@ resource "kubernetes_deployment" "dawarich" {
value = "dawarich" value = "dawarich"
} }
env { env {
name = "DATABASE_PASSWORD" name = "DATABASE_PASSWORD"
value = data.vault_kv_secret_v2.secrets.data["db_password"] value_from {
secret_key_ref {
name = "dawarich-secrets"
key = "db_password"
}
}
} }
env { env {
name = "DATABASE_NAME" name = "DATABASE_NAME"

View file

@ -4,11 +4,6 @@ variable "tls_secret_name" {
} }
variable "nfs_server" { type = string } variable "nfs_server" { type = string }
data "vault_kv_secret_v2" "secrets" {
mount = "secret"
name = "diun"
}
resource "kubernetes_namespace" "diun" { resource "kubernetes_namespace" "diun" {
metadata { metadata {
name = "diun" 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" { module "tls_secret" {
source = "../../modules/kubernetes/setup_tls_secret" source = "../../modules/kubernetes/setup_tls_secret"
namespace = kubernetes_namespace.diun.metadata[0].name 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"] # value = data.vault_kv_secret_v2.secrets.data["nfty_token"]
# } # }
env { env {
name = "DIUN_NOTIF_SLACK_WEBHOOKURL" name = "DIUN_NOTIF_SLACK_WEBHOOKURL"
value = data.vault_kv_secret_v2.secrets.data["slack_url"] value_from {
secret_key_ref {
name = "diun-secrets"
key = "slack_url"
}
}
} }
env { env {
name = "LOG_LEVEL" name = "LOG_LEVEL"

View file

@ -6,11 +6,6 @@ variable "nfs_server" { type = string }
variable "discord_f1_guild_id" { type = string } variable "discord_f1_guild_id" { type = string }
variable "discord_f1_channel_ids" { 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" { resource "kubernetes_namespace" "f1-stream" {
metadata { metadata {
name = "f1-stream" 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" { module "nfs_data" {
source = "../../modules/kubernetes/nfs_volume" source = "../../modules/kubernetes/nfs_volume"
name = "f1-stream-data" name = "f1-stream-data"
@ -37,6 +59,9 @@ resource "kubernetes_deployment" "f1-stream" {
app = "f1-stream" app = "f1-stream"
tier = local.tiers.aux tier = local.tiers.aux
} }
annotations = {
"reloader.stakater.com/auto" = "true"
}
} }
spec { spec {
replicas = 0 # Scaled down for cluster stability periodic scans cause memory pressure replicas = 0 # Scaled down for cluster stability periodic scans cause memory pressure
@ -69,8 +94,13 @@ resource "kubernetes_deployment" "f1-stream" {
container_port = 8000 container_port = 8000
} }
env { env {
name = "DISCORD_TOKEN" name = "DISCORD_TOKEN"
value = data.vault_kv_secret_v2.secrets.data["discord_user_token"] value_from {
secret_key_ref {
name = "f1-stream-secrets"
key = "discord_user_token"
}
}
} }
env { env {
name = "DISCORD_CHANNELS" name = "DISCORD_CHANNELS"

View file

@ -4,11 +4,6 @@ variable "tls_secret_name" {
} }
variable "nfs_server" { type = string } variable "nfs_server" { type = string }
data "vault_kv_secret_v2" "secrets" {
mount = "secret"
name = "forgejo"
}
resource "kubernetes_namespace" "forgejo" { resource "kubernetes_namespace" "forgejo" {
metadata { metadata {
name = "forgejo" name = "forgejo"

View file

@ -4,11 +4,6 @@ variable "tls_secret_name" {
variable "nfs_server" { type = string } variable "nfs_server" { type = string }
variable "mysql_host" { type = string } variable "mysql_host" { type = string }
data "vault_kv_secret_v2" "secrets" {
mount = "secret"
name = "hackmd"
}
resource "kubernetes_namespace" "hackmd" { resource "kubernetes_namespace" "hackmd" {
metadata { metadata {
name = "hackmd" name = "hackmd"
@ -42,6 +37,9 @@ resource "kubernetes_deployment" "hackmd" {
"kubernetes.io/cluster-service" = "true" "kubernetes.io/cluster-service" = "true"
tier = local.tiers.edge tier = local.tiers.edge
} }
annotations = {
"reloader.stakater.com/auto" = "true"
}
} }
spec { spec {
replicas = 1 replicas = 1
@ -102,8 +100,12 @@ resource "kubernetes_deployment" "hackmd" {
image = "hackmdio/hackmd" image = "hackmdio/hackmd"
env { env {
name = "CMD_DB_URL" name = "CMD_DB_URL"
# value = format("%s%s%s", "postgres://codimd:", var.hackmd_db_password, "@localhost/codimd") value_from {
value = format("%s%s%s", "mysql://codimd:", data.vault_kv_secret_v2.secrets.data["db_password"], "@${var.mysql_host}/codimd") secret_key_ref {
name = "hackmd-secrets"
key = "CMD_DB_URL"
}
}
} }
env { env {
name = "CMD_USECDN" name = "CMD_USECDN"
@ -176,3 +178,34 @@ module "ingress" {
"gethomepage.dev/pod-selector" = "" "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]
}

View file

@ -13,12 +13,6 @@ variable "kube_config_path" {
default = "~/.kube/config" default = "~/.kube/config"
} }
variable "vault_root_token" {
type = string
sensitive = true
default = ""
}
provider "kubernetes" { provider "kubernetes" {
config_path = var.kube_config_path config_path = var.kube_config_path
} }
@ -31,6 +25,5 @@ provider "helm" {
provider "vault" { provider "vault" {
address = "https://vault.viktorbarzin.me" address = "https://vault.viktorbarzin.me"
token = var.vault_root_token
skip_child_token = true skip_child_token = true
} }

View file

@ -6,8 +6,3 @@ dependency "platform" {
config_path = "../platform" config_path = "../platform"
skip_outputs = true skip_outputs = true
} }
dependency "vault" {
config_path = "../vault"
skip_outputs = true
}

View file

@ -5,11 +5,6 @@ variable "tls_secret_name" {
variable "nfs_server" { type = string } variable "nfs_server" { type = string }
variable "postgresql_host" { type = string } variable "postgresql_host" { type = string }
data "vault_kv_secret_v2" "secrets" {
mount = "secret"
name = "health"
}
resource "kubernetes_namespace" "health" { resource "kubernetes_namespace" "health" {
metadata { metadata {
name = "health" name = "health"
@ -41,6 +36,9 @@ resource "kubernetes_deployment" "health" {
app = "health" app = "health"
tier = local.tiers.aux tier = local.tiers.aux
} }
annotations = {
"reloader.stakater.com/auto" = "true"
}
} }
spec { spec {
replicas = 1 replicas = 1
@ -65,12 +63,22 @@ resource "kubernetes_deployment" "health" {
} }
env { env {
name = "DATABASE_URL" name = "DATABASE_URL"
value = "postgresql+asyncpg://health:${data.vault_kv_secret_v2.secrets.data["db_password"]}@${var.postgresql_host}:5432/health" value_from {
secret_key_ref {
name = "health-secrets"
key = "DATABASE_URL"
}
}
} }
env { env {
name = "SECRET_KEY" name = "SECRET_KEY"
value = data.vault_kv_secret_v2.secrets.data["secret_key"] value_from {
secret_key_ref {
name = "health-secrets"
key = "secret_key"
}
}
} }
env { env {
name = "UPLOAD_DIR" name = "UPLOAD_DIR"
@ -151,3 +159,41 @@ module "ingress" {
"gethomepage.dev/pod-selector" = "" "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]
}

View file

@ -13,12 +13,6 @@ variable "kube_config_path" {
default = "~/.kube/config" default = "~/.kube/config"
} }
variable "vault_root_token" {
type = string
sensitive = true
default = ""
}
provider "kubernetes" { provider "kubernetes" {
config_path = var.kube_config_path config_path = var.kube_config_path
} }
@ -31,6 +25,5 @@ provider "helm" {
provider "vault" { provider "vault" {
address = "https://vault.viktorbarzin.me" address = "https://vault.viktorbarzin.me"
token = var.vault_root_token
skip_child_token = true skip_child_token = true
} }

View file

@ -6,8 +6,3 @@ dependency "platform" {
config_path = "../platform" config_path = "../platform"
skip_outputs = true skip_outputs = true
} }
dependency "vault" {
config_path = "../vault"
skip_outputs = true
}

View file

@ -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" { resource "kubernetes_deployment" "immich_server" {
metadata { metadata {
name = "immich-server" name = "immich-server"
@ -112,6 +139,9 @@ resource "kubernetes_deployment" "immich_server" {
app = "immich-server" app = "immich-server"
tier = local.tiers.gpu tier = local.tiers.gpu
} }
annotations = {
"reloader.stakater.com/search" = "true"
}
} }
lifecycle { lifecycle {
@ -140,8 +170,9 @@ resource "kubernetes_deployment" "immich_server" {
app = "immich-server" app = "immich-server"
} }
annotations = { annotations = {
"diun.enable" = "true" "diun.enable" = "true"
"diun.include_tags" = "^\\d+\\.\\d+\\.\\d+$" "diun.include_tags" = "^\\d+\\.\\d+\\.\\d+$"
"reloader.stakater.com/auto" = "true"
} }
} }
@ -169,8 +200,13 @@ resource "kubernetes_deployment" "immich_server" {
value = "immich" value = "immich"
} }
env { env {
name = "DB_PASSWORD" name = "DB_PASSWORD"
value = data.vault_kv_secret_v2.secrets.data["db_password"] value_from {
secret_key_ref {
name = "immich-secrets"
key = "db_password"
}
}
} }
env { env {
name = "IMMICH_MACHINE_LEARNING_URL" name = "IMMICH_MACHINE_LEARNING_URL"
@ -331,6 +367,9 @@ resource "kubernetes_deployment" "immich-postgres" {
labels = { labels = {
tier = local.tiers.gpu tier = local.tiers.gpu
} }
annotations = {
"reloader.stakater.com/search" = "true"
}
} }
lifecycle { lifecycle {
@ -365,8 +404,13 @@ resource "kubernetes_deployment" "immich-postgres" {
name = "postgresql" name = "postgresql"
} }
env { env {
name = "POSTGRES_PASSWORD" name = "POSTGRES_PASSWORD"
value = data.vault_kv_secret_v2.secrets.data["db_password"] value_from {
secret_key_ref {
name = "immich-secrets"
key = "db_password"
}
}
} }
env { env {
name = "POSTGRES_USER" name = "POSTGRES_USER"
@ -641,13 +685,22 @@ resource "kubernetes_cron_job_v1" "postgresql-backup" {
image = "postgres:16.4-bullseye" image = "postgres:16.4-bullseye"
command = ["/bin/sh", "-c", <<-EOT command = ["/bin/sh", "-c", <<-EOT
export now=$(date +"%Y_%m_%d_%H_%M") 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 # Rotate - delete last log file
cd /backup cd /backup
find . -name "dump_*.sql" -type f -mtime +14 -delete # 14 day retention of backups find . -name "dump_*.sql" -type f -mtime +14 -delete # 14 day retention of backups
EOT EOT
] ]
env {
name = "PGPASSWORD"
value_from {
secret_key_ref {
name = "immich-secrets"
key = "db_password"
}
}
}
volume_mount { volume_mount {
name = "postgresql-backup" name = "postgresql-backup"
mount_path = "/backup" mount_path = "/backup"

View file

@ -131,16 +131,8 @@ resource "kubernetes_cluster_role_binding" "kubernetes-dashboard" {
# depends_on = [module.dashboard] # depends_on = [module.dashboard]
} }
resource "kubernetes_secret" "kubernetes-dashboard-admin-token" { # Admin token: use `vault write kubernetes/creds/dashboard-admin kubernetes_namespace=kubernetes-dashboard`
metadata { # instead of a static never-expiring token.
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"
}
## Readonly RBAC ## Readonly RBAC
resource "kubernetes_cluster_role" "kubernetes-dashboard-viewonly" { resource "kubernetes_cluster_role" "kubernetes-dashboard-viewonly" {

View file

@ -1,8 +1,16 @@
# Generated by Terragrunt. Sig: nIlQXj57tbuaRZEa # Generated by Terragrunt. Sig: nIlQXj57tbuaRZEa
terraform {
required_providers {
vault = {
source = "hashicorp/vault"
version = "~> 4.0"
}
}
}
variable "kube_config_path" { variable "kube_config_path" {
type = string type = string
default = "~/.kube/config" default = "~/.kube/config"
sensitive = true
} }
provider "kubernetes" { provider "kubernetes" {
@ -14,3 +22,8 @@ provider "helm" {
config_path = var.kube_config_path config_path = var.kube_config_path
} }
} }
provider "vault" {
address = "https://vault.viktorbarzin.me"
skip_child_token = true
}

View file

@ -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" { module "tls_secret" {
source = "../../modules/kubernetes/setup_tls_secret" source = "../../modules/kubernetes/setup_tls_secret"
namespace = kubernetes_namespace.linkwarden.metadata[0].name namespace = kubernetes_namespace.linkwarden.metadata[0].name
@ -93,12 +120,22 @@ resource "kubernetes_deployment" "linkwarden" {
value = "https://authentik.viktorbarzin.me/application/o/linkwarden" value = "https://authentik.viktorbarzin.me/application/o/linkwarden"
} }
env { env {
name = "AUTHENTIK_CLIENT_ID" name = "AUTHENTIK_CLIENT_ID"
value = data.vault_kv_secret_v2.secrets.data["authentik_client_id"] value_from {
secret_key_ref {
name = "linkwarden-secrets"
key = "authentik_client_id"
}
}
} }
env { env {
name = "AUTHENTIK_CLIENT_SECRET" name = "AUTHENTIK_CLIENT_SECRET"
value = data.vault_kv_secret_v2.secrets.data["authentik_client_secret"] value_from {
secret_key_ref {
name = "linkwarden-secrets"
key = "authentik_client_secret"
}
}
} }
resources { resources {
requests = { requests = {

View file

@ -13,12 +13,6 @@ variable "kube_config_path" {
default = "~/.kube/config" default = "~/.kube/config"
} }
variable "vault_root_token" {
type = string
sensitive = true
default = ""
}
provider "kubernetes" { provider "kubernetes" {
config_path = var.kube_config_path config_path = var.kube_config_path
} }
@ -31,6 +25,5 @@ provider "helm" {
provider "vault" { provider "vault" {
address = "https://vault.viktorbarzin.me" address = "https://vault.viktorbarzin.me"
token = var.vault_root_token
skip_child_token = true skip_child_token = true
} }

View file

@ -5,11 +5,6 @@ variable "tls_secret_name" {
variable "nfs_server" { type = string } variable "nfs_server" { type = string }
variable "postgresql_host" { type = string } variable "postgresql_host" { type = string }
data "vault_kv_secret_v2" "secrets" {
mount = "secret"
name = "n8n"
}
module "tls_secret" { module "tls_secret" {
source = "../../modules/kubernetes/setup_tls_secret" source = "../../modules/kubernetes/setup_tls_secret"
namespace = kubernetes_namespace.n8n.metadata[0].name 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" { module "nfs_data" {
source = "../../modules/kubernetes/nfs_volume" source = "../../modules/kubernetes/nfs_volume"
name = "n8n-data" name = "n8n-data"
@ -84,6 +106,9 @@ resource "kubernetes_deployment" "n8n" {
app = "n8n" app = "n8n"
tier = local.tiers.aux tier = local.tiers.aux
} }
annotations = {
"reloader.stakater.com/auto" = "true"
}
} }
spec { spec {
replicas = 1 replicas = 1
@ -124,8 +149,13 @@ resource "kubernetes_deployment" "n8n" {
value = "n8n" value = "n8n"
} }
env { env {
name = "DB_POSTGRESDB_PASSWORD" name = "DB_POSTGRESDB_PASSWORD"
value = data.vault_kv_secret_v2.secrets.data["db_password"] value_from {
secret_key_ref {
name = "n8n-secrets"
key = "db_password"
}
}
} }
env { env {
name = "GENERIC_TIMEZONE" name = "GENERIC_TIMEZONE"

View file

@ -6,11 +6,6 @@ variable "nfs_server" { type = string }
variable "redis_host" { type = string } variable "redis_host" { type = string }
variable "postgresql_host" { type = string } variable "postgresql_host" { type = string }
data "vault_kv_secret_v2" "secrets" {
mount = "secret"
name = "netbox"
}
resource "kubernetes_namespace" "netbox" { resource "kubernetes_namespace" "netbox" {
metadata { metadata {
name = "netbox" 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" { module "tls_secret" {
source = "../../modules/kubernetes/setup_tls_secret" source = "../../modules/kubernetes/setup_tls_secret"
namespace = kubernetes_namespace.netbox.metadata[0].name namespace = kubernetes_namespace.netbox.metadata[0].name
@ -76,8 +98,13 @@ resource "kubernetes_deployment" "netbox" {
value = "netbox" value = "netbox"
} }
env { env {
name = "DB_PASSWORD" name = "DB_PASSWORD"
value = data.vault_kv_secret_v2.secrets.data["db_password"] value_from {
secret_key_ref {
name = "netbox-secrets"
key = "db_password"
}
}
} }
env { env {
name = "DB_HOST" name = "DB_HOST"
@ -112,8 +139,13 @@ resource "kubernetes_deployment" "netbox" {
value = "me@viktorbarzin.me" value = "me@viktorbarzin.me"
} }
env { env {
name = "SUPERUSER_PASSWORD" name = "SUPERUSER_PASSWORD"
value = data.vault_kv_secret_v2.secrets.data["superuser_password"] value_from {
secret_key_ref {
name = "netbox-secrets"
key = "superuser_password"
}
}
} }
env { env {
name = "REMOTE_AUTH_ENABLED" name = "REMOTE_AUTH_ENABLED"

View file

@ -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" { resource "kubernetes_resource_quota" "nextcloud" {
metadata { metadata {
name = "nextcloud-quota" name = "nextcloud-quota"
@ -182,8 +209,13 @@ resource "kubernetes_deployment" "whiteboard" {
value = "http://nextcloud:8080" value = "http://nextcloud:8080"
} }
env { env {
name = "JWT_SECRET_KEY" name = "JWT_SECRET_KEY"
value = data.vault_kv_secret_v2.secrets.data["db_password"] # anything secret is fine value_from {
secret_key_ref {
name = "nextcloud-secrets"
key = "db_password"
}
}
} }
} }
} }

View file

@ -6,11 +6,6 @@ variable "nfs_server" { type = string }
variable "redis_host" { type = string } variable "redis_host" { type = string }
variable "mysql_host" { type = string } variable "mysql_host" { type = string }
data "vault_kv_secret_v2" "secrets" {
mount = "secret"
name = "onlyoffice"
}
resource "kubernetes_namespace" "onlyoffice" { resource "kubernetes_namespace" "onlyoffice" {
metadata { metadata {
name = "onlyoffice" 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" { resource "kubernetes_limit_range" "onlyoffice" {
metadata { metadata {
name = "onlyoffice-limits" name = "onlyoffice-limits"
@ -82,6 +104,9 @@ resource "kubernetes_deployment" "onlyoffice-document-server" {
app = "onlyoffice-document-server" app = "onlyoffice-document-server"
tier = local.tiers.edge tier = local.tiers.edge
} }
annotations = {
"reloader.stakater.com/auto" = "true"
}
} }
spec { spec {
replicas = 1 replicas = 1
@ -138,8 +163,13 @@ resource "kubernetes_deployment" "onlyoffice-document-server" {
value = "onlyoffice" value = "onlyoffice"
} }
env { env {
name = "DB_PWD" name = "DB_PWD"
value = data.vault_kv_secret_v2.secrets.data["db_password"] value_from {
secret_key_ref {
name = "onlyoffice-secrets"
key = "db_password"
}
}
} }
env { env {
name = "REDIS_SERVER_HOST" name = "REDIS_SERVER_HOST"
@ -150,8 +180,13 @@ resource "kubernetes_deployment" "onlyoffice-document-server" {
value = 6379 value = 6379
} }
env { env {
name = "JWT_SECRET" name = "JWT_SECRET"
value = data.vault_kv_secret_v2.secrets.data["jwt_token"] value_from {
secret_key_ref {
name = "onlyoffice-secrets"
key = "jwt_token"
}
}
} }
volume_mount { volume_mount {

View file

@ -13,12 +13,6 @@ variable "kube_config_path" {
default = "~/.kube/config" default = "~/.kube/config"
} }
variable "vault_root_token" {
type = string
sensitive = true
default = ""
}
provider "kubernetes" { provider "kubernetes" {
config_path = var.kube_config_path config_path = var.kube_config_path
} }
@ -31,6 +25,5 @@ provider "helm" {
provider "vault" { provider "vault" {
address = "https://vault.viktorbarzin.me" address = "https://vault.viktorbarzin.me"
token = var.vault_root_token
skip_child_token = true skip_child_token = true
} }

View file

@ -31,6 +31,33 @@ module "tls_secret" {
tls_secret_name = var.tls_secret_name 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" { resource "kubernetes_service_account" "openclaw" {
metadata { metadata {
name = "openclaw" name = "openclaw"
@ -296,6 +323,9 @@ resource "kubernetes_deployment" "openclaw" {
labels = { labels = {
app = "openclaw" app = "openclaw"
} }
annotations = {
"reloader.stakater.com/search" = "true"
}
} }
spec { spec {
service_account_name = kubernetes_service_account.openclaw.metadata[0].name 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" value = "http://claude-memory.claude-memory.svc.cluster.local"
} }
env { env {
name = "MEMORY_API_KEY" name = "MEMORY_API_KEY"
value = data.vault_kv_secret_v2.secrets.data["claude_memory_api_key"] value_from {
secret_key_ref {
name = "openclaw-secrets"
key = "claude_memory_api_key"
}
}
} }
# Python packages path for skills # Python packages path for skills
env { env {
@ -456,12 +491,22 @@ resource "kubernetes_deployment" "openclaw" {
container_port = 7352 container_port = 7352
} }
env { env {
name = "NVIDIA_API_KEY" name = "NVIDIA_API_KEY"
value = data.vault_kv_secret_v2.secrets.data["nvidia_api_key"] value_from {
secret_key_ref {
name = "openclaw-secrets"
key = "nvidia_api_key"
}
}
} }
env { env {
name = "OPENROUTER_API_KEY" name = "OPENROUTER_API_KEY"
value = data.vault_kv_secret_v2.secrets.data["openrouter_api_key"] value_from {
secret_key_ref {
name = "openclaw-secrets"
key = "openrouter_api_key"
}
}
} }
volume_mount { volume_mount {
name = "tools" name = "tools"
@ -913,12 +958,22 @@ resource "kubernetes_cron_job_v1" "task_processor" {
] ]
env { env {
name = "FORGEJO_TOKEN" name = "FORGEJO_TOKEN"
value = data.vault_kv_secret_v2.secrets.data["forgejo_api_token"] value_from {
secret_key_ref {
name = "openclaw-secrets"
key = "forgejo_api_token"
}
}
} }
env { env {
name = "OPENCLAW_TOKEN" name = "OPENCLAW_TOKEN"
value = data.vault_kv_secret_v2.secrets.data["nvidia_api_key"] value_from {
secret_key_ref {
name = "openclaw-secrets"
key = "nvidia_api_key"
}
}
} }
resources { resources {

View file

@ -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" { module "tls_secret" {
source = "../../modules/kubernetes/setup_tls_secret" source = "../../modules/kubernetes/setup_tls_secret"
namespace = kubernetes_namespace.paperless-ngx.metadata[0].name namespace = kubernetes_namespace.paperless-ngx.metadata[0].name
@ -104,8 +131,13 @@ resource "kubernetes_deployment" "paperless-ngx" {
value = "paperless-ngx" value = "paperless-ngx"
} }
env { env {
name = "PAPERLESS_DBPASS" name = "PAPERLESS_DBPASS"
value = data.vault_kv_secret_v2.secrets.data["db_password"] value_from {
secret_key_ref {
name = "paperless-ngx-secrets"
key = "db_password"
}
}
} }
env { env {
name = "PAPERLESS_CSRF_TRUSTED_ORIGINS" name = "PAPERLESS_CSRF_TRUSTED_ORIGINS"

View file

@ -52,6 +52,16 @@ resource "kubernetes_priority_class" "tier_2_gpu" {
description = "GPU workloads: Immich, Ollama, Frigate" 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" { resource "kubernetes_priority_class" "tier_3_edge" {
metadata { metadata {
name = "tier-3-edge" 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"
}
])
}
}
]
}
}
}

View file

@ -13,12 +13,6 @@ variable "kube_config_path" {
default = "~/.kube/config" default = "~/.kube/config"
} }
variable "vault_root_token" {
type = string
sensitive = true
default = ""
}
provider "kubernetes" { provider "kubernetes" {
config_path = var.kube_config_path config_path = var.kube_config_path
} }
@ -31,6 +25,5 @@ provider "helm" {
provider "vault" { provider "vault" {
address = "https://vault.viktorbarzin.me" address = "https://vault.viktorbarzin.me"
token = var.vault_root_token
skip_child_token = true skip_child_token = true
} }

View file

@ -11,11 +11,6 @@ variable "plotting_book_google_client_secret" {
sensitive = true sensitive = true
} }
data "vault_kv_secret_v2" "secrets" {
mount = "secret"
name = "plotting-book"
}
resource "kubernetes_namespace" "plotting-book" { resource "kubernetes_namespace" "plotting-book" {
metadata { metadata {
name = "plotting-book" 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" { module "tls_secret" {
source = "../../modules/kubernetes/setup_tls_secret" source = "../../modules/kubernetes/setup_tls_secret"
namespace = kubernetes_namespace.plotting-book.metadata[0].name namespace = kubernetes_namespace.plotting-book.metadata[0].name
@ -56,6 +78,9 @@ resource "kubernetes_deployment" "plotting-book" {
app = "plotting-book" app = "plotting-book"
tier = local.tiers.aux tier = local.tiers.aux
} }
annotations = {
"reloader.stakater.com/auto" = "true"
}
} }
lifecycle { lifecycle {
ignore_changes = [ ignore_changes = [
@ -91,8 +116,13 @@ resource "kubernetes_deployment" "plotting-book" {
name = "plotting-book" name = "plotting-book"
image_pull_policy = "Always" image_pull_policy = "Always"
env { env {
name = "SESSION_SECRET" name = "SESSION_SECRET"
value = data.vault_kv_secret_v2.secrets.data["session_secret"] value_from {
secret_key_ref {
name = "plotting-book-secrets"
key = "session_secret"
}
}
} }
env { env {
name = "GOOGLE_CLIENT_ID" name = "GOOGLE_CLIENT_ID"

View file

@ -12,8 +12,8 @@ data "vault_kv_secret_v2" "secrets" {
} }
locals { locals {
namespace = "resume" namespace = "resume"
app_url = "https://resume.viktorbarzin.me" app_url = "https://resume.viktorbarzin.me"
mailserver_accounts = jsondecode(data.vault_kv_secret_v2.secrets.data["mailserver_accounts"]) 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 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) # Printer service (browserless chromium for PDF generation)
resource "kubernetes_deployment" "printer" { resource "kubernetes_deployment" "printer" {
metadata { metadata {
@ -157,6 +184,9 @@ resource "kubernetes_deployment" "resume" {
labels = { labels = {
app = "resume" app = "resume"
} }
annotations = {
"reloader.stakater.com/search" = "true"
}
} }
spec { spec {
container { container {
@ -185,8 +215,13 @@ resource "kubernetes_deployment" "resume" {
value = "http://resume.${local.namespace}.svc.cluster.local" value = "http://resume.${local.namespace}.svc.cluster.local"
} }
env { env {
name = "AUTH_SECRET" name = "AUTH_SECRET"
value = data.vault_kv_secret_v2.secrets.data["auth_secret"] value_from {
secret_key_ref {
name = "resume-secrets"
key = "auth_secret"
}
}
} }
# Server config # Server config

View file

@ -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" { module "tls_secret" {
source = "../../modules/kubernetes/setup_tls_secret" source = "../../modules/kubernetes/setup_tls_secret"
namespace = kubernetes_namespace.rybbit.metadata[0].name namespace = kubernetes_namespace.rybbit.metadata[0].name
@ -51,6 +78,9 @@ resource "kubernetes_deployment" "clickhouse" {
app = "clickhouse" app = "clickhouse"
tier = local.tiers.aux tier = local.tiers.aux
} }
annotations = {
"reloader.stakater.com/auto" = "true"
}
} }
spec { spec {
replicas = 1 replicas = 1
@ -77,8 +107,13 @@ resource "kubernetes_deployment" "clickhouse" {
value = local.clickhouse_db value = local.clickhouse_db
} }
env { env {
name = "CLICKHOUSE_PASSWORD" name = "CLICKHOUSE_PASSWORD"
value = data.vault_kv_secret_v2.secrets.data["clickhouse_password"] value_from {
secret_key_ref {
name = "rybbit-secrets"
key = "clickhouse_password"
}
}
} }
port { port {
name = "clickhouse" name = "clickhouse"
@ -201,6 +236,9 @@ resource "kubernetes_deployment" "rybbit" {
app = "rybbit" app = "rybbit"
tier = local.tiers.aux tier = local.tiers.aux
} }
annotations = {
"reloader.stakater.com/auto" = "true"
}
} }
spec { spec {
replicas = 1 replicas = 1
@ -237,8 +275,13 @@ resource "kubernetes_deployment" "rybbit" {
value = "default" value = "default"
} }
env { env {
name = "CLICKHOUSE_PASSWORD" name = "CLICKHOUSE_PASSWORD"
value = data.vault_kv_secret_v2.secrets.data["clickhouse_password"] value_from {
secret_key_ref {
name = "rybbit-secrets"
key = "clickhouse_password"
}
}
} }
env { env {
name = "POSTGRES_HOST" name = "POSTGRES_HOST"
@ -257,8 +300,13 @@ resource "kubernetes_deployment" "rybbit" {
value = "rybbit" value = "rybbit"
} }
env { env {
name = "POSTGRES_PASSWORD" name = "POSTGRES_PASSWORD"
value = data.vault_kv_secret_v2.secrets.data["postgres_password"] value_from {
secret_key_ref {
name = "rybbit-secrets"
key = "postgres_password"
}
}
} }
env { env {
name = "BASE_URL" name = "BASE_URL"

View file

@ -1,8 +1,3 @@
data "vault_kv_secret_v2" "secrets" {
mount = "secret"
name = "shadowsocks"
}
variable "method" { variable "method" {
default = "chacha20-ietf-poly1305" 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" { resource "kubernetes_deployment" "shadowsocks" {
metadata { metadata {
name = "shadowsocks" name = "shadowsocks"
@ -29,7 +51,7 @@ resource "kubernetes_deployment" "shadowsocks" {
tier = local.tiers.edge tier = local.tiers.edge
} }
annotations = { annotations = {
"reloader.stakater.com/search" = "true" "reloader.stakater.com/auto" = "true"
} }
} }
spec { spec {
@ -55,8 +77,13 @@ resource "kubernetes_deployment" "shadowsocks" {
value = var.method value = var.method
} }
env { env {
name = "PASSWORD" name = "PASSWORD"
value = data.vault_kv_secret_v2.secrets.data["password"] value_from {
secret_key_ref {
name = "shadowsocks-secrets"
key = "password"
}
}
} }
port { port {
container_port = 8388 container_port = 8388

View file

@ -5,11 +5,6 @@ variable "tls_secret_name" {
variable "nfs_server" { type = string } variable "nfs_server" { type = string }
variable "mysql_host" { type = string } variable "mysql_host" { type = string }
data "vault_kv_secret_v2" "secrets" {
mount = "secret"
name = "speedtest"
}
resource "kubernetes_namespace" "speedtest" { resource "kubernetes_namespace" "speedtest" {
metadata { metadata {
name = "speedtest" 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" { module "tls_secret" {
source = "../../modules/kubernetes/setup_tls_secret" source = "../../modules/kubernetes/setup_tls_secret"
namespace = kubernetes_namespace.speedtest.metadata[0].name namespace = kubernetes_namespace.speedtest.metadata[0].name
@ -45,6 +67,9 @@ resource "kubernetes_deployment" "speedtest" {
app = "speedtest" app = "speedtest"
tier = local.tiers.aux tier = local.tiers.aux
} }
annotations = {
"reloader.stakater.com/auto" = "true"
}
} }
spec { spec {
replicas = 1 replicas = 1
@ -108,8 +133,13 @@ resource "kubernetes_deployment" "speedtest" {
value = "speedtest" value = "speedtest"
} }
env { env {
name = "DB_PASSWORD" name = "DB_PASSWORD"
value = data.vault_kv_secret_v2.secrets.data["db_password"] value_from {
secret_key_ref {
name = "speedtest-secrets"
key = "db_password"
}
}
} }
env { env {
name = "DB_PORT" name = "DB_PORT"

View file

@ -13,12 +13,6 @@ variable "kube_config_path" {
default = "~/.kube/config" default = "~/.kube/config"
} }
variable "vault_root_token" {
type = string
sensitive = true
default = ""
}
provider "kubernetes" { provider "kubernetes" {
config_path = var.kube_config_path config_path = var.kube_config_path
} }
@ -31,6 +25,5 @@ provider "helm" {
provider "vault" { provider "vault" {
address = "https://vault.viktorbarzin.me" address = "https://vault.viktorbarzin.me"
token = var.vault_root_token
skip_child_token = true skip_child_token = true
} }

View file

@ -11,11 +11,6 @@ variable "nfs_server" { type = string }
variable "postgresql_host" { type = string } variable "postgresql_host" { type = string }
variable "mail_host" { type = string } variable "mail_host" { type = string }
data "vault_kv_secret_v2" "secrets" {
mount = "secret"
name = "tandoor"
}
resource "kubernetes_namespace" "tandoor" { resource "kubernetes_namespace" "tandoor" {
metadata { metadata {
name = "tandoor" 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" { resource "random_password" "secret_key" {
length = 128 length = 128
special = false special = false
@ -52,6 +75,9 @@ resource "kubernetes_deployment" "tandoor" {
app = "tandoor" app = "tandoor"
tier = local.tiers.aux tier = local.tiers.aux
} }
annotations = {
"reloader.stakater.com/auto" = "true"
}
} }
spec { spec {
# Disabled: reduce cluster memory pressure (2026-03-14 OOM incident) # Disabled: reduce cluster memory pressure (2026-03-14 OOM incident)
@ -96,8 +122,13 @@ resource "kubernetes_deployment" "tandoor" {
value = "tandoor" value = "tandoor"
} }
env { env {
name = "POSTGRES_PASSWORD" name = "POSTGRES_PASSWORD"
value = data.vault_kv_secret_v2.secrets.data["db_password"] value_from {
secret_key_ref {
name = "tandoor-secrets"
key = "db_password"
}
}
} }
env { env {
name = "TANDOOR_PORT" name = "TANDOOR_PORT"

View file

@ -6,23 +6,12 @@ variable "nfs_server" { type = string }
variable "postgresql_host" { type = string } variable "postgresql_host" { type = string }
variable "redis_host" { type = string } variable "redis_host" { type = string }
variable "ollama_host" { type = string } variable "ollama_host" { type = string }
data "vault_kv_secret_v2" "secrets" {
mount = "secret"
name = "trading-bot"
}
locals { locals {
common_env = { 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_REDIS_URL = "redis://${var.redis_host}:6379/4"
TRADING_LOG_LEVEL = "INFO" 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_ALPACA_BASE_URL = "https://paper-api.alpaca.markets"
TRADING_PAPER_TRADING = "true" 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_REDDIT_USER_AGENT = "trading-bot/0.1"
TRADING_OLLAMA_HOST = "http://${var.ollama_host}:11434" TRADING_OLLAMA_HOST = "http://${var.ollama_host}:11434"
TRADING_OLLAMA_MODEL = "gemma3" TRADING_OLLAMA_MODEL = "gemma3"
@ -31,8 +20,6 @@ locals {
TRADING_POLL_INTERVAL_SECONDS = "60" TRADING_POLL_INTERVAL_SECONDS = "60"
TRADING_HISTORICAL_BARS = "100" TRADING_HISTORICAL_BARS = "100"
TRADING_SNAPSHOT_INTERVAL_SECONDS = "60" 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_FUNDAMENTALS_CACHE_TTL_HOURS = "24"
TRADING_RP_ID = "trading.viktorbarzin.me" TRADING_RP_ID = "trading.viktorbarzin.me"
TRADING_RP_NAME = "Trading Bot" TRADING_RP_NAME = "Trading Bot"
@ -56,6 +43,53 @@ module "tls_secret" {
tls_secret_name = var.tls_secret_name 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 # Database init job - creates the trading database and user in PostgreSQL
resource "kubernetes_job" "db_init" { resource "kubernetes_job" "db_init" {
metadata { metadata {
@ -74,18 +108,23 @@ resource "kubernetes_job" "db_init" {
<<-EOT <<-EOT
set -e set -e
# Create role if not exists # 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="$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 -c "CREATE ROLE trading WITH LOGIN PASSWORD '$DB_PASSWORD'"
# Create database if not exists # 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="$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 -c "CREATE DATABASE trading OWNER trading"
# Grant privileges # 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) # 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" echo "Database init complete"
EOT EOT
] ]
env_from {
secret_ref {
name = "trading-bot-secrets"
}
}
} }
restart_policy = "Never" restart_policy = "Never"
} }
@ -113,14 +152,15 @@ resource "kubernetes_job" "migrations" {
image = "viktorbarzin/trading-bot-service:latest" image = "viktorbarzin/trading-bot-service:latest"
image_pull_policy = "Always" image_pull_policy = "Always"
command = ["python", "-m", "alembic", "upgrade", "head"] 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 { env {
name = "TRADING_REDIS_URL" name = "TRADING_REDIS_URL"
value = "redis://${var.redis_host}:6379/4" value = "redis://${var.redis_host}:6379/4"
} }
env_from {
secret_ref {
name = "trading-bot-secrets"
}
}
} }
restart_policy = "Never" restart_policy = "Never"
} }
@ -143,6 +183,9 @@ resource "kubernetes_deployment" "trading-bot-frontend" {
app = "trading-bot-frontend" app = "trading-bot-frontend"
tier = local.tiers.edge tier = local.tiers.edge
} }
annotations = {
"reloader.stakater.com/auto" = "true"
}
} }
spec { spec {
# Disabled: reduce cluster memory pressure (2026-03-14 OOM incident) # Disabled: reduce cluster memory pressure (2026-03-14 OOM incident)
@ -200,6 +243,11 @@ resource "kubernetes_deployment" "trading-bot-frontend" {
value = env.value value = env.value
} }
} }
env_from {
secret_ref {
name = "trading-bot-secrets"
}
}
resources { resources {
requests = { requests = {
cpu = "50m" cpu = "50m"
@ -231,6 +279,9 @@ resource "kubernetes_deployment" "trading-bot-workers" {
app = "trading-bot-workers" app = "trading-bot-workers"
tier = local.tiers.edge tier = local.tiers.edge
} }
annotations = {
"reloader.stakater.com/auto" = "true"
}
} }
spec { spec {
# Disabled: reduce cluster memory pressure (2026-03-14 OOM incident) # 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" name = "TRADING_OTEL_METRICS_PORT"
value = "9091" value = "9091"
} }
env_from {
secret_ref {
name = "trading-bot-secrets"
}
}
resources { resources {
requests = { requests = {
cpu = "10m" cpu = "10m"
@ -292,6 +348,11 @@ resource "kubernetes_deployment" "trading-bot-workers" {
name = "TRADING_OTEL_METRICS_PORT" name = "TRADING_OTEL_METRICS_PORT"
value = "9092" value = "9092"
} }
env_from {
secret_ref {
name = "trading-bot-secrets"
}
}
resources { resources {
requests = { requests = {
cpu = "100m" cpu = "100m"
@ -318,6 +379,11 @@ resource "kubernetes_deployment" "trading-bot-workers" {
name = "TRADING_OTEL_METRICS_PORT" name = "TRADING_OTEL_METRICS_PORT"
value = "9093" value = "9093"
} }
env_from {
secret_ref {
name = "trading-bot-secrets"
}
}
resources { resources {
requests = { requests = {
cpu = "10m" cpu = "10m"
@ -344,6 +410,11 @@ resource "kubernetes_deployment" "trading-bot-workers" {
name = "TRADING_OTEL_METRICS_PORT" name = "TRADING_OTEL_METRICS_PORT"
value = "9094" value = "9094"
} }
env_from {
secret_ref {
name = "trading-bot-secrets"
}
}
resources { resources {
requests = { requests = {
cpu = "10m" cpu = "10m"
@ -370,6 +441,11 @@ resource "kubernetes_deployment" "trading-bot-workers" {
name = "TRADING_OTEL_METRICS_PORT" name = "TRADING_OTEL_METRICS_PORT"
value = "9095" value = "9095"
} }
env_from {
secret_ref {
name = "trading-bot-secrets"
}
}
resources { resources {
requests = { requests = {
cpu = "10m" cpu = "10m"
@ -396,6 +472,11 @@ resource "kubernetes_deployment" "trading-bot-workers" {
name = "TRADING_OTEL_METRICS_PORT" name = "TRADING_OTEL_METRICS_PORT"
value = "9096" value = "9096"
} }
env_from {
secret_ref {
name = "trading-bot-secrets"
}
}
resources { resources {
requests = { requests = {
cpu = "10m" cpu = "10m"

View file

@ -7,7 +7,7 @@ dependency "platform" {
skip_outputs = true skip_outputs = true
} }
dependency "vault" { dependency "external-secrets" {
config_path = "../vault" config_path = "../external-secrets"
skip_outputs = true skip_outputs = true
} }

View file

@ -3,11 +3,6 @@ variable "tls_secret_name" {
sensitive = true sensitive = true
} }
data "vault_kv_secret_v2" "secrets" {
mount = "secret"
name = "tuya-bridge"
}
resource "kubernetes_namespace" "tuya-bridge" { resource "kubernetes_namespace" "tuya-bridge" {
metadata { metadata {
name = "tuya-bridge" 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" { module "tls_secret" {
source = "../../modules/kubernetes/setup_tls_secret" source = "../../modules/kubernetes/setup_tls_secret"
namespace = kubernetes_namespace.tuya-bridge.metadata[0].name namespace = kubernetes_namespace.tuya-bridge.metadata[0].name
@ -32,6 +54,9 @@ resource "kubernetes_deployment" "tuya-bridge" {
app = "tuya-bridge" app = "tuya-bridge"
tier = local.tiers.cluster tier = local.tiers.cluster
} }
annotations = {
"reloader.stakater.com/auto" = "true"
}
} }
spec { spec {
replicas = 3 replicas = 3
@ -54,20 +79,40 @@ resource "kubernetes_deployment" "tuya-bridge" {
container_port = 8080 container_port = 8080
} }
env { env {
name = "TINYTUYA_API_KEY" name = "TINYTUYA_API_KEY"
value = data.vault_kv_secret_v2.secrets.data["api_key"] value_from {
secret_key_ref {
name = "tuya-bridge-secrets"
key = "api_key"
}
}
} }
env { env {
name = "TINYTUYA_API_SECRET" name = "TINYTUYA_API_SECRET"
value = data.vault_kv_secret_v2.secrets.data["api_secret"] value_from {
secret_key_ref {
name = "tuya-bridge-secrets"
key = "api_secret"
}
}
} }
env { env {
name = "SERVICE_API_KEY" # used for auth the API endpoint name = "SERVICE_API_KEY" # used for auth the API endpoint
value = data.vault_kv_secret_v2.secrets.data["service_secret"] value_from {
secret_key_ref {
name = "tuya-bridge-secrets"
key = "service_secret"
}
}
} }
env { env {
name = "SLACK_URL" name = "SLACK_URL"
value = data.vault_kv_secret_v2.secrets.data["slack_url"] value_from {
secret_key_ref {
name = "tuya-bridge-secrets"
key = "slack_url"
}
}
} }
resources { resources {
requests = { requests = {

View file

@ -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" { module "tls_secret" {
source = "../../modules/kubernetes/setup_tls_secret" source = "../../modules/kubernetes/setup_tls_secret"
namespace = kubernetes_namespace.shlink.metadata[0].name namespace = kubernetes_namespace.shlink.metadata[0].name
@ -87,6 +114,9 @@ resource "kubernetes_deployment" "shlink" {
run = "shlink" run = "shlink"
tier = local.tiers.aux tier = local.tiers.aux
} }
annotations = {
"reloader.stakater.com/auto" = "true"
}
} }
spec { spec {
replicas = 1 replicas = 1
@ -114,8 +144,13 @@ resource "kubernetes_deployment" "shlink" {
value = "https" value = "https"
} }
env { env {
name = "GEOLITE_LICENSE_KEY" name = "GEOLITE_LICENSE_KEY"
value = data.vault_kv_secret_v2.secrets.data["geolite_license_key"] value_from {
secret_key_ref {
name = "url-secrets"
key = "geolite_license_key"
}
}
} }
# DB config # DB config
env { env {

View file

@ -4,11 +4,6 @@ variable "tls_secret_name" {
} }
variable "nfs_server" { type = string } variable "nfs_server" { type = string }
data "vault_kv_secret_v2" "secrets" {
mount = "secret"
name = "wealthfolio"
}
# To refresh transactions use finance db positions exporters: # 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 # 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" { module "tls_secret" {
source = "../../modules/kubernetes/setup_tls_secret" source = "../../modules/kubernetes/setup_tls_secret"
namespace = kubernetes_namespace.wealthfolio.metadata[0].name namespace = kubernetes_namespace.wealthfolio.metadata[0].name
@ -53,6 +75,9 @@ resource "kubernetes_deployment" "wealthfolio" {
app = "wealthfolio" app = "wealthfolio"
tier = local.tiers.aux tier = local.tiers.aux
} }
annotations = {
"reloader.stakater.com/auto" = "true"
}
} }
spec { spec {
replicas = 1 replicas = 1
@ -79,8 +104,13 @@ resource "kubernetes_deployment" "wealthfolio" {
value = "0.0.0.0:8080" value = "0.0.0.0:8080"
} }
env { env {
name = "WF_AUTH_PASSWORD_HASH" name = "WF_AUTH_PASSWORD_HASH"
value = data.vault_kv_secret_v2.secrets.data["password_hash"] value_from {
secret_key_ref {
name = "wealthfolio-secrets"
key = "password_hash"
}
}
} }
env { env {
name = "WF_DB_PATH" name = "WF_DB_PATH"

View file

@ -77,7 +77,7 @@ resource "kubernetes_deployment" "webhook_handler" {
tier = local.tiers.aux tier = local.tiers.aux
} }
annotations = { annotations = {
"reloader.stakater.com/search" = "true" "reloader.stakater.com/auto" = "true"
} }
} }
spec { spec {
@ -126,32 +126,62 @@ resource "kubernetes_deployment" "webhook_handler" {
sub_path = "id_rsa" sub_path = "id_rsa"
} }
env { env {
name = "WEBHOOKSECRET" name = "WEBHOOKSECRET"
value = data.vault_kv_secret_v2.secrets.data["secret"] value_from {
secret_key_ref {
name = "webhook-handler-secrets"
key = "secret"
}
}
} }
env { env {
name = "FB_APP_SECRET" name = "FB_APP_SECRET"
value = data.vault_kv_secret_v2.secrets.data["fb_app_secret"] value_from {
secret_key_ref {
name = "webhook-handler-secrets"
key = "fb_app_secret"
}
}
} }
env { env {
name = "FB_VERIFY_TOKEN" name = "FB_VERIFY_TOKEN"
value = data.vault_kv_secret_v2.secrets.data["fb_verify_token"] value_from {
secret_key_ref {
name = "webhook-handler-secrets"
key = "fb_verify_token"
}
}
} }
env { env {
name = "FB_PAGE_TOKEN" name = "FB_PAGE_TOKEN"
value = data.vault_kv_secret_v2.secrets.data["fb_page_token"] value_from {
secret_key_ref {
name = "webhook-handler-secrets"
key = "fb_page_token"
}
}
} }
env { env {
name = "CONFIG" name = "CONFIG"
value = "./chatbot/config/viktorwebservices.yaml" value = "./chatbot/config/viktorwebservices.yaml"
} }
env { env {
name = "GIT_USER" name = "GIT_USER"
value = data.vault_kv_secret_v2.secrets.data["git_user"] value_from {
secret_key_ref {
name = "webhook-handler-secrets"
key = "git_user"
}
}
} }
env { env {
name = "GIT_TOKEN" name = "GIT_TOKEN"
value = data.vault_kv_secret_v2.secrets.data["git_token"] value_from {
secret_key_ref {
name = "webhook-handler-secrets"
key = "git_token"
}
}
} }
env { env {
name = "SSH_KEY" name = "SSH_KEY"
@ -205,3 +235,30 @@ module "ingress" {
"gethomepage.dev/pod-selector" = "" "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]
}

View file

@ -43,6 +43,33 @@ module "tls_secret" {
tls_secret_name = var.tls_secret_name 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" { resource "kubernetes_config_map" "git_crypt_key" {
metadata { metadata {
name = "git-crypt-key" name = "git-crypt-key"

View file

@ -1,5 +1,7 @@
server: server:
enabled: true enabled: true
podAnnotations:
reloader.stakater.com/search: "true"
statefulSet: statefulSet:
replicaCount: 1 replicaCount: 1
image: image:
@ -36,6 +38,8 @@ server:
agent: agent:
enabled: true enabled: true
podAnnotations:
reloader.stakater.com/search: "true"
replicaCount: 2 replicaCount: 2
image: image:
registry: docker.io registry: docker.io