diff --git a/live/backend.tf b/live/backend.tf deleted file mode 100644 index 31854d0..0000000 --- a/live/backend.tf +++ /dev/null @@ -1,15 +0,0 @@ -# Generated by Terragrunt. Sig: nIlQXj57tbuaRZEa -terraform { - backend "s3" { - access_key = "" - bucket = "tfstate-terragrunt-stage" - endpoint = "storage.yandexcloud.net" - force_path_style = true - key = "./terraform.tfstate" - region = "ru-central1" - secret_key = "" - skip_credentials_validation = true - skip_metadata_api_check = true - skip_region_validation = true - } -} diff --git a/live/namespace/terragrunt.hcl b/live/namespace/terragrunt.hcl new file mode 100644 index 0000000..7d57ac9 --- /dev/null +++ b/live/namespace/terragrunt.hcl @@ -0,0 +1,50 @@ +include "root" { + path = find_in_parent_folders() +} + +locals { + repo_root = try(get_repo_root(), "${get_terragrunt_dir()}/../..") + infra_config = yamldecode(file("${local.repo_root}/infrastructure.yaml")) + + env_override = trimspace(get_env("INFRA_ENV", "")) + env_from_file = try(trimspace(local.infra_config.current_environment), "") + available_envs = sort(keys(try(local.infra_config.environments, {}))) + env_fallback = try(local.available_envs[0], "") + + env_name = local.env_override != "" ? local.env_override : ( + local.env_from_file != "" ? local.env_from_file : local.env_fallback + ) + + env_config = try(local.infra_config.environments[local.env_name], {}) +} + +remote_state { + backend = "s3" + generate = { + path = "backend.tf" + if_exists = "overwrite_terragrunt" + } + config = { + endpoint = get_env("TF_STATE_S3_ENDPOINT", "") + bucket = get_env("TF_STATE_S3_BUCKET", "") + key = "${local.env_name}/namespace/terraform.tfstate" + region = get_env("TF_STATE_S3_REGION", "ru-central1") + access_key = get_env("S3_ACCESS_KEY", get_env("AWS_ACCESS_KEY_ID", "")) + secret_key = get_env("S3_SECRET_KEY", get_env("AWS_SECRET_ACCESS_KEY", "")) + skip_region_validation = true + skip_credentials_validation = true + skip_metadata_api_check = true + skip_bucket_root_access = true + force_path_style = true + } +} + +terraform { + source = "${get_terragrunt_dir()}/../../modules//k8s-namespace" +} + +inputs = { + namespaces = try(local.env_config.namespaces, []) + kubeconfig_path = get_env("KUBECONFIG", "") + kube_context = get_env("KUBE_CONTEXT", "") +} diff --git a/live/prod/database/terragrunt.hcl b/live/prod/database/terragrunt.hcl deleted file mode 100644 index 6c1089c..0000000 --- a/live/prod/database/terragrunt.hcl +++ /dev/null @@ -1,31 +0,0 @@ -include "root" { - path = find_in_parent_folders() -} - -include "env" { - path = find_in_parent_folders("env.hcl") - expose = true - merge_strategy = "deep" -} - -locals { - repo_root = try(get_repo_root(), "${get_terragrunt_dir()}/../../..") - infra_config = yamldecode(file("${local.repo_root}/infrastructure.yaml")) - env_name = basename(dirname(get_terragrunt_dir())) - env_config = local.infra_config.environments[local.env_name] -} - -terraform { - source = "${get_terragrunt_dir()}/../../../modules//yc-database" -} - -inputs = { - databases = local.env_config.databases - - yc_token = get_env("YC_TOKEN", "") - yc_service_account_key_file = get_env("YC_SERVICE_ACCOUNT_KEY_FILE", "") - yc_cloud_id = get_env("YC_CLOUD_ID", "") - yc_folder_id = get_env("YC_PROD_FOLDER_ID", get_env("YC_FOLDER_ID", "")) - kubeconfig_path = get_env("KUBECONFIG", "") - kube_context = get_env("KUBE_CONTEXT", "") -} diff --git a/live/prod/env.hcl b/live/prod/env.hcl deleted file mode 100644 index add8e01..0000000 --- a/live/prod/env.hcl +++ /dev/null @@ -1,12 +0,0 @@ -# Конфигурация для окружения prod -locals { - environment = "prod" - folder_id = get_env("YC_PROD_FOLDER_ID", "") - - # Общие параметры для всех компонентов в prod - common_tags = { - Environment = "prod" - Project = "terraform" - ManagedBy = "terraform" - } -} diff --git a/live/prod/kafka-topics/terragrunt.hcl b/live/prod/kafka-topics/terragrunt.hcl deleted file mode 100644 index b5b7248..0000000 --- a/live/prod/kafka-topics/terragrunt.hcl +++ /dev/null @@ -1,61 +0,0 @@ -include "root" { - path = find_in_parent_folders() -} - -include "env" { - path = find_in_parent_folders("env.hcl") - expose = true - merge_strategy = "deep" -} - -locals { - repo_root = try(get_repo_root(), "${get_terragrunt_dir()}/../../..") - infra_config = yamldecode(file("${local.repo_root}/infrastructure.yaml")) - env_name = basename(dirname(get_terragrunt_dir())) - env_config = local.infra_config.environments[local.env_name] - - kafka_topics = try(local.env_config.kafka.topics, []) - kafka_users = try(local.env_config.kafka.users, []) - - kafka_cluster_refs = { - for ref, cfg in local.env_config.kafka_cluster_refs : ref => merge(cfg, { - cluster_id = startswith(tostring(cfg.cluster_id), "env:") ? get_env(trimprefix(tostring(cfg.cluster_id), "env:"), "") : tostring(cfg.cluster_id) - }) - } -} - -terraform { - source = "${get_terragrunt_dir()}/../../../modules//kafka-topics-yc" -} - -generate "import_existing" { - path = "import_existing.tf" - if_exists = "overwrite" - contents = join("\n\n", [ - for user in local.kafka_users : <<-EOF - import { - to = yandex_mdb_kafka_user.this["${user.clusterRef}:${user.name}"] - id = "${local.kafka_cluster_refs[user.clusterRef].cluster_id}:${user.name}" - } - EOF - if try(user.adopt, false) - ]) -} - -inputs = { - environment = local.env_name - topics = local.kafka_topics - users = local.kafka_users - kafka_cluster_refs = local.kafka_cluster_refs - kafka_policy = local.env_config.kafka_policy - create_users = true - default_user_roles = ["ACCESS_ROLE_PRODUCER", "ACCESS_ROLE_CONSUMER"] - user_password_length = 32 - - yc_token = get_env("YC_TOKEN", "") - yc_service_account_key_file = get_env("YC_SERVICE_ACCOUNT_KEY_FILE", "") - yc_cloud_id = get_env("YC_CLOUD_ID", "") - yc_folder_id = get_env("YC_PROD_FOLDER_ID", get_env("YC_FOLDER_ID", "")) - kubeconfig_path = get_env("KUBECONFIG", "") - kube_context = get_env("KUBE_CONTEXT", "") -} diff --git a/live/prod/namespace/terragrunt.hcl b/live/prod/namespace/terragrunt.hcl deleted file mode 100644 index ed8567a..0000000 --- a/live/prod/namespace/terragrunt.hcl +++ /dev/null @@ -1,31 +0,0 @@ -include "root" { - path = find_in_parent_folders() -} - -include "env" { - path = find_in_parent_folders("env.hcl") - expose = true - merge_strategy = "deep" -} - -locals { - repo_root = try(get_repo_root(), "${get_terragrunt_dir()}/../../..") - infra_config = yamldecode(file("${local.repo_root}/infrastructure.yaml")) - env_name = basename(dirname(get_terragrunt_dir())) - env_config = local.infra_config.environments[local.env_name] -} - -terraform { - source = "${get_terragrunt_dir()}/../../../modules//k8s-namespace" -} - -inputs = { - namespaces = local.env_config.namespaces - - yc_token = get_env("YC_TOKEN", "") - yc_service_account_key_file = get_env("YC_SERVICE_ACCOUNT_KEY_FILE", "") - yc_cloud_id = get_env("YC_CLOUD_ID", "") - yc_folder_id = get_env("YC_PROD_FOLDER_ID", get_env("YC_FOLDER_ID", "")) - kubeconfig_path = get_env("KUBECONFIG", "") - kube_context = get_env("KUBE_CONTEXT", "") -} diff --git a/live/prod/rabbitmq/terragrunt.hcl b/live/prod/rabbitmq/terragrunt.hcl deleted file mode 100644 index 1707768..0000000 --- a/live/prod/rabbitmq/terragrunt.hcl +++ /dev/null @@ -1,52 +0,0 @@ -include "root" { - path = find_in_parent_folders() -} - -include "env" { - path = find_in_parent_folders("env.hcl") - expose = true - merge_strategy = "deep" -} - -locals { - repo_root = try(get_repo_root(), "${get_terragrunt_dir()}/../../..") - infra_config = yamldecode(file("${local.repo_root}/infrastructure.yaml")) - env_name = basename(dirname(get_terragrunt_dir())) - env_var_prefix = upper(replace(local.env_name, "-", "_")) - env_config = local.infra_config.environments[local.env_name] - rabbitmq_config = try(local.env_config.rabbitmq, {}) -} - -terraform { - source = "${get_terragrunt_dir()}/../../../modules//rabbitmq" -} - -inputs = { - environment = local.env_name - - management_endpoint = get_env("RABBITMQ_${local.env_var_prefix}_MANAGEMENT_ENDPOINT", try(local.rabbitmq_config.management_endpoint, "")) - management_username = get_env("RABBITMQ_${local.env_var_prefix}_MANAGEMENT_USERNAME", "") - management_password = get_env("RABBITMQ_${local.env_var_prefix}_MANAGEMENT_PASSWORD", "") - - amqp_host = get_env("RABBITMQ_${local.env_var_prefix}_AMQP_HOST", try(local.rabbitmq_config.amqp_host, "rabbitmq.rabbitmq.svc.cluster.local")) - amqp_port = tonumber(get_env("RABBITMQ_${local.env_var_prefix}_AMQP_PORT", tostring(try(local.rabbitmq_config.amqp_port, 5672)))) - - rabbitmq_policy = try(local.rabbitmq_config.policy, {}) - vhosts = try(local.rabbitmq_config.vhosts, []) - users = try(local.rabbitmq_config.users, []) - permissions = try(local.rabbitmq_config.permissions, []) - topic_permissions = try(local.rabbitmq_config.topic_permissions, []) - exchanges = try(local.rabbitmq_config.exchanges, []) - queues = try(local.rabbitmq_config.queues, []) - bindings = try(local.rabbitmq_config.bindings, []) - policies = try(local.rabbitmq_config.policies, []) - unmanaged_output_users = try(local.rabbitmq_config.unmanaged_output_users, []) - unmanaged_output_permissions = try(local.rabbitmq_config.unmanaged_output_permissions, []) - - yc_token = get_env("YC_TOKEN", "") - yc_service_account_key_file = get_env("YC_SERVICE_ACCOUNT_KEY_FILE", "") - yc_cloud_id = get_env("YC_CLOUD_ID", "") - yc_folder_id = get_env("YC_PROD_FOLDER_ID", get_env("YC_FOLDER_ID", "")) - kubeconfig_path = get_env("KUBECONFIG", "") - kube_context = get_env("KUBE_CONTEXT", "") -} diff --git a/live/prod/s3/terragrunt.hcl b/live/prod/s3/terragrunt.hcl deleted file mode 100644 index 37037d2..0000000 --- a/live/prod/s3/terragrunt.hcl +++ /dev/null @@ -1,33 +0,0 @@ -include "root" { - path = find_in_parent_folders() -} - -include "env" { - path = find_in_parent_folders("env.hcl") - expose = true - merge_strategy = "deep" -} - -locals { - repo_root = try(get_repo_root(), "${get_terragrunt_dir()}/../../..") - infra_config = yamldecode(file("${local.repo_root}/infrastructure.yaml")) - env_name = basename(dirname(get_terragrunt_dir())) - env_config = local.infra_config.environments[local.env_name] -} - -terraform { - source = "${get_terragrunt_dir()}/../../../modules//yc-s3" -} - -inputs = { - buckets = local.env_config.buckets - yc_service_accounts = try(local.env_config.yc_service_accounts, []) - folder_id = get_env("YC_PROD_FOLDER_ID", get_env("YC_FOLDER_ID", "")) - - yc_token = get_env("YC_TOKEN", "") - yc_service_account_key_file = get_env("YC_SERVICE_ACCOUNT_KEY_FILE", "") - yc_cloud_id = get_env("YC_CLOUD_ID", "") - yc_folder_id = get_env("YC_PROD_FOLDER_ID", get_env("YC_FOLDER_ID", "")) - kubeconfig_path = get_env("KUBECONFIG", "") - kube_context = get_env("KUBE_CONTEXT", "") -} diff --git a/live/prod/secrets/terragrunt.hcl b/live/prod/secrets/terragrunt.hcl deleted file mode 100644 index 08486c9..0000000 --- a/live/prod/secrets/terragrunt.hcl +++ /dev/null @@ -1,224 +0,0 @@ -include "root" { - path = find_in_parent_folders() -} - -include "env" { - path = find_in_parent_folders("env.hcl") - expose = true - merge_strategy = "deep" -} - -dependency "namespace" { - config_path = "../namespace" - mock_outputs = { - namespaces = {} - names = [] - } - mock_outputs_allowed_terraform_commands = ["init", "validate", "plan"] - skip_outputs = get_env("TG_SKIP_DEPENDENCY_OUTPUTS", "false") == "true" -} - -dependency "s3" { - config_path = "../s3" - mock_outputs = { - bucket_name = "mock-bucket" - access_key = "mock-access-key" - secret_key = "mock-secret-key" - buckets = {} - service_accounts = {} - } - mock_outputs_allowed_terraform_commands = ["init", "validate", "plan"] - skip_outputs = get_env("TG_SKIP_DEPENDENCY_OUTPUTS", "false") == "true" -} - -dependency "database" { - config_path = "../database" - mock_outputs = { - database_outputs_map = {} - } - mock_outputs_allowed_terraform_commands = ["init", "validate", "plan"] - skip_outputs = get_env("TG_SKIP_DEPENDENCY_OUTPUTS", "false") == "true" -} - -dependency "rabbitmq" { - config_path = "../rabbitmq" - mock_outputs = { - rabbitmq_outputs_map = {} - } - mock_outputs_allowed_terraform_commands = ["init", "validate", "plan"] - skip_outputs = get_env("TG_SKIP_DEPENDENCY_OUTPUTS", "false") == "true" -} - -dependency "kafka_topics" { - config_path = "../kafka-topics" - mock_outputs = { - kafka_outputs_map = {} - } - mock_outputs_allowed_terraform_commands = ["init", "validate", "plan"] - skip_outputs = get_env("TG_SKIP_DEPENDENCY_OUTPUTS", "false") == "true" -} - -dependency "valkey_users" { - config_path = "../valkey-users" - mock_outputs = { - valkey_outputs_map = {} - } - mock_outputs_allowed_terraform_commands = ["init", "validate", "plan"] - skip_outputs = get_env("TG_SKIP_DEPENDENCY_OUTPUTS", "false") == "true" -} - -locals { - yc_postgresql_ca_cert = <<-EOT ------BEGIN CERTIFICATE----- -MIIE3TCCAsWgAwIBAgIKPxb5sAAAAAAAFzANBgkqhkiG9w0BAQ0FADAfMR0wGwYD -VQQDExRZYW5kZXhJbnRlcm5hbFJvb3RDQTAeFw0xNzA2MjAxNjQ0MzdaFw0yNzA2 -MjAxNjU0MzdaMFUxEjAQBgoJkiaJk/IsZAEZFgJydTEWMBQGCgmSJomT8ixkARkW -BnlhbmRleDESMBAGCgmSJomT8ixkARkWAmxkMRMwEQYDVQQDEwpZYW5kZXhDTENB -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqgNnjk0JKPcbsk1+KG2t -eM1AfMnEe5RkAJuBBuwVV49snhcvO1jhKBx/pCnjr6biICc1/oAFDVgU8yVYYPwp -WZ2vH3ZtscjJ/RAT/NS9OKKG7kKknhFhVYxua5xhoIQmm6usBNYYiTcWoFm1eHC8 -I9oddOLSscZYbh3unVRvt+3V+drVmUx9oSUKpqMgfysiv1MN6zB3vq9TFkbhz53E -k0tEcV+W2NnDaeFhLKy284FDKLvOdTDj1EDsSAihxl7sNEKpupNuhgyy2siOqUb+ -d5mO/CRfaAKGg3E6hDM3pEi48E506dJdjPXWfHKSvuguMLRlb2RWdVocRZuyWxOh -0QIDAQABo4HkMIHhMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRMU5uItjx+ -TOicX1+ovC1Xq2PSnzAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8E -BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSrucX/oe/mUx0zOSKE -0XbUN04tajBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vY3Jscy55YW5kZXgucnUv -WWFuZGV4SW50ZXJuYWxSb290Q0EvWWFuZGV4SW50ZXJuYWxSb290Q0EuY3JsMA0G -CSqGSIb3DQEBDQUAA4ICAQAsR5Lb4Pv2FD0Kk+4oc1GEOnehxKLsQtdV81nrU+IV -l9pr2oNMdi8lwIolvHZRllLM4Ba5AcRH6YJ5fe7AjKm+5EdSkhqVWo2UOllRCbtS -wmL50+erOAkxstSlRkO6b8x1L0MOBKv54E5YcQ/Wwt27ldSb6RkEmJBGvmxObAaf -5zc51pqSqao9tnldYaCblEQ/Zmy43FliIpa2eUJoh8DqK8bVo2gcI3wbQ32tWs9u -wvKk8fo4lAdhCwhv+QHuqau1VAY9hPU106bsFIDUmijTMxjAobKBi6CkIX6EbNHU -Jv4DzYVLlDd2y0CADdn2F6I70xpCBn5cquSGuvFbqZjQDmIHwb7WQSxadkiGRWfc -zVTnmiHjJONJJIpE2t+FOV3hc+8o98OzOtNaH2QQ9j6dnKvtIGKGFeNSDp0vXPOi -QhHiIyuB7eWx+g2whktQ74UCpGDSXYnEW3s8w5wezVWIEmouq7q4rCEkTNvJ7Ico -43AgUdPzAFS2zYktw1C+cbUALM8smvXbXrXOBzMmscjIhtXvLMrpPeh23VfdJfQB -0rN2BmRCLUE8JOV+o0k98XMm83oN+lGkL1l+hyoj3ok1uI3JrsWOcDyjOds3ptcN -KimJLm27ndjcxDNo/iA6gefMJuCxFRaqI+eF4P0jSkMgnnQqZkvLGFuHCw8eRDhm -bw== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFGTCCAwGgAwIBAgIQJMM7ZIy2SYxCBgK7WcFwnjANBgkqhkiG9w0BAQ0FADAf -MR0wGwYDVQQDExRZYW5kZXhJbnRlcm5hbFJvb3RDQTAeFw0xMzAyMTExMzQxNDNa -Fw0zMzAyMTExMzUxNDJaMB8xHTAbBgNVBAMTFFlhbmRleEludGVybmFsUm9vdENB -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgb4xoQjBQ7oEFk8EHVGy -1pDEmPWw0Wgw5nX9RM7LL2xQWyUuEq+Lf9Dgh+O725aZ9+SO2oEs47DHHt81/fne -5N6xOftRrCpy8hGtUR/A3bvjnQgjs+zdXvcO9cTuuzzPTFSts/iZATZsAruiepMx -SGj9S1fGwvYws/yiXWNoNBz4Tu1Tlp0g+5fp/ADjnxc6DqNk6w01mJRDbx+6rlBO -aIH2tQmJXDVoFdrhmBK9qOfjxWlIYGy83TnrvdXwi5mKTMtpEREMgyNLX75UjpvO -NkZgBvEXPQq+g91wBGsWIE2sYlguXiBniQgAJOyRuSdTxcJoG8tZkLDPRi5RouWY -gxXr13edn1TRDGco2hkdtSUBlajBMSvAq+H0hkslzWD/R+BXkn9dh0/DFnxVt4XU -5JbFyd/sKV/rF4Vygfw9ssh1ZIWdqkfZ2QXOZ2gH4AEeoN/9vEfUPwqPVzL0XEZK -r4s2WjU9mE5tHrVsQOZ80wnvYHYi2JHbl0hr5ghs4RIyJwx6LEEnj2tzMFec4f7o -dQeSsZpgRJmpvpAfRTxhIRjZBrKxnMytedAkUPguBQwjVCn7+EaKiJfpu42JG8Mm -+/dHi+Q9Tc+0tX5pKOIpQMlMxMHw8MfPmUjC3AAd9lsmCtuybYoeN2IRdbzzchJ8 -l1ZuoI3gH7pcIeElfVSqSBkCAwEAAaNRME8wCwYDVR0PBAQDAgGGMA8GA1UdEwEB -/wQFMAMBAf8wHQYDVR0OBBYEFKu5xf+h7+ZTHTM5IoTRdtQ3Ti1qMBAGCSsGAQQB -gjcVAQQDAgEAMA0GCSqGSIb3DQEBDQUAA4ICAQAVpyJ1qLjqRLC34F1UXkC3vxpO -nV6WgzpzA+DUNog4Y6RhTnh0Bsir+I+FTl0zFCm7JpT/3NP9VjfEitMkHehmHhQK -c7cIBZSF62K477OTvLz+9ku2O/bGTtYv9fAvR4BmzFfyPDoAKOjJSghD1p/7El+1 -eSjvcUBzLnBUtxO/iYXRNo7B3+1qo4F5Hz7rPRLI0UWW/0UAfVCO2fFtyF6C1iEY -/q0Ldbf3YIaMkf2WgGhnX9yH/8OiIij2r0LVNHS811apyycjep8y/NkG4q1Z9jEi -VEX3P6NEL8dWtXQlvlNGMcfDT3lmB+tS32CPEUwce/Ble646rukbERRwFfxXojpf -C6ium+LtJc7qnK6ygnYF4D6mz4H+3WaxJd1S1hGQxOb/3WVw63tZFnN62F6/nc5g -6T44Yb7ND6y3nVcygLpbQsws6HsjX65CoSjrrPn0YhKxNBscF7M7tLTW/5LK9uhk -yjRCkJ0YagpeLxfV1l1ZJZaTPZvY9+ylHnWHhzlq0FzcrooSSsp4i44DB2K7O2ID -87leymZkKUY6PMDa4GkDJx0dG4UXDhRETMf+NkYgtLJ+UIzMNskwVDcxO4kVL+Hi -Pj78bnC5yCw8P5YylR45LdxLzLO68unoXOyFz1etGXzszw8lJI9LNubYxk77mK8H -LpuQKbSbIERsmR+QqQ== ------END CERTIFICATE----- -EOT - - repo_root = try(get_repo_root(), "${get_terragrunt_dir()}/../../..") - env_name = basename(dirname(get_terragrunt_dir())) - - # Единый декларативный источник: форма + данные секретов в одном sops-файле - # infrastructure-secrets.yaml (шифруется только data). В CI файл расшифровывается - # заранее и путь передаётся через INFRA_SECRET_VALUES_FILE; локально - fallback - # на прямую расшифровку через sops_decrypt_file. Значения передаются в модуль - # только через inputs (эфемерно), чтобы расшифрованные данные не оседали на диске. - secret_values_env_file = get_env("INFRA_SECRET_VALUES_FILE", "") - secret_values_raw = local.secret_values_env_file != "" ? file(local.secret_values_env_file) : try(sops_decrypt_file("${local.repo_root}/infrastructure-secrets.yaml"), "") - secret_values_config = try(yamldecode(local.secret_values_raw), {}) - secret_values_env = try(local.secret_values_config.environments[local.env_name], {}) - - secrets = try(local.secret_values_env.secrets, []) - - # Нормализованный список для модуля. Ключ ресурса теперь namespace/name, - # resource_key больше не используется. - secrets_input = [ - for secret in local.secrets : { - name = secret.name - namespace = secret.namespace - type = try(secret.type, "opaque") - registry_url = try(secret.registry_url, "") - data = try(secret.data, {}) - depends_on = try(secret.depends_on, {}) - yc_sa_key_names = try(secret.yc_sa_key_names, {}) - custom_keys = try(secret.custom_keys, {}) - constant_keys = try(secret.constant_keys, {}) - random_keys = try(secret.random_keys, {}) - credential_keys = try(secret.credential_keys, {}) - labels = try(secret.labels, {}) - annotations = try(secret.annotations, {}) - ignore_changes = try(secret.lifecycle.ignore_changes, false) - } - ] -} - -generate "import_existing" { - path = "import_existing.tf" - if_exists = "overwrite" - contents = join("\n\n", [ - for secret in local.secrets : <<-EOF - import { - to = kubernetes_secret.without_ignore["${secret.namespace}/${secret.name}"] - id = "${secret.namespace}/${secret.name}" - } - EOF - if try(secret.adopt, false) && try(secret.lifecycle.ignore_changes, false) == false - ]) -} - -terraform { - source = "${get_terragrunt_dir()}/../../../modules//k8s-secret" -} - -inputs = { - secrets = local.secrets_input - - s3_outputs = { - bucket_name = dependency.s3.outputs.bucket_name - access_key = dependency.s3.outputs.access_key - secret_key = dependency.s3.outputs.secret_key - } - - s3_buckets_map = dependency.s3.outputs.buckets - yc_service_accounts_map = try(dependency.s3.outputs.service_accounts, {}) - - database_outputs_map = dependency.database.outputs.database_outputs_map - rabbitmq_outputs_map = try(dependency.rabbitmq.outputs.rabbitmq_outputs_map, {}) - kafka_outputs_map = try(dependency.kafka_topics.outputs.kafka_outputs_map, {}) - valkey_outputs_map = dependency.valkey_users.outputs.valkey_outputs_map - - constants = { - s3_endpoint = "https://storage.yandexcloud.net" - postgres_port = "6432" - postgres_ca = local.yc_postgresql_ca_cert - valkey_port = "6380" - valkey_ca = local.yc_postgresql_ca_cert - } - - env_vars = { - DOCKER_REGISTRY_USERNAME = get_env("DOCKER_REGISTRY_USERNAME", "") - DOCKER_REGISTRY_PASSWORD = get_env("DOCKER_REGISTRY_PASSWORD", "") - } - - yc_token = get_env("YC_TOKEN", "") - yc_service_account_key_file = get_env("YC_SERVICE_ACCOUNT_KEY_FILE", "") - yc_cloud_id = get_env("YC_CLOUD_ID", "") - yc_folder_id = get_env("YC_PROD_FOLDER_ID", get_env("YC_FOLDER_ID", "")) - kubeconfig_path = get_env("KUBECONFIG", "") - kube_context = get_env("KUBE_CONTEXT", "") -} diff --git a/live/prod/valkey-users/terragrunt.hcl b/live/prod/valkey-users/terragrunt.hcl deleted file mode 100644 index 3be8c02..0000000 --- a/live/prod/valkey-users/terragrunt.hcl +++ /dev/null @@ -1,31 +0,0 @@ -include "root" { - path = find_in_parent_folders() -} - -include "env" { - path = find_in_parent_folders("env.hcl") - expose = true - merge_strategy = "deep" -} - -locals { - repo_root = try(get_repo_root(), "${get_terragrunt_dir()}/../../..") - infra_config = yamldecode(file("${local.repo_root}/infrastructure.yaml")) - env_name = basename(dirname(get_terragrunt_dir())) - env_config = local.infra_config.environments[local.env_name] -} - -terraform { - source = "${get_terragrunt_dir()}/../../../modules//yc-valkey-user" -} - -inputs = { - valkey_users = try(local.env_config.valkey_users, []) - - yc_token = get_env("YC_TOKEN", "") - yc_service_account_key_file = get_env("YC_SERVICE_ACCOUNT_KEY_FILE", "") - yc_cloud_id = get_env("YC_CLOUD_ID", "") - yc_folder_id = get_env("YC_PROD_FOLDER_ID", get_env("YC_FOLDER_ID", "")) - kubeconfig_path = get_env("KUBECONFIG", "") - kube_context = get_env("KUBE_CONTEXT", "") -} diff --git a/live/provider.tf b/live/provider.tf deleted file mode 100644 index 7e6317f..0000000 --- a/live/provider.tf +++ /dev/null @@ -1,63 +0,0 @@ -# Generated by Terragrunt. Sig: nIlQXj57tbuaRZEa -terraform { - required_version = ">= 1.0" - - required_providers { - yandex = { - source = "yandex-cloud/yandex" - version = "~> 0.100" - } - kubernetes = { - source = "hashicorp/kubernetes" - version = "~> 2.23" - } - random = { - source = "hashicorp/random" - version = "~> 3.1" - } - } -} - -provider "yandex" { - token = var.yc_token != "" ? var.yc_token : null - cloud_id = var.yc_cloud_id != "" ? var.yc_cloud_id : null - folder_id = var.yc_folder_id != "" ? var.yc_folder_id : null - zone = "ru-central1-a" -} - -provider "kubernetes" { - # Если kubeconfig_path не указан, провайдер использует дефолтный путь ~/.kube/config - # Если указан, берем только первый путь (KUBECONFIG может содержать несколько путей через :) - config_path = var.kubeconfig_path != "" ? split(":", var.kubeconfig_path)[0] : null - config_context = var.kube_context != "" ? var.kube_context : null -} - -variable "yc_token" { - type = string - default = "" - description = "Yandex Cloud token" -} - -variable "yc_cloud_id" { - type = string - default = "" - description = "Yandex Cloud ID" -} - -variable "yc_folder_id" { - type = string - default = "" - description = "Yandex Cloud folder ID" -} - -variable "kubeconfig_path" { - type = string - default = "" - description = "Path to kubeconfig file" -} - -variable "kube_context" { - type = string - default = "" - description = "Kubernetes context" -} diff --git a/live/vault-secrets/terragrunt.hcl b/live/vault-secrets/terragrunt.hcl new file mode 100644 index 0000000..aa2459c --- /dev/null +++ b/live/vault-secrets/terragrunt.hcl @@ -0,0 +1,69 @@ +include "root" { + path = find_in_parent_folders() +} + +locals { + repo_root = try(get_repo_root(), "${get_terragrunt_dir()}/../..") + infra_config = yamldecode(file("${local.repo_root}/infrastructure.yaml")) + secrets_file = "${local.repo_root}/infrastructure-secrets.yaml" + + secrets_config = try(yamldecode(sops_decrypt_file(local.secrets_file)), {}) + + env_override = trimspace(get_env("INFRA_ENV", "")) + env_from_file = try(trimspace(local.infra_config.current_environment), "") + available_envs = sort(keys(try(local.secrets_config.environments, {}))) + env_fallback = try(local.available_envs[0], "") + + env_name = local.env_override != "" ? local.env_override : ( + local.env_from_file != "" ? local.env_from_file : local.env_fallback + ) + + env_config = try(local.secrets_config.environments[local.env_name], {}) + vault_config = try(local.env_config.vault, {}) + vault_enabled = try(local.vault_config.enabled, false) +} + +remote_state { + backend = "s3" + generate = { + path = "backend.tf" + if_exists = "overwrite_terragrunt" + } + config = { + endpoint = get_env("TF_STATE_S3_ENDPOINT", "") + bucket = get_env("TF_STATE_S3_BUCKET", "") + key = "${local.env_name}/vault-secrets/terraform.tfstate" + region = get_env("TF_STATE_S3_REGION", "ru-central1") + access_key = get_env("S3_ACCESS_KEY", get_env("AWS_ACCESS_KEY_ID", "")) + secret_key = get_env("S3_SECRET_KEY", get_env("AWS_SECRET_ACCESS_KEY", "")) + skip_region_validation = true + skip_credentials_validation = true + skip_metadata_api_check = true + skip_bucket_root_access = true + force_path_style = true + } +} + +terraform { + source = "${get_terragrunt_dir()}/../../modules//vault-platform-secrets" +} + +inputs = { + vault_kv_mount = try(local.vault_config.kv_mount, "secrets") + path_prefix = try(local.vault_config.path_prefix, "vault") + common = try(local.vault_config.common, {}) + infra = try(local.vault_config.infrastructure, {}) + applications = try(local.vault_config.applications, {}) + default_rsa_bits = try(local.vault_config.default_rsa_bits, 4096) + kubernetes_auth_backend = try(local.vault_config.kubernetes_auth_backend, "kubernetes") + + create_regcred = local.vault_enabled && try(local.vault_config.features.create_regcred, true) + create_smtp_auth = local.vault_enabled && try(local.vault_config.features.create_smtp_auth, true) + create_django_auth = local.vault_enabled && try(local.vault_config.features.create_django_auth, true) + create_rsa_keys = local.vault_enabled && try(local.vault_config.features.create_rsa_keys, true) + create_infra_secrets = local.vault_enabled && try(local.vault_config.features.create_infra_secrets, true) + create_infra_policies = local.vault_enabled && try(local.vault_config.features.create_infra_policies, true) + create_infra_roles = local.vault_enabled && try(local.vault_config.features.create_infra_roles, true) + create_app_policies = local.vault_enabled && try(local.vault_config.features.create_app_policies, true) + create_app_roles = local.vault_enabled && try(local.vault_config.features.create_app_roles, true) +} diff --git a/modules/vault-platform-secrets/main.tf b/modules/vault-platform-secrets/main.tf new file mode 100644 index 0000000..84a47b2 --- /dev/null +++ b/modules/vault-platform-secrets/main.tf @@ -0,0 +1,1272 @@ +locals { + regcred_input = try(var.common.regcred, {}) + regcred_manual = try(trimspace(local.regcred_input.dockerconfigjson), "") + regcred_generate = try(local.regcred_input.generate, false) + + regcred_server = try(local.regcred_input.server, "https://index.docker.io/v1/") + regcred_username = try(local.regcred_input.username, "") + regcred_password_input = try(local.regcred_input.password, "") + regcred_email = try(local.regcred_input.email, "") + regcred_password = local.regcred_password_input != "" ? local.regcred_password_input : try(random_password.regcred_password[0].result, "") + regcred_auth = base64encode("${local.regcred_username}:${local.regcred_password}") + regcred_generated = jsonencode({ + auths = { + (local.regcred_server) = { + username = local.regcred_username + password = local.regcred_password + email = local.regcred_email + auth = local.regcred_auth + } + } + }) + regcred_value = local.regcred_manual != "" ? local.regcred_manual : ( + local.regcred_generate ? local.regcred_generated : "CHANGE_ME" + ) + + smtp_input = merge(try(var.common.smtp_secret, {}), try(var.common.smtp_auth, {})) + smtp_manual = try(trimspace(local.smtp_input.env_json), "") + smtp_generate = try(local.smtp_input.generate, false) + + smtp_password_input = try(local.smtp_input.password, "") + smtp_password = local.smtp_password_input != "" ? local.smtp_password_input : try(random_password.smtp_password[0].result, "") + smtp_generated_obj = { + SMTP_HOST = try(local.smtp_input.host, "smtp.placeholder.local") + SMTP_PORT = tostring(try(local.smtp_input.port, 587)) + SMTP_USERNAME = try(local.smtp_input.username, "placeholder") + SMTP_PASSWORD = local.smtp_password + SMTP_FROM_EMAIL = try(local.smtp_input.from_email, "noreply@example.local") + SMTP_USE_TLS = tostring(try(local.smtp_input.use_tls, true)) + } + smtp_generated = jsonencode(local.smtp_generated_obj) + smtp_value = local.smtp_manual != "" ? local.smtp_manual : ( + local.smtp_generate ? local.smtp_generated : "{}" + ) + + django_input = try(var.common.django_auth, {}) + django_manual = try(trimspace(local.django_input.token), "") + django_generate = try(local.django_input.generate, false) + django_extra = try(local.django_input.extra, {}) + django_token = local.django_manual != "" ? local.django_manual : ( + local.django_generate ? try(random_password.django_token[0].result, "") : "CHANGE_ME" + ) + django_username_manual = try(trimspace(local.django_input.username), "") + django_password_manual = try(trimspace(local.django_input.password), "") + django_token_decoded = can(base64decode(local.django_token)) ? base64decode(local.django_token) : "" + django_token_parts = local.django_token_decoded != "" ? split(":", local.django_token_decoded) : [] + django_username = local.django_username_manual != "" ? local.django_username_manual : ( + length(local.django_token_parts) > 0 ? local.django_token_parts[0] : "" + ) + django_password = local.django_password_manual != "" ? local.django_password_manual : ( + length(local.django_token_parts) > 1 ? join(":", slice(local.django_token_parts, 1, length(local.django_token_parts))) : "" + ) + + rsa_input = try(var.common.rsa_keys, {}) + rsa_generate = try(local.rsa_input.generate, false) + rsa_bits = try(local.rsa_input.bits, var.default_rsa_bits) + rsa_public_manual = try(trimspace(local.rsa_input.public_key), "") + rsa_private_manual = try(trimspace(local.rsa_input.private_key), "") + rsa_public = local.rsa_public_manual != "" ? local.rsa_public_manual : ( + local.rsa_generate ? try(tls_private_key.rsa_keys[0].public_key_pem, "") : "CHANGE_ME_PUBLIC" + ) + rsa_private = local.rsa_private_manual != "" ? local.rsa_private_manual : ( + local.rsa_generate ? try(tls_private_key.rsa_keys[0].private_key_pem, "") : "CHANGE_ME_PRIVATE" + ) + + infra_defaults = { + postgresql = { + enabled = true + admin = { + enabled = true + generate = false + postgres_password = "" + } + s3 = { + enabled = false + bucket = "" + endpoint = "https://storage.yandexcloud.net" + region = "ru-central1" + access_key = "" + secret_key = "" + } + users = { + enabled = true + generate = false + values = {} + } + apps = {} + role = { + enabled = true + name = "postgresql" + service_account_names = ["postgresql"] + service_account_namespaces = ["postgresql"] + token_ttl = "24h" + } + } + rabbitmq = { + enabled = true + auth = { + enabled = true + generate = false + username = "" + password = "" + } + apps = {} + role = { + enabled = true + name = "rabbitmq" + service_account_names = ["rabbitmq"] + service_account_namespaces = ["rabbitmq"] + token_ttl = "24h" + } + } + minio = { + enabled = true + admin = { + enabled = true + generate = false + root_user = "" + root_password = "" + } + apps = {} + role = { + enabled = true + name = "minio" + service_account_names = ["minio-sa"] + service_account_namespaces = ["minio"] + token_ttl = "24h" + } + } + kafka = { + enabled = true + bootstrap = { + enabled = true + generate = false + cluster_id = "" + controller_password = "" + inter_broker_password = "" + } + apps = {} + role = { + enabled = true + name = "kafka" + service_account_names = ["kafka-kafka-contour"] + service_account_namespaces = ["kafka"] + token_ttl = "24h" + } + } + zitadel = { + enabled = true + postgresql = { + enabled = true + generate = false + password = "" + } + role = { + enabled = true + name = "zitadel" + service_account_names = ["zitadel-idp-contour"] + service_account_namespaces = ["zitadel"] + token_ttl = "24h" + } + } + camunda = { + enabled = true + identity_components = { + enabled = true + generate = false + values = {} + } + identity_firstuser = { + enabled = true + generate = false + identity_firstuser_password = "" + } + keycloak_admin = { + enabled = true + generate = false + admin_password = "" + } + postgresql = { + enabled = true + generate = false + password = "" + postgres_password = "" + } + role = { + enabled = true + name = "camunda" + service_account_names = ["camunda-connectors", "camunda-identity", "camunda-operate", "camunda-optimize", "camunda-tasklist", "camunda-postgresql", "camunda-keycloak", "camunda-identity-postgresql"] + service_account_namespaces = ["camunda"] + token_ttl = "24h" + } + } + keycloak = { + enabled = false + admin = { + enabled = true + generate = false + password = "" + } + postgresql = { + enabled = true + generate = false + host = "" + port = "" + user = "" + database = "" + password = "" + } + role = { + enabled = true + name = "keycloak" + service_account_names = ["keycloak-keycloak-contour"] + service_account_namespaces = ["keycloak"] + token_ttl = "24h" + } + } + } + + infra = { + for name, cfg in local.infra_defaults : + name => merge(cfg, try(var.infra[name], {})) + } + + postgresql_cfg = local.infra.postgresql + rabbitmq_cfg = local.infra.rabbitmq + minio_cfg = local.infra.minio + kafka_cfg = local.infra.kafka + zitadel_cfg = local.infra.zitadel + camunda_cfg = local.infra.camunda + keycloak_cfg = local.infra.keycloak + + postgresql_admin_cfg = merge(try(local.postgresql_cfg.admin, {}), try(var.infra.postgresql.admin, {})) + postgresql_s3_cfg = merge(try(local.postgresql_cfg.s3, {}), try(var.infra.postgresql.s3, {})) + postgresql_users_cfg = merge(try(local.postgresql_cfg.users, {}), try(var.infra.postgresql.users, {})) + postgresql_apps_cfg = try(local.postgresql_cfg.apps, {}) + rabbitmq_auth_cfg = merge(try(local.rabbitmq_cfg.auth, {}), try(var.infra.rabbitmq.auth, {})) + rabbitmq_apps_cfg = try(local.rabbitmq_cfg.apps, {}) + minio_admin_cfg = merge(try(local.minio_cfg.admin, {}), try(var.infra.minio.admin, {})) + minio_apps_cfg = try(local.minio_cfg.apps, {}) + kafka_bootstrap_cfg = merge(try(local.kafka_cfg.bootstrap, {}), try(var.infra.kafka.bootstrap, {})) + kafka_apps_cfg = try(local.kafka_cfg.apps, {}) + zitadel_pg_cfg = merge(try(local.zitadel_cfg.postgresql, {}), try(var.infra.zitadel.postgresql, {})) + camunda_identity_components_cfg = merge(try(local.camunda_cfg.identity_components, {}), try(var.infra.camunda.identity_components, {})) + camunda_identity_firstuser_cfg = merge(try(local.camunda_cfg.identity_firstuser, {}), try(var.infra.camunda.identity_firstuser, {})) + camunda_keycloak_cfg = merge(try(local.camunda_cfg.keycloak_admin, {}), try(var.infra.camunda.keycloak_admin, {})) + camunda_pg_cfg = merge(try(local.camunda_cfg.postgresql, {}), try(var.infra.camunda.postgresql, {})) + keycloak_admin_cfg = merge(try(local.keycloak_cfg.admin, {}), try(var.infra.keycloak.admin, {})) + keycloak_pg_cfg = merge(try(local.keycloak_cfg.postgresql, {}), try(var.infra.keycloak.postgresql, {})) + + postgresql_admin_password = trimspace(try(local.postgresql_admin_cfg.postgres_password, "")) != "" ? trimspace(local.postgresql_admin_cfg.postgres_password) : ( + try(local.postgresql_admin_cfg.generate, false) ? try(random_password.postgresql_admin_password[0].result, "") : "" + ) + postgresql_apps = { + for app, cfg in local.postgresql_apps_cfg : + app => merge( + { + enabled = true + generate = false + username = "" + password = "" + password_key = "" + }, + cfg, + { + username = trimspace(try(cfg.username, "")) != "" ? trimspace(try(cfg.username, "")) : app + password_key = trimspace(try(cfg.password_key, "")) != "" ? trimspace(try(cfg.password_key, "")) : app + password = trimspace(try(cfg.password, "")) != "" ? trimspace(try(cfg.password, "")) : ( + try(cfg.generate, false) ? try(random_password.postgresql_app_password[app].result, "") : "" + ) + } + ) + } + postgresql_app_user_values = { + for app, cfg in local.postgresql_apps : + try(cfg.password_key, app) => try(cfg.password, "") + if try(cfg.enabled, true) && ( + try(local.postgresql_apps_cfg[app].generate, false) || + trimspace(try(local.postgresql_apps_cfg[app].password, "")) != "" + ) + } + postgresql_users_values = merge( + try(local.postgresql_users_cfg.values, {}), + local.postgresql_app_user_values, + ) + postgresql_users_has_values = ( + length(keys(try(local.postgresql_users_cfg.values, {}))) > 0 || + length(keys({ + for app, cfg in local.postgresql_apps_cfg : + app => cfg + if try(cfg.enabled, true) && ( + try(cfg.generate, false) || + trimspace(try(cfg.password, "")) != "" + ) + })) > 0 + ) + + rabbitmq_password = trimspace(try(local.rabbitmq_auth_cfg.password, "")) != "" ? trimspace(local.rabbitmq_auth_cfg.password) : ( + try(local.rabbitmq_auth_cfg.generate, false) ? try(random_password.rabbitmq_password[0].result, "") : "" + ) + rabbitmq_apps = { + for app, cfg in local.rabbitmq_apps_cfg : + app => merge( + { + enabled = true + generate = false + username = "" + password = "" + vhost = "/" + permissions = { + configure = ".*" + write = ".*" + read = ".*" + } + vhosts = [] + }, + cfg, + { + username = trimspace(try(cfg.username, "")) != "" ? trimspace(try(cfg.username, "")) : app + password = trimspace(try(cfg.password, "")) != "" ? trimspace(try(cfg.password, "")) : ( + try(cfg.generate, false) ? try(random_password.rabbitmq_app_password[app].result, "") : "" + ) + vhost = trimspace(try(cfg.vhost, "")) != "" ? trimspace(try(cfg.vhost, "")) : "/" + permissions = merge( + { + configure = ".*" + write = ".*" + read = ".*" + }, + try(cfg.permissions, {}) + ) + vhosts = length(try(cfg.vhosts, [])) > 0 ? [ + for vh in try(cfg.vhosts, []) : { + name = trimspace(try(vh.name, "")) != "" ? trimspace(try(vh.name, "")) : (trimspace(try(cfg.vhost, "")) != "" ? trimspace(try(cfg.vhost, "")) : "/") + permissions = merge( + { + configure = ".*" + write = ".*" + read = ".*" + }, + try(vh.permissions, {}) + ) + } + ] : [] + } + ) + } + + minio_root_user = trimspace(try(local.minio_admin_cfg.root_user, "")) != "" ? trimspace(local.minio_admin_cfg.root_user) : ( + try(local.minio_admin_cfg.generate, false) ? try(random_password.minio_root_user[0].result, "") : "" + ) + minio_root_password = trimspace(try(local.minio_admin_cfg.root_password, "")) != "" ? trimspace(local.minio_admin_cfg.root_password) : ( + try(local.minio_admin_cfg.generate, false) ? try(random_password.minio_root_password[0].result, "") : "" + ) + minio_apps = { + for app, cfg in local.minio_apps_cfg : + app => merge( + { + enabled = true + generate = false + username = "" + password = "" + access_key = "" + secret_key = "" + policy = "readwrite" + buckets = [] + client = { + endpoint = "http://minio.minio.svc.cluster.local:9000" + region = "us-east-1" + } + }, + cfg, + { + username = trimspace(try(cfg.username, "")) != "" ? trimspace(try(cfg.username, "")) : app + password = trimspace(try(cfg.password, "")) != "" ? trimspace(try(cfg.password, "")) : ( + try(cfg.generate, false) ? try(random_password.minio_app_password[app].result, "") : "" + ) + access_key = trimspace(try(cfg.access_key, "")) != "" ? trimspace(try(cfg.access_key, "")) : ( + trimspace(try(cfg.username, "")) != "" ? trimspace(try(cfg.username, "")) : app + ) + secret_key = trimspace(try(cfg.secret_key, "")) != "" ? trimspace(try(cfg.secret_key, "")) : ( + trimspace(try(cfg.password, "")) != "" ? trimspace(try(cfg.password, "")) : ( + try(cfg.generate, false) ? try(random_password.minio_app_password[app].result, "") : "" + ) + ) + buckets = [ + for bucket in try(cfg.buckets, []) : ( + can(bucket.name) ? merge( + { + name = "" + policy = "private" + versioning = false + }, + bucket + ) : { + name = tostring(bucket) + policy = "private" + versioning = false + } + ) + if trimspace(can(bucket.name) ? try(bucket.name, "") : tostring(bucket)) != "" + ] + client = merge( + { + endpoint = "http://minio.minio.svc.cluster.local:9000" + region = "us-east-1" + }, + try(cfg.client, {}) + ) + } + ) + } + + kafka_cluster_id = trimspace(try(local.kafka_bootstrap_cfg.cluster_id, "")) != "" ? trimspace(local.kafka_bootstrap_cfg.cluster_id) : ( + try(local.kafka_bootstrap_cfg.generate, false) ? try(random_password.kafka_cluster_id[0].result, "") : "" + ) + kafka_controller_password = trimspace(try(local.kafka_bootstrap_cfg.controller_password, "")) != "" ? trimspace(local.kafka_bootstrap_cfg.controller_password) : ( + try(local.kafka_bootstrap_cfg.generate, false) ? try(random_password.kafka_controller_password[0].result, "") : "" + ) + kafka_inter_broker_password = trimspace(try(local.kafka_bootstrap_cfg.inter_broker_password, "")) != "" ? trimspace(local.kafka_bootstrap_cfg.inter_broker_password) : ( + try(local.kafka_bootstrap_cfg.generate, false) ? try(random_password.kafka_inter_broker_password[0].result, "") : "" + ) + kafka_apps = { + for app, cfg in local.kafka_apps_cfg : + app => merge( + { + enabled = true + generate = false + username = "" + password = "" + auth = { + bootstrap_servers = "kafka-kafka-contour-controller-headless.kafka.svc.cluster.local:9094" + security_protocol = "SASL_PLAINTEXT" + sasl_mechanism = "SCRAM-SHA-512" + } + topics = [] + }, + cfg, + { + username = trimspace(try(cfg.username, "")) != "" ? trimspace(try(cfg.username, "")) : app + password = trimspace(try(cfg.password, "")) != "" ? trimspace(try(cfg.password, "")) : ( + try(cfg.generate, false) ? try(random_password.kafka_app_password[app].result, "") : "" + ) + auth = merge( + { + bootstrap_servers = "kafka-kafka-contour-controller-headless.kafka.svc.cluster.local:9094" + security_protocol = "SASL_PLAINTEXT" + sasl_mechanism = "SCRAM-SHA-512" + }, + try(cfg.auth, {}) + ) + topics = [ + for topic in try(cfg.topics, []) : merge( + { + name = "" + partitions = 3 + replication_factor = 1 + configs = {} + }, + topic + ) + if trimspace(try(topic.name, "")) != "" + ] + } + ) + } + + zitadel_pg_password = trimspace(try(local.zitadel_pg_cfg.password, "")) != "" ? trimspace(local.zitadel_pg_cfg.password) : ( + trimspace(try(local.postgresql_users_values["zitadel"], "")) != "" ? trimspace(try(local.postgresql_users_values["zitadel"], "")) : ( + try(local.zitadel_pg_cfg.generate, false) ? try(random_password.zitadel_pg_password[0].result, "") : "" + ) + ) + + camunda_keycloak_admin_password = trimspace(try(local.camunda_keycloak_cfg.admin_password, "")) != "" ? trimspace(local.camunda_keycloak_cfg.admin_password) : ( + try(local.camunda_keycloak_cfg.generate, false) ? try(random_password.camunda_keycloak_admin_password[0].result, "") : "" + ) + + camunda_identity_components = { + "connectors-secret" = try(local.camunda_identity_components_cfg.values["connectors-secret"], "") != "" ? local.camunda_identity_components_cfg.values["connectors-secret"] : (try(local.camunda_identity_components_cfg.generate, false) ? try(random_password.camunda_connectors_secret[0].result, "") : "") + "console-secret" = try(local.camunda_identity_components_cfg.values["console-secret"], "") != "" ? local.camunda_identity_components_cfg.values["console-secret"] : (try(local.camunda_identity_components_cfg.generate, false) ? try(random_password.camunda_console_secret[0].result, "") : "") + "keycloak-secret" = try(local.camunda_identity_components_cfg.values["keycloak-secret"], "") != "" ? local.camunda_identity_components_cfg.values["keycloak-secret"] : (try(local.camunda_identity_components_cfg.generate, false) ? try(random_password.camunda_keycloak_secret[0].result, "") : "") + "operate-secret" = try(local.camunda_identity_components_cfg.values["operate-secret"], "") != "" ? local.camunda_identity_components_cfg.values["operate-secret"] : (try(local.camunda_identity_components_cfg.generate, false) ? try(random_password.camunda_operate_secret[0].result, "") : "") + "optimize-secret" = try(local.camunda_identity_components_cfg.values["optimize-secret"], "") != "" ? local.camunda_identity_components_cfg.values["optimize-secret"] : (try(local.camunda_identity_components_cfg.generate, false) ? try(random_password.camunda_optimize_secret[0].result, "") : "") + "tasklist-secret" = try(local.camunda_identity_components_cfg.values["tasklist-secret"], "") != "" ? local.camunda_identity_components_cfg.values["tasklist-secret"] : (try(local.camunda_identity_components_cfg.generate, false) ? try(random_password.camunda_tasklist_secret[0].result, "") : "") + "zeebe-secret" = try(local.camunda_identity_components_cfg.values["zeebe-secret"], "") != "" ? local.camunda_identity_components_cfg.values["zeebe-secret"] : (try(local.camunda_identity_components_cfg.generate, false) ? try(random_password.camunda_zeebe_secret[0].result, "") : "") + } + + camunda_identity_firstuser_password = trimspace(try(local.camunda_identity_firstuser_cfg.identity_firstuser_password, "")) != "" ? trimspace(local.camunda_identity_firstuser_cfg.identity_firstuser_password) : ( + try(local.camunda_identity_firstuser_cfg.generate, false) ? try(random_password.camunda_identity_firstuser_password[0].result, "") : "" + ) + + camunda_pg_password = trimspace(try(local.camunda_pg_cfg.password, "")) != "" ? trimspace(local.camunda_pg_cfg.password) : ( + try(local.camunda_pg_cfg.generate, false) ? try(random_password.camunda_pg_password[0].result, "") : "" + ) + camunda_pg_postgres_password = trimspace(try(local.camunda_pg_cfg.postgres_password, "")) != "" ? trimspace(local.camunda_pg_cfg.postgres_password) : ( + try(local.camunda_pg_cfg.generate, false) ? try(random_password.camunda_pg_postgres_password[0].result, "") : "" + ) + + keycloak_admin_password = trimspace(try(local.keycloak_admin_cfg.password, "")) != "" ? trimspace(local.keycloak_admin_cfg.password) : ( + try(local.keycloak_admin_cfg.generate, false) ? try(random_password.keycloak_admin_password[0].result, "") : "" + ) + keycloak_pg_password = trimspace(try(local.keycloak_pg_cfg.password, "")) != "" ? trimspace(local.keycloak_pg_cfg.password) : ( + trimspace(try(local.postgresql_users_values["keycloak"], "")) != "" ? trimspace(try(local.postgresql_users_values["keycloak"], "")) : ( + try(local.keycloak_pg_cfg.generate, false) ? try(random_password.keycloak_pg_password[0].result, "") : "" + ) + ) + + policy_docs = { + postgresql = <<-EOT + path "${var.vault_kv_mount}/data/postgresql/admin" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/metadata/postgresql/admin" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/data/postgresql/users" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/metadata/postgresql/users" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/data/postgresql/s3" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/metadata/postgresql/s3" { + capabilities = ["read"] + } + EOT + rabbitmq = <<-EOT + path "${var.vault_kv_mount}/data/rabbitmq/auth" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/metadata/rabbitmq/auth" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/data/rabbitmq/apps/*" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/metadata/rabbitmq/apps" { + capabilities = ["list", "read"] + } + + path "${var.vault_kv_mount}/metadata/rabbitmq/apps/*" { + capabilities = ["read"] + } + EOT + minio = <<-EOT + path "${var.vault_kv_mount}/data/minio/admin" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/metadata/minio/admin" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/data/minio/apps/*" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/metadata/minio/apps" { + capabilities = ["list", "read"] + } + + path "${var.vault_kv_mount}/metadata/minio/apps/*" { + capabilities = ["read"] + } + EOT + kafka = <<-EOT + path "${var.vault_kv_mount}/data/kafka/bootstrap" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/metadata/kafka/bootstrap" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/data/kafka/apps/*" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/metadata/kafka/apps" { + capabilities = ["list", "read"] + } + + path "${var.vault_kv_mount}/metadata/kafka/apps/*" { + capabilities = ["read"] + } + EOT + zitadel = <<-EOT + path "${var.vault_kv_mount}/data/zitadel/postgresql" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/metadata/zitadel/postgresql" { + capabilities = ["read"] + } + EOT + camunda = <<-EOT + path "${var.vault_kv_mount}/data/camunda/identity-components" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/metadata/camunda/identity-components" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/data/camunda/identity-firstuser" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/metadata/camunda/identity-firstuser" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/data/camunda/keycloak-admin" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/metadata/camunda/keycloak-admin" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/data/camunda/postgresql" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/metadata/camunda/postgresql" { + capabilities = ["read"] + } + EOT + keycloak = <<-EOT + path "${var.vault_kv_mount}/data/keycloak/admin" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/metadata/keycloak/admin" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/data/keycloak/postgresql" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/metadata/keycloak/postgresql" { + capabilities = ["read"] + } + EOT + } + + policy_enabled = { + postgresql = try(local.postgresql_cfg.enabled, true) + rabbitmq = try(local.rabbitmq_cfg.enabled, true) + minio = try(local.minio_cfg.enabled, true) + kafka = try(local.kafka_cfg.enabled, true) + zitadel = try(local.zitadel_cfg.enabled, true) + camunda = try(local.camunda_cfg.enabled, true) + keycloak = try(local.keycloak_cfg.enabled, true) + } + + role_cfgs = { + postgresql = merge(try(local.postgresql_cfg.role, {}), try(var.infra.postgresql.role, {})) + rabbitmq = merge(try(local.rabbitmq_cfg.role, {}), try(var.infra.rabbitmq.role, {})) + minio = merge(try(local.minio_cfg.role, {}), try(var.infra.minio.role, {})) + kafka = merge(try(local.kafka_cfg.role, {}), try(var.infra.kafka.role, {})) + zitadel = merge(try(local.zitadel_cfg.role, {}), try(var.infra.zitadel.role, {})) + camunda = merge(try(local.camunda_cfg.role, {}), try(var.infra.camunda.role, {})) + keycloak = merge(try(local.keycloak_cfg.role, {}), try(var.infra.keycloak.role, {})) + } + + app_cfgs = { + for app, cfg in try(var.applications, {}) : + app => merge( + { + enabled = true + policy_name = app + role_name = app + service_account_names = [app] + service_account_namespaces = [app] + token_ttl = "24h" + read_paths = [] + secret_path = "" + secret_data = {} + }, + cfg, + { + secret_path = trimspace(try(cfg.secret_path, "")) + secret_data = try(cfg.secret_data, {}) + read_paths = distinct(compact(concat( + [for path in try(cfg.read_paths, []) : trimspace(path)], + trimspace(try(cfg.secret_path, "")) != "" ? [trimspace(try(cfg.secret_path, ""))] : [] + ))) + } + ) + if try(cfg.enabled, true) + } + + app_policy_docs = { + for app, cfg in local.app_cfgs : + app => trimspace(join("\n\n", [ + for path in cfg.read_paths : <<-EOT + path "${var.vault_kv_mount}/data/${path}" { + capabilities = ["read"] + } + + path "${var.vault_kv_mount}/metadata/${path}" { + capabilities = ["read"] + } + EOT + ])) + } +} + +resource "random_password" "regcred_password" { + count = var.create_regcred && local.regcred_generate && local.regcred_password_input == "" ? 1 : 0 + + length = 40 + special = false +} + +resource "random_password" "smtp_password" { + count = var.create_smtp_auth && local.smtp_generate && local.smtp_password_input == "" ? 1 : 0 + + length = 40 + special = false +} + +resource "random_password" "django_token" { + count = var.create_django_auth && local.django_generate && local.django_manual == "" ? 1 : 0 + + length = 40 + special = false +} + +resource "tls_private_key" "rsa_keys" { + count = var.create_rsa_keys && local.rsa_generate && local.rsa_public_manual == "" && local.rsa_private_manual == "" ? 1 : 0 + + algorithm = "RSA" + rsa_bits = local.rsa_bits +} + +resource "random_password" "postgresql_admin_password" { + count = var.create_infra_secrets && try(local.postgresql_cfg.enabled, true) && try(local.postgresql_admin_cfg.enabled, true) && try(local.postgresql_admin_cfg.generate, false) && trimspace(try(local.postgresql_admin_cfg.postgres_password, "")) == "" ? 1 : 0 + + length = 32 + special = false +} + +resource "random_password" "postgresql_app_password" { + for_each = { + for app, cfg in local.postgresql_apps_cfg : + app => cfg if var.create_infra_secrets && try(local.postgresql_cfg.enabled, true) && try(cfg.enabled, true) && try(cfg.generate, false) && trimspace(try(cfg.password, "")) == "" + } + + length = 32 + special = false +} + +resource "random_password" "rabbitmq_password" { + count = var.create_infra_secrets && try(local.rabbitmq_cfg.enabled, true) && try(local.rabbitmq_auth_cfg.enabled, true) && try(local.rabbitmq_auth_cfg.generate, false) && trimspace(try(local.rabbitmq_auth_cfg.password, "")) == "" ? 1 : 0 + + length = 32 + special = false +} + +resource "random_password" "rabbitmq_app_password" { + for_each = { + for app, cfg in local.rabbitmq_apps_cfg : + app => cfg if var.create_infra_secrets && try(local.rabbitmq_cfg.enabled, true) && try(cfg.enabled, true) && try(cfg.generate, false) && trimspace(try(cfg.password, "")) == "" + } + + length = 32 + special = false +} + +resource "random_password" "minio_root_user" { + count = var.create_infra_secrets && try(local.minio_cfg.enabled, true) && try(local.minio_admin_cfg.enabled, true) && try(local.minio_admin_cfg.generate, false) && trimspace(try(local.minio_admin_cfg.root_user, "")) == "" ? 1 : 0 + + length = 20 + special = false +} + +resource "random_password" "minio_root_password" { + count = var.create_infra_secrets && try(local.minio_cfg.enabled, true) && try(local.minio_admin_cfg.enabled, true) && try(local.minio_admin_cfg.generate, false) && trimspace(try(local.minio_admin_cfg.root_password, "")) == "" ? 1 : 0 + + length = 40 + special = false +} + +resource "random_password" "minio_app_password" { + for_each = { + for app, cfg in local.minio_apps_cfg : + app => cfg if var.create_infra_secrets && try(local.minio_cfg.enabled, true) && try(cfg.enabled, true) && try(cfg.generate, false) && trimspace(try(cfg.password, "")) == "" + } + + length = 32 + special = false +} + +resource "random_password" "kafka_cluster_id" { + count = var.create_infra_secrets && try(local.kafka_cfg.enabled, true) && try(local.kafka_bootstrap_cfg.enabled, true) && try(local.kafka_bootstrap_cfg.generate, false) && trimspace(try(local.kafka_bootstrap_cfg.cluster_id, "")) == "" ? 1 : 0 + + length = 22 + special = false +} + +resource "random_password" "kafka_controller_password" { + count = var.create_infra_secrets && try(local.kafka_cfg.enabled, true) && try(local.kafka_bootstrap_cfg.enabled, true) && try(local.kafka_bootstrap_cfg.generate, false) && trimspace(try(local.kafka_bootstrap_cfg.controller_password, "")) == "" ? 1 : 0 + + length = 20 + special = false +} + +resource "random_password" "kafka_inter_broker_password" { + count = var.create_infra_secrets && try(local.kafka_cfg.enabled, true) && try(local.kafka_bootstrap_cfg.enabled, true) && try(local.kafka_bootstrap_cfg.generate, false) && trimspace(try(local.kafka_bootstrap_cfg.inter_broker_password, "")) == "" ? 1 : 0 + + length = 20 + special = false +} + +resource "random_password" "kafka_app_password" { + for_each = { + for app, cfg in local.kafka_apps_cfg : + app => cfg if var.create_infra_secrets && try(local.kafka_cfg.enabled, true) && try(cfg.enabled, true) && try(cfg.generate, false) && trimspace(try(cfg.password, "")) == "" + } + + length = 32 + special = false +} + +resource "random_password" "zitadel_pg_password" { + count = var.create_infra_secrets && try(local.zitadel_cfg.enabled, true) && try(local.zitadel_pg_cfg.enabled, true) && try(local.zitadel_pg_cfg.generate, false) && trimspace(try(local.zitadel_pg_cfg.password, "")) == "" ? 1 : 0 + + length = 32 + special = false +} + +resource "random_password" "camunda_keycloak_admin_password" { + count = var.create_infra_secrets && try(local.camunda_cfg.enabled, true) && try(local.camunda_keycloak_cfg.enabled, true) && try(local.camunda_keycloak_cfg.generate, false) && trimspace(try(local.camunda_keycloak_cfg.admin_password, "")) == "" ? 1 : 0 + + length = 32 + special = false +} + +resource "random_password" "camunda_connectors_secret" { + count = var.create_infra_secrets && try(local.camunda_cfg.enabled, true) && try(local.camunda_identity_components_cfg.enabled, true) && try(local.camunda_identity_components_cfg.generate, false) && try(local.camunda_identity_components_cfg.values["connectors-secret"], "") == "" ? 1 : 0 + length = 32 + special = false +} + +resource "random_password" "camunda_console_secret" { + count = var.create_infra_secrets && try(local.camunda_cfg.enabled, true) && try(local.camunda_identity_components_cfg.enabled, true) && try(local.camunda_identity_components_cfg.generate, false) && try(local.camunda_identity_components_cfg.values["console-secret"], "") == "" ? 1 : 0 + length = 32 + special = false +} + +resource "random_password" "camunda_keycloak_secret" { + count = var.create_infra_secrets && try(local.camunda_cfg.enabled, true) && try(local.camunda_identity_components_cfg.enabled, true) && try(local.camunda_identity_components_cfg.generate, false) && try(local.camunda_identity_components_cfg.values["keycloak-secret"], "") == "" ? 1 : 0 + length = 32 + special = false +} + +resource "random_password" "camunda_operate_secret" { + count = var.create_infra_secrets && try(local.camunda_cfg.enabled, true) && try(local.camunda_identity_components_cfg.enabled, true) && try(local.camunda_identity_components_cfg.generate, false) && try(local.camunda_identity_components_cfg.values["operate-secret"], "") == "" ? 1 : 0 + length = 32 + special = false +} + +resource "random_password" "camunda_optimize_secret" { + count = var.create_infra_secrets && try(local.camunda_cfg.enabled, true) && try(local.camunda_identity_components_cfg.enabled, true) && try(local.camunda_identity_components_cfg.generate, false) && try(local.camunda_identity_components_cfg.values["optimize-secret"], "") == "" ? 1 : 0 + length = 32 + special = false +} + +resource "random_password" "camunda_tasklist_secret" { + count = var.create_infra_secrets && try(local.camunda_cfg.enabled, true) && try(local.camunda_identity_components_cfg.enabled, true) && try(local.camunda_identity_components_cfg.generate, false) && try(local.camunda_identity_components_cfg.values["tasklist-secret"], "") == "" ? 1 : 0 + length = 32 + special = false +} + +resource "random_password" "camunda_zeebe_secret" { + count = var.create_infra_secrets && try(local.camunda_cfg.enabled, true) && try(local.camunda_identity_components_cfg.enabled, true) && try(local.camunda_identity_components_cfg.generate, false) && try(local.camunda_identity_components_cfg.values["zeebe-secret"], "") == "" ? 1 : 0 + length = 32 + special = false +} + +resource "random_password" "camunda_identity_firstuser_password" { + count = var.create_infra_secrets && try(local.camunda_cfg.enabled, true) && try(local.camunda_identity_firstuser_cfg.enabled, true) && try(local.camunda_identity_firstuser_cfg.generate, false) && trimspace(try(local.camunda_identity_firstuser_cfg.identity_firstuser_password, "")) == "" ? 1 : 0 + length = 32 + special = false +} + +resource "random_password" "camunda_pg_password" { + count = var.create_infra_secrets && try(local.camunda_cfg.enabled, true) && try(local.camunda_pg_cfg.enabled, true) && try(local.camunda_pg_cfg.generate, false) && trimspace(try(local.camunda_pg_cfg.password, "")) == "" ? 1 : 0 + + length = 32 + special = false +} + +resource "random_password" "camunda_pg_postgres_password" { + count = var.create_infra_secrets && try(local.camunda_cfg.enabled, true) && try(local.camunda_pg_cfg.enabled, true) && try(local.camunda_pg_cfg.generate, false) && trimspace(try(local.camunda_pg_cfg.postgres_password, "")) == "" ? 1 : 0 + + length = 32 + special = false +} + +resource "random_password" "keycloak_admin_password" { + count = var.create_infra_secrets && try(local.keycloak_cfg.enabled, true) && try(local.keycloak_admin_cfg.enabled, true) && try(local.keycloak_admin_cfg.generate, false) && trimspace(try(local.keycloak_admin_cfg.password, "")) == "" ? 1 : 0 + + length = 20 + special = false +} + +resource "random_password" "keycloak_pg_password" { + count = var.create_infra_secrets && try(local.keycloak_cfg.enabled, true) && try(local.keycloak_pg_cfg.enabled, true) && trimspace(try(local.keycloak_pg_cfg.password, "")) == "" && try(local.keycloak_pg_cfg.generate, false) ? 1 : 0 + + length = 32 + special = false +} + +resource "vault_kv_secret_v2" "common_regcred" { + count = var.create_regcred ? 1 : 0 + + mount = var.vault_kv_mount + name = "${var.path_prefix}/common/regcred" + + data_json = jsonencode({ + type = "kubernetes.io/dockerconfigjson" + ".dockerconfigjson" = local.regcred_value + }) +} + +resource "vault_kv_secret_v2" "common_smtp_auth" { + count = var.create_smtp_auth ? 1 : 0 + + mount = var.vault_kv_mount + name = "${var.path_prefix}/common/smtp_auth" + + data_json = jsonencode({ + "env.json" = local.smtp_value + }) +} + +resource "vault_kv_secret_v2" "common_django_auth" { + count = var.create_django_auth ? 1 : 0 + + mount = var.vault_kv_mount + name = "${var.path_prefix}/common/django_auth" + + data_json = jsonencode(merge( + { + "django-auth.json" = jsonencode({ + token = local.django_token + }) + key = local.django_token + username = local.django_username + password = local.django_password + }, + local.django_extra + )) +} + +resource "vault_kv_secret_v2" "common_rsa_keys" { + count = var.create_rsa_keys ? 1 : 0 + + mount = var.vault_kv_mount + name = "${var.path_prefix}/common/rsa_keys" + + data_json = jsonencode({ + public_key = local.rsa_public + private_key = local.rsa_private + }) +} + +resource "vault_kv_secret_v2" "postgresql_admin" { + count = var.create_infra_secrets && try(local.postgresql_cfg.enabled, true) && try(local.postgresql_admin_cfg.enabled, true) ? 1 : 0 + + mount = var.vault_kv_mount + name = "postgresql/admin" + + data_json = jsonencode({ + "postgres-password" = local.postgresql_admin_password + }) +} + +resource "vault_kv_secret_v2" "postgresql_users" { + count = var.create_infra_secrets && try(local.postgresql_cfg.enabled, true) && try(local.postgresql_users_cfg.enabled, true) && local.postgresql_users_has_values ? 1 : 0 + + mount = var.vault_kv_mount + name = "postgresql/users" + + data_json = jsonencode(local.postgresql_users_values) +} + +resource "vault_kv_secret_v2" "postgresql_s3" { + count = var.create_infra_secrets && try(local.postgresql_cfg.enabled, true) && try(local.postgresql_s3_cfg.enabled, false) ? 1 : 0 + + mount = var.vault_kv_mount + name = "postgresql/s3" + + data_json = jsonencode({ + bucket = trimspace(try(local.postgresql_s3_cfg.bucket, "")) + endpoint = trimspace(try(local.postgresql_s3_cfg.endpoint, "https://storage.yandexcloud.net")) + region = trimspace(try(local.postgresql_s3_cfg.region, "ru-central1")) + access_key = trimspace(try(local.postgresql_s3_cfg.access_key, "")) + secret_key = trimspace(try(local.postgresql_s3_cfg.secret_key, "")) + }) +} + +resource "vault_kv_secret_v2" "postgresql_apps" { + for_each = { + for app, cfg in local.postgresql_apps : + app => cfg if var.create_infra_secrets && try(local.postgresql_cfg.enabled, true) && try(cfg.enabled, true) + } + + mount = var.vault_kv_mount + name = "postgresql/apps/${each.key}" + + data_json = jsonencode({ + username = try(each.value.username, each.key) + password = try(each.value.password, "") + password_key = try(each.value.password_key, each.key) + }) +} + +resource "vault_kv_secret_v2" "rabbitmq_auth" { + count = var.create_infra_secrets && try(local.rabbitmq_cfg.enabled, true) && try(local.rabbitmq_auth_cfg.enabled, true) ? 1 : 0 + + mount = var.vault_kv_mount + name = "rabbitmq/auth" + + data_json = jsonencode({ + username = try(local.rabbitmq_auth_cfg.username, "") + password = local.rabbitmq_password + }) +} + +resource "vault_kv_secret_v2" "rabbitmq_apps" { + for_each = { + for app, cfg in local.rabbitmq_apps : + app => cfg if var.create_infra_secrets && try(local.rabbitmq_cfg.enabled, true) && try(cfg.enabled, true) + } + + mount = var.vault_kv_mount + name = "rabbitmq/apps/${each.key}" + + data_json = jsonencode({ + username = try(each.value.username, each.key) + password = try(each.value.password, "") + vhost = try(each.value.vhost, "/") + permissions = try(each.value.permissions, { configure = ".*", write = ".*", read = ".*" }) + vhosts = try(each.value.vhosts, []) + }) +} + +resource "vault_kv_secret_v2" "minio_admin" { + count = var.create_infra_secrets && try(local.minio_cfg.enabled, true) && try(local.minio_admin_cfg.enabled, true) ? 1 : 0 + + mount = var.vault_kv_mount + name = "minio/admin" + + data_json = jsonencode({ + rootUser = local.minio_root_user + rootPassword = local.minio_root_password + }) +} + +resource "vault_kv_secret_v2" "minio_apps" { + for_each = { + for app, cfg in local.minio_apps : + app => cfg if var.create_infra_secrets && try(local.minio_cfg.enabled, true) && try(cfg.enabled, true) + } + + mount = var.vault_kv_mount + name = "minio/apps/${each.key}" + + data_json = jsonencode({ + username = try(each.value.username, each.key) + password = try(each.value.password, "") + access_key = try(each.value.access_key, try(each.value.username, each.key)) + secret_key = try(each.value.secret_key, try(each.value.password, "")) + policy = try(each.value.policy, "readwrite") + buckets = try(each.value.buckets, []) + client = try(each.value.client, {}) + }) +} + +resource "vault_kv_secret_v2" "kafka_bootstrap" { + count = var.create_infra_secrets && try(local.kafka_cfg.enabled, true) && try(local.kafka_bootstrap_cfg.enabled, true) ? 1 : 0 + + mount = var.vault_kv_mount + name = "kafka/bootstrap" + + data_json = jsonencode({ + clusterId = local.kafka_cluster_id + controllerPassword = local.kafka_controller_password + interBrokerPassword = local.kafka_inter_broker_password + }) +} + +resource "vault_kv_secret_v2" "kafka_apps" { + for_each = { + for app, cfg in local.kafka_apps : + app => cfg if var.create_infra_secrets && try(local.kafka_cfg.enabled, true) && try(cfg.enabled, true) + } + + mount = var.vault_kv_mount + name = "kafka/apps/${each.key}" + + data_json = jsonencode({ + username = try(each.value.username, each.key) + password = try(each.value.password, "") + auth = try(each.value.auth, {}) + topics = try(each.value.topics, []) + }) +} + +resource "vault_kv_secret_v2" "zitadel_postgresql" { + count = var.create_infra_secrets && try(local.zitadel_cfg.enabled, true) && try(local.zitadel_pg_cfg.enabled, true) ? 1 : 0 + + mount = var.vault_kv_mount + name = "zitadel/postgresql" + + data_json = jsonencode({ + password = local.zitadel_pg_password + }) +} + +resource "vault_kv_secret_v2" "camunda_keycloak_admin" { + count = var.create_infra_secrets && try(local.camunda_cfg.enabled, true) && try(local.camunda_keycloak_cfg.enabled, true) ? 1 : 0 + + mount = var.vault_kv_mount + name = "camunda/keycloak-admin" + + data_json = jsonencode({ + "admin-password" = local.camunda_keycloak_admin_password + }) +} + +resource "vault_kv_secret_v2" "camunda_identity_components" { + count = var.create_infra_secrets && try(local.camunda_cfg.enabled, true) && try(local.camunda_identity_components_cfg.enabled, true) ? 1 : 0 + + mount = var.vault_kv_mount + name = "camunda/identity-components" + + data_json = jsonencode(local.camunda_identity_components) +} + +resource "vault_kv_secret_v2" "camunda_identity_firstuser" { + count = var.create_infra_secrets && try(local.camunda_cfg.enabled, true) && try(local.camunda_identity_firstuser_cfg.enabled, true) ? 1 : 0 + + mount = var.vault_kv_mount + name = "camunda/identity-firstuser" + + data_json = jsonencode({ + "identity-firstuser-password" = local.camunda_identity_firstuser_password + }) +} + +resource "vault_kv_secret_v2" "camunda_postgresql" { + count = var.create_infra_secrets && try(local.camunda_cfg.enabled, true) && try(local.camunda_pg_cfg.enabled, true) ? 1 : 0 + + mount = var.vault_kv_mount + name = "camunda/postgresql" + + data_json = jsonencode({ + password = local.camunda_pg_password + "postgres-password" = local.camunda_pg_postgres_password + }) +} + +resource "vault_kv_secret_v2" "keycloak_admin" { + count = var.create_infra_secrets && try(local.keycloak_cfg.enabled, true) && try(local.keycloak_admin_cfg.enabled, true) ? 1 : 0 + + mount = var.vault_kv_mount + name = "keycloak/admin" + + data_json = jsonencode({ + password = local.keycloak_admin_password + }) +} + +resource "vault_kv_secret_v2" "keycloak_postgresql" { + count = var.create_infra_secrets && try(local.keycloak_cfg.enabled, true) && try(local.keycloak_pg_cfg.enabled, true) ? 1 : 0 + + mount = var.vault_kv_mount + name = "keycloak/postgresql" + + data_json = jsonencode({ + host = try(local.keycloak_pg_cfg.host, "") + port = tostring(try(local.keycloak_pg_cfg.port, "")) + user = try(local.keycloak_pg_cfg.user, "") + database = try(local.keycloak_pg_cfg.database, "") + password = local.keycloak_pg_password + }) +} + +resource "vault_policy" "infra" { + for_each = { + for name, enabled in local.policy_enabled : + name => name if var.create_infra_policies && enabled + } + + name = each.value + policy = local.policy_docs[each.value] +} + +resource "vault_kubernetes_auth_backend_role" "infra" { + for_each = { + for name, role in local.role_cfgs : + name => role if var.create_infra_roles && try(local.policy_enabled[name], true) && try(role.enabled, true) + } + + backend = var.kubernetes_auth_backend + role_name = try(each.value.name, each.key) + bound_service_account_names = try(each.value.service_account_names, []) + bound_service_account_namespaces = try(each.value.service_account_namespaces, []) + token_ttl = can(tonumber(try(each.value.token_ttl, ""))) ? tonumber(each.value.token_ttl) : 86400 + token_policies = [try(each.value.name, each.key)] +} + +resource "vault_kv_secret_v2" "application" { + for_each = { + for app, cfg in local.app_cfgs : + app => cfg if(var.create_app_policies || var.create_app_roles) && trimspace(try(cfg.secret_path, "")) != "" && length(keys(try(cfg.secret_data, {}))) > 0 + } + + mount = var.vault_kv_mount + name = each.value.secret_path + + data_json = jsonencode(each.value.secret_data) +} + +resource "vault_policy" "app" { + for_each = { + for app, cfg in local.app_cfgs : + app => cfg if var.create_app_policies && length(cfg.read_paths) > 0 + } + + name = try(each.value.policy_name, each.key) + policy = local.app_policy_docs[each.key] +} + +resource "vault_kubernetes_auth_backend_role" "app" { + for_each = { + for app, cfg in local.app_cfgs : + app => cfg if var.create_app_roles && length(cfg.read_paths) > 0 + } + + backend = var.kubernetes_auth_backend + role_name = try(each.value.role_name, each.key) + bound_service_account_names = try(each.value.service_account_names, []) + bound_service_account_namespaces = try(each.value.service_account_namespaces, []) + token_ttl = can(tonumber(try(each.value.token_ttl, ""))) ? tonumber(each.value.token_ttl) : 86400 + token_policies = [try(each.value.policy_name, each.key)] +} diff --git a/modules/vault-platform-secrets/outputs.tf b/modules/vault-platform-secrets/outputs.tf new file mode 100644 index 0000000..9ecc8ed --- /dev/null +++ b/modules/vault-platform-secrets/outputs.tf @@ -0,0 +1,54 @@ +output "common_secret_paths" { + description = "Paths of created common Vault secrets" + value = compact([ + try(vault_kv_secret_v2.common_regcred[0].name, ""), + try(vault_kv_secret_v2.common_smtp_auth[0].name, ""), + try(vault_kv_secret_v2.common_django_auth[0].name, ""), + try(vault_kv_secret_v2.common_rsa_keys[0].name, "") + ]) +} + +output "infra_secret_paths" { + description = "Paths of created infrastructure Vault secrets" + value = concat( + compact([ + try(vault_kv_secret_v2.postgresql_admin[0].name, ""), + try(vault_kv_secret_v2.postgresql_s3[0].name, ""), + try(vault_kv_secret_v2.postgresql_users[0].name, ""), + try(vault_kv_secret_v2.rabbitmq_auth[0].name, ""), + try(vault_kv_secret_v2.minio_admin[0].name, ""), + try(vault_kv_secret_v2.kafka_bootstrap[0].name, ""), + try(vault_kv_secret_v2.zitadel_postgresql[0].name, ""), + try(vault_kv_secret_v2.camunda_identity_components[0].name, ""), + try(vault_kv_secret_v2.camunda_identity_firstuser[0].name, ""), + try(vault_kv_secret_v2.camunda_keycloak_admin[0].name, ""), + try(vault_kv_secret_v2.camunda_postgresql[0].name, ""), + try(vault_kv_secret_v2.keycloak_admin[0].name, ""), + try(vault_kv_secret_v2.keycloak_postgresql[0].name, "") + ]), + [for s in values(vault_kv_secret_v2.postgresql_apps) : s.name], + [for s in values(vault_kv_secret_v2.rabbitmq_apps) : s.name], + [for s in values(vault_kv_secret_v2.minio_apps) : s.name], + [for s in values(vault_kv_secret_v2.kafka_apps) : s.name] + ) +} + +output "infra_policy_names" { + description = "Created infrastructure Vault policy names" + value = keys(vault_policy.infra) +} + +output "infra_role_names" { + description = "Created infrastructure Vault kubernetes auth role names" + value = [for r in values(vault_kubernetes_auth_backend_role.infra) : r.role_name] +} + +output "app_policy_names" { + description = "Created application Vault policy names" + value = keys(vault_policy.app) +} + +output "app_role_names" { + description = "Created application Vault kubernetes auth role names" + value = [for r in values(vault_kubernetes_auth_backend_role.app) : r.role_name] +} diff --git a/modules/vault-platform-secrets/variables.tf b/modules/vault-platform-secrets/variables.tf new file mode 100644 index 0000000..6cd4253 --- /dev/null +++ b/modules/vault-platform-secrets/variables.tf @@ -0,0 +1,95 @@ +variable "vault_kv_mount" { + description = "Vault KV v2 mount" + type = string + default = "secrets" +} + +variable "path_prefix" { + description = "Path prefix inside Vault mount" + type = string + default = "platform" +} + +variable "common" { + description = "Common platform secret configuration" + type = any + default = {} +} + +variable "infra" { + description = "Infrastructure services secret/policy/role configuration" + type = any + default = {} +} + +variable "applications" { + description = "Application Vault access roles/policies configuration" + type = any + default = {} +} + +variable "create_regcred" { + description = "Create common dockerhub regcred secret" + type = bool + default = true +} + +variable "create_smtp_auth" { + description = "Create common SMTP auth secret" + type = bool + default = true +} + +variable "create_django_auth" { + description = "Create common django-auth secret" + type = bool + default = true +} + +variable "create_rsa_keys" { + description = "Create common RSA key pair secret" + type = bool + default = true +} + +variable "create_infra_secrets" { + description = "Create infrastructure service secrets" + type = bool + default = true +} + +variable "create_infra_policies" { + description = "Create infrastructure Vault policies" + type = bool + default = true +} + +variable "create_infra_roles" { + description = "Create infrastructure Vault kubernetes auth roles" + type = bool + default = true +} + +variable "create_app_policies" { + description = "Create application Vault policies" + type = bool + default = true +} + +variable "create_app_roles" { + description = "Create application Vault kubernetes auth roles" + type = bool + default = true +} + +variable "kubernetes_auth_backend" { + description = "Vault kubernetes auth backend path" + type = string + default = "kubernetes" +} + +variable "default_rsa_bits" { + description = "Default RSA key size" + type = number + default = 4096 +} diff --git a/modules/vault-platform-secrets/versions.tf b/modules/vault-platform-secrets/versions.tf new file mode 100644 index 0000000..fd126d1 --- /dev/null +++ b/modules/vault-platform-secrets/versions.tf @@ -0,0 +1,3 @@ +terraform { + required_version = ">= 1.5.0" +}