diff --git a/main.tf b/main.tf index eb6382a4..5680b3f2 100644 --- a/main.tf +++ b/main.tf @@ -132,6 +132,7 @@ variable "clickhouse_password" { type = string } variable "clickhouse_postgres_password" { type = string } variable "wealthfolio_password_hash" { type = string } variable "aiostreams_database_connection_string" { type = string } +variable "actualbudget_credentials" { type = map(any) } provider "kubernetes" { @@ -180,7 +181,14 @@ module "k8s-node-template" { is_k8s_template = true # provision cloud init file with k8s deps snippet_name = local.k8s_cloud_init_snippet_name # Add mirror registry - containerd_config_update_command = "echo '[plugins.\"io.containerd.grpc.v1.cri\".registry.mirrors.\"docker.io\"]' >> /etc/containerd/config.toml && echo ' endpoint = [\"http://10.0.20.10:5000\"]' >> /etc/containerd/config.toml" # docker registry vm + containerd_config_update_command = <<-EOF + echo '[plugins.\"io.containerd.grpc.v1.cri\".registry.mirrors.\"docker.io\"]' >> /etc/containerd/config.toml && echo ' endpoint = [\"http://10.0.20.10:5000\"]' >> /etc/containerd/config.toml # docker registry vm + + sed -i 's/.*max_concurrent_downloads = 3/max_concurrent_downloads = 20/g' /etc/containerd/config.toml # Enable multiple concurrent downloads + sudo sed -i '/serializeImagePulls:/d' /var/lib/kubelet/config.yaml && \ + sudo sed -i '/maxParallelImagePulls:/d' /var/lib/kubelet/config.yaml && \ + echo -e 'serializeImagePulls: false\nmaxParallelImagePulls: 50' | sudo tee -a /var/lib/kubelet/config.yaml && \ + EOF k8s_join_command = var.k8s_join_command } @@ -546,6 +554,8 @@ module "kubernetes_cluster" { wealthfolio_password_hash = var.wealthfolio_password_hash aiostreams_database_connection_string = var.aiostreams_database_connection_string + + actualbudget_credentials = var.actualbudget_credentials } diff --git a/modules/kubernetes/actualbudget/factory/main.tf b/modules/kubernetes/actualbudget/factory/main.tf index b19f1f06..315fed2f 100644 --- a/modules/kubernetes/actualbudget/factory/main.tf +++ b/modules/kubernetes/actualbudget/factory/main.tf @@ -4,6 +4,14 @@ variable "tag" { default = "latest" } variable "tier" { type = string } +variable "sync_id" { + type = string + default = null # If not passed, we won't run banksync +} +variable "budget_encryption_password" { + type = string + default = null # If not passed, we won't run banksync ;known after initial installation +} resource "kubernetes_deployment" "actualbudget" { metadata { @@ -13,9 +21,6 @@ resource "kubernetes_deployment" "actualbudget" { app = "actualbudget-${var.name}" tier = var.tier } - annotations = { - "reloader.stakater.com/search" = "true" - } } spec { replicas = 1 @@ -94,3 +99,119 @@ module "ingress" { } rybbit_site_id = "3e6b6b68088a" } + + +resource "random_string" "api-key" { + length = 32 + lower = true +} + +resource "kubernetes_deployment" "actualbudget-http-api" { + count = var.budget_encryption_password != null ? 1 : 0 + metadata { + name = "actualbudget-http-api-${var.name}" + namespace = "actualbudget" + labels = { + app = "actualbudget-http-api-${var.name}" + tier = var.tier + } + } + spec { + replicas = 1 + strategy { + type = "RollingUpdate" + } + selector { + match_labels = { + app = "actualbudget-http-api-${var.name}" + } + } + template { + metadata { + labels = { + app = "actualbudget-http-api-${var.name}" + } + } + spec { + container { + image = "jhonderson/actual-http-api:latest" + name = "actualbudget" + + port { + container_port = 5007 + } + env { + name = "ACTUAL_SERVER_URL" + value = "https://budget-${var.name}.viktorbarzin.me" + } + env { + name = "ACTUAL_SERVER_PASSWORD" + value = var.budget_encryption_password + } + env { + name = "API_KEY" + value = random_string.api-key.result + } + + } + } + } + } +} + +resource "kubernetes_service" "actualbudget-http-api" { + metadata { + name = "budget-http-api-${var.name}" + namespace = "actualbudget" + labels = { + app = "actualbudget-http-api-${var.name}" + } + } + + spec { + selector = { + app = "actualbudget-http-api-${var.name}" + } + port { + name = "http" + port = 80 + target_port = 5007 + } + } +} + +resource "kubernetes_cron_job_v1" "bank-sync" { + count = var.sync_id != null && var.budget_encryption_password != null ? 1 : 0 + metadata { + name = "bank-sync-${var.name}" + namespace = "actualbudget" + } + spec { + concurrency_policy = "Replace" + failed_jobs_history_limit = 5 + schedule = "0 0 * * *" # Daily + starting_deadline_seconds = 10 + successful_jobs_history_limit = 10 + job_template { + metadata {} + spec { + backoff_limit = 3 + ttl_seconds_after_finished = 10 + template { + metadata {} + spec { + container { + name = "bank-sync" + image = "curlimages/curl" + command = ["/bin/sh", "-c", <<-EOT + # set -eux # Shows credentials so use only when debugging + curl -X POST --location 'http://budget-http-api-${var.name}/v1/budgets/${var.sync_id}/accounts/banksync' --header 'accept: application/json' --header 'budget-encryption-password: ${var.budget_encryption_password}' --header 'x-api-key: ${random_string.api-key.result}' + EOT + ] + } + } + } + } + } + } +} diff --git a/modules/kubernetes/actualbudget/main.tf b/modules/kubernetes/actualbudget/main.tf index fa5259ae..0226f1ba 100644 --- a/modules/kubernetes/actualbudget/main.tf +++ b/modules/kubernetes/actualbudget/main.tf @@ -1,5 +1,6 @@ variable "tls_secret_name" {} variable "tier" { type = string } +variable "credentials" { type = map(any) } # To create a new deployment: /** @@ -26,20 +27,24 @@ module "tls_secret" { # https://budget-viktor.viktorbarzin.me/ module "viktor" { - source = "./factory" - name = "viktor" - tag = "edge" - tls_secret_name = var.tls_secret_name - depends_on = [kubernetes_namespace.actualbudget] - tier = var.tier + source = "./factory" + name = "viktor" + tag = "edge" + tls_secret_name = var.tls_secret_name + depends_on = [kubernetes_namespace.actualbudget] + tier = var.tier + budget_encryption_password = lookup(var.credentials["viktor"], "password", null) + sync_id = lookup(var.credentials["viktor"], "sync_id", null) } # https://budget-anca.viktorbarzin.me/ module "anca" { - source = "./factory" - name = "anca" - tag = "edge" - tls_secret_name = var.tls_secret_name - depends_on = [kubernetes_namespace.actualbudget] - tier = var.tier + source = "./factory" + name = "anca" + tag = "edge" + tls_secret_name = var.tls_secret_name + depends_on = [kubernetes_namespace.actualbudget] + tier = var.tier + budget_encryption_password = lookup(var.credentials["anca"], "password", null) + sync_id = lookup(var.credentials["anca"], "sync_id", null) } diff --git a/modules/kubernetes/main.tf b/modules/kubernetes/main.tf index 3e6ebb82..21105323 100644 --- a/modules/kubernetes/main.tf +++ b/modules/kubernetes/main.tf @@ -111,6 +111,7 @@ variable "clickhouse_password" { type = string } variable "clickhouse_postgres_password" { type = string } variable "wealthfolio_password_hash" { type = string } variable "aiostreams_database_connection_string" { type = string } +variable "actualbudget_credentials" { type = map(any) } variable "defcon_level" { @@ -552,7 +553,7 @@ module "nginx-ingress" { module "crowdsec" { source = "./crowdsec" - tier = local.tiers.cluster + tier = local.tiers.core for_each = contains(local.active_modules, "crowdsec") ? { crowdsec = true } : {} tls_secret_name = var.tls_secret_name homepage_username = var.homepage_credentials["crowdsec"]["username"] @@ -809,6 +810,7 @@ module "actualbudget" { for_each = contains(local.active_modules, "actualbudget") ? { actualbudget = true } : {} tls_secret_name = var.tls_secret_name tier = local.tiers.edge + credentials = var.actualbudget_credentials depends_on = [null_resource.core_services] } diff --git a/modules/kubernetes/reverse_proxy/main.tf b/modules/kubernetes/reverse_proxy/main.tf index 3ea44e12..dd5fd4ef 100644 --- a/modules/kubernetes/reverse_proxy/main.tf +++ b/modules/kubernetes/reverse_proxy/main.tf @@ -82,7 +82,11 @@ module "idrac" { port = 443 tls_secret_name = var.tls_secret_name backend_protocol = "HTTPS" - depends_on = [kubernetes_namespace.reverse-proxy] + extra_annotations = { + # authentik causes 413; we don't need the header below + "nginx.ingress.kubernetes.io/auth-response-headers" : null + } + depends_on = [kubernetes_namespace.reverse-proxy] } # Can either listen on https or http; can't do both :/ @@ -96,23 +100,10 @@ module "tp-link-gateway" { backend_protocol = "HTTPS" depends_on = [kubernetes_namespace.reverse-proxy] protected = true - # Doesn't work due to 413 due to GA/authentik cookie - # additional_configuration_snippet = <<-EOF - # # 1. Try to extract the sysauth cookie and its value - # # This regex looks for 'sysauth=' followed by everything until a semicolon or end of string - # set $sysauth_only ""; - # if ($http_cookie ~* "sysauth=([^;]+)") { - # set $sysauth_only "sysauth=$1"; - # } - - # # 2. Overwrite the Cookie header. - # # If sysauth was found, only it is sent. If not found, no cookies are sent. - # proxy_set_header Cookie $sysauth_only; - # EOF - # extra_annotations = { - # client-header-buffer-size : "16k" - # large-client-header-buffers : "4 16k" - # } + extra_annotations = { + # authentik causes 413; we don't need the header below + "nginx.ingress.kubernetes.io/auth-response-headers" : null + } } # https://truenas.viktorbarzin.me/ diff --git a/modules/kubernetes/tuya-bridge/main.tf b/modules/kubernetes/tuya-bridge/main.tf index e685c59a..9ed60b19 100644 --- a/modules/kubernetes/tuya-bridge/main.tf +++ b/modules/kubernetes/tuya-bridge/main.tf @@ -30,7 +30,7 @@ resource "kubernetes_deployment" "tuya-bridge" { } } spec { - replicas = 1 + replicas = 3 selector { match_labels = { app = "tuya-bridge" diff --git a/terraform.tfstate b/terraform.tfstate index bcb43bcf..ab2ae087 100644 Binary files a/terraform.tfstate and b/terraform.tfstate differ diff --git a/terraform.tfvars b/terraform.tfvars index 984fd4e0..30ea3bbc 100644 Binary files a/terraform.tfvars and b/terraform.tfvars differ