diff --git a/ansible/inventory/generated/test.yml b/ansible/inventory/generated/test.yml new file mode 100644 index 0000000..624ae3c --- /dev/null +++ b/ansible/inventory/generated/test.yml @@ -0,0 +1,14 @@ +"all": + "children": + "db": + "hosts": + "iklim-db-01": + "ansible_host": "167.235.194.61" + "ansible_user": "root" + "private_ip": "10.10.20.11" + "swarm": + "hosts": + "iklim-app-01": + "ansible_host": "167.235.205.93" + "ansible_user": "root" + "private_ip": "10.10.10.11" diff --git a/terraform/hetzner/prod/firewall.tf b/terraform/hetzner/prod/firewall.tf index 92ad586..e742936 100644 --- a/terraform/hetzner/prod/firewall.tf +++ b/terraform/hetzner/prod/firewall.tf @@ -1,141 +1,172 @@ -# Swarm node firewall — public HTTP/HTTPS + private infra services resource "hcloud_firewall" "swarm" { name = "${local.name_prefix}-firewall-app" - # SSH — admin CIDRs only rule { - direction = "in" - protocol = "tcp" - port = "22" - source_ips = var.admin_allowed_cidrs + direction = "in" + protocol = "tcp" + port = "22" + source_ips = var.admin_allowed_cidrs + description = "SSH — admin CIDRs only" } - # HTTP public rule { - direction = "in" - protocol = "tcp" - port = "80" - source_ips = ["0.0.0.0/0", "::/0"] + direction = "in" + protocol = "tcp" + port = "80" + source_ips = ["0.0.0.0/0", "::/0"] + description = "HTTP public" } - # HTTPS public rule { - direction = "in" - protocol = "tcp" - port = "443" - source_ips = ["0.0.0.0/0", "::/0"] + direction = "in" + protocol = "tcp" + port = "443" + source_ips = ["0.0.0.0/0", "::/0"] + description = "HTTPS public" } - # Docker Swarm control plane rule { - direction = "in" - protocol = "tcp" - port = "2377" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "2377" + source_ips = [local.app_subnet_cidr] + description = "Docker Swarm control plane" } - # Docker Swarm node discovery (TCP) rule { - direction = "in" - protocol = "tcp" - port = "7946" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "7946" + source_ips = [local.app_subnet_cidr] + description = "Docker Swarm node discovery (TCP)" } - # Docker Swarm node discovery (UDP) rule { - direction = "in" - protocol = "udp" - port = "7946" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "udp" + port = "7946" + source_ips = [local.app_subnet_cidr] + description = "Docker Swarm node discovery (UDP)" } - # Docker Swarm VXLAN overlay rule { - direction = "in" - protocol = "udp" - port = "4789" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "udp" + port = "4789" + source_ips = [local.app_subnet_cidr] + description = "Docker Swarm VXLAN overlay" } - # Vault API — private only, never public rule { - direction = "in" - protocol = "tcp" - port = "8200" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "2377" + source_ips = [local.db_subnet_cidr] + description = "Docker Swarm control plane from DB subnet" } - # Redis rule { - direction = "in" - protocol = "tcp" - port = "6379" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "7946" + source_ips = [local.db_subnet_cidr] + description = "Docker Swarm node discovery (TCP) from DB subnet" } - # RabbitMQ AMQP rule { - direction = "in" - protocol = "tcp" - port = "5672" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "udp" + port = "7946" + source_ips = [local.db_subnet_cidr] + description = "Docker Swarm node discovery (UDP) from DB subnet" } - # RabbitMQ STOMP rule { - direction = "in" - protocol = "tcp" - port = "61613" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "udp" + port = "4789" + source_ips = [local.db_subnet_cidr] + description = "Docker Swarm VXLAN overlay from DB subnet" } - # RabbitMQ Web STOMP rule { - direction = "in" - protocol = "tcp" - port = "15674" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "8200" + source_ips = [local.app_subnet_cidr] + description = "Vault API — private only, never public" } - # RabbitMQ Management — private only, SWAG arkasindan 443 uzerinden rule { - direction = "in" - protocol = "tcp" - port = "15672" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "6379" + source_ips = [local.app_subnet_cidr] + description = "Redis" } - # APISIX Dashboard — private only, SWAG arkasindan 443 uzerinden (IP kisitli) rule { - direction = "in" - protocol = "tcp" - port = "9000" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "5672" + source_ips = [local.app_subnet_cidr] + description = "RabbitMQ AMQP" } - # APISIX Admin API — sadece Docker overlay icinden Dashboard erisir rule { - direction = "in" - protocol = "tcp" - port = "9180" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "61613" + source_ips = [local.app_subnet_cidr] + description = "RabbitMQ STOMP" } - # Prometheus — private only, Grafana Docker overlay uzerinden erisir rule { - direction = "in" - protocol = "tcp" - port = "9090" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "15674" + source_ips = [local.app_subnet_cidr] + description = "RabbitMQ Web STOMP" } - # Grafana — private only, SWAG arkasindan 443 uzerinden (IP kisitli) rule { - direction = "in" - protocol = "tcp" - port = "3000" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "15672" + source_ips = [local.app_subnet_cidr] + description = "RabbitMQ Management — private, via SWAG on 443" + } + + rule { + direction = "in" + protocol = "tcp" + port = "9000" + source_ips = [local.app_subnet_cidr] + description = "APISIX Dashboard — private, via SWAG on 443 (IP restricted)" + } + + rule { + direction = "in" + protocol = "tcp" + port = "9180" + source_ips = [local.app_subnet_cidr] + description = "APISIX Admin API — internal only, accessed by Dashboard via Docker overlay" + } + + rule { + direction = "in" + protocol = "tcp" + port = "9090" + source_ips = [local.app_subnet_cidr] + description = "Prometheus — private, accessed by Grafana via Docker overlay" + } + + rule { + direction = "in" + protocol = "tcp" + port = "3000" + source_ips = [local.app_subnet_cidr] + description = "Grafana — private, via SWAG on 443 (IP restricted)" } labels = { @@ -144,48 +175,103 @@ resource "hcloud_firewall" "swarm" { } } -# DB node firewall — SSH + DB ports from app/swarm subnet only resource "hcloud_firewall" "db" { name = "${local.name_prefix}-firewall-db" - # SSH — admin CIDRs only rule { - direction = "in" - protocol = "tcp" - port = "22" - source_ips = var.admin_allowed_cidrs + direction = "in" + protocol = "tcp" + port = "22" + source_ips = var.admin_allowed_cidrs + description = "SSH — admin CIDRs only" } - # PostgreSQL from app/swarm subnet rule { - direction = "in" - protocol = "tcp" - port = "5432" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "5432" + source_ips = [local.app_subnet_cidr] + description = "PostgreSQL from app subnet" } - # PostgreSQL replication within DB subnet rule { - direction = "in" - protocol = "tcp" - port = "5432" - source_ips = [local.db_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "5432" + source_ips = [local.db_subnet_cidr] + description = "PostgreSQL replication within DB subnet" } - # MongoDB from app/swarm subnet rule { - direction = "in" - protocol = "tcp" - port = "27017" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "27017" + source_ips = [local.app_subnet_cidr] + description = "MongoDB from app subnet" } - # MongoDB replica set internal traffic rule { - direction = "in" - protocol = "tcp" - port = "27017" - source_ips = [local.db_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "27017" + source_ips = [local.db_subnet_cidr] + description = "MongoDB replica set internal traffic" + } + + rule { + direction = "in" + protocol = "tcp" + port = "2377" + source_ips = [local.app_subnet_cidr] + description = "Docker Swarm control plane from app subnet" + } + + rule { + direction = "in" + protocol = "tcp" + port = "7946" + source_ips = [local.app_subnet_cidr] + description = "Docker Swarm node discovery (TCP) from app subnet" + } + + rule { + direction = "in" + protocol = "udp" + port = "7946" + source_ips = [local.app_subnet_cidr] + description = "Docker Swarm node discovery (UDP) from app subnet" + } + + rule { + direction = "in" + protocol = "udp" + port = "4789" + source_ips = [local.app_subnet_cidr] + description = "Docker Swarm VXLAN overlay from app subnet" + } + + rule { + direction = "in" + protocol = "tcp" + port = "2379" + source_ips = [local.db_subnet_cidr] + description = "etcd client port within DB subnet" + } + + rule { + direction = "in" + protocol = "tcp" + port = "2380" + source_ips = [local.db_subnet_cidr] + description = "etcd peer port within DB subnet" + } + + rule { + direction = "in" + protocol = "tcp" + port = "8008" + source_ips = [local.db_subnet_cidr] + description = "Patroni REST API within DB subnet" } labels = { diff --git a/terraform/hetzner/prod/terraform.tfvars.example b/terraform/hetzner/prod/terraform.tfvars.example index 5e98d29..dcec566 100644 --- a/terraform/hetzner/prod/terraform.tfvars.example +++ b/terraform/hetzner/prod/terraform.tfvars.example @@ -2,7 +2,7 @@ hcloud_token = "YOUR_HETZNER_PROD_PROJECT_API_TOKEN" location = "fsn1" image = "rocky-10" -server_type_swarm = "cx42" -server_type_db = "cx52" +server_type_swarm = "cpx42" +server_type_db = "cpx32" admin_ssh_public_key_path = "~/.ssh/id_ed25519.pub" admin_allowed_cidrs = ["1.2.3.4/32", "5.6.7.8/32"] diff --git a/terraform/hetzner/prod/variables.tf b/terraform/hetzner/prod/variables.tf index e5c0639..ea101a2 100644 --- a/terraform/hetzner/prod/variables.tf +++ b/terraform/hetzner/prod/variables.tf @@ -18,13 +18,13 @@ variable "image" { variable "server_type_swarm" { type = string - default = "cx42" + default = "cpx42" description = "Hetzner server type for Swarm nodes" } variable "server_type_db" { type = string - default = "cx52" + default = "cpx32" description = "Hetzner server type for DB nodes" } diff --git a/terraform/hetzner/test/.terraform.lock.hcl b/terraform/hetzner/test/.terraform.lock.hcl new file mode 100644 index 0000000..dcaf640 --- /dev/null +++ b/terraform/hetzner/test/.terraform.lock.hcl @@ -0,0 +1,23 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hetznercloud/hcloud" { + version = "1.62.0" + constraints = "~> 1.49" + hashes = [ + "h1:Y9/c7aSVFnOvkvx+E33cNqHsFOu7S4vijxorU9FXmxs=", + "zh:2077f1655b6a7e26ae6d8ce3b5f35a6a65728416deb16dbd5165115da7534f37", + "zh:2234db7b84efa489b8e5f29f756cfed4a5bab760985f62d38c4b9ed2b3d6b4b6", + "zh:4abee7212fd15bcbf22b156ff18933f3975f2b1153fd3e93a1cacf31b9d35137", + "zh:5d7a63a8d4c73babea715c0c7a5dc04a08b5076e2f1f59855bf61f2393017bf0", + "zh:5ef15b4367c139b18167b2169421cb1f760d485db42f05ef292bd63eadcfa802", + "zh:62b432d918815812ea35ceca252d0ea833a8e1dbbc72c6b2d410369d7b8b0d85", + "zh:63fd3d3803a86447f9a1c0c49bffe704168fbc907ea3688cfd847e1dd012e9ff", + "zh:6a84f7125dad475f939afb58a1f0ec089e835d1b30ca64f467d85565a89f7508", + "zh:834c2ddcaa986323ecb7aa2baa3fd7b1888c2aec249f296822e53a4bc46be66e", + "zh:887e503de3720894eb756bcfc67b3d8ceb68564f9f8bb2a115d7398f0b5990b7", + "zh:976216f9aa89a466a1d97ae776c4df2edbac4a9ab29ff9850884060d15024570", + "zh:c3d7fc02e0fdf1bbee3a07c9171281a59d79ad9df2ec04342a81f3875709171c", + "zh:e0165f404357f2c1f89524165f38c88f643fb518483adfbf7817a6033a83f4d8", + ] +} diff --git a/terraform/hetzner/test/firewall.tf b/terraform/hetzner/test/firewall.tf index 3463937..fd8171e 100644 --- a/terraform/hetzner/test/firewall.tf +++ b/terraform/hetzner/test/firewall.tf @@ -1,141 +1,172 @@ -# Swarm node firewall — public HTTP/HTTPS + private infra services resource "hcloud_firewall" "swarm" { name = "${local.name_prefix}-firewall-app" - # SSH — admin CIDRs only rule { - direction = "in" - protocol = "tcp" - port = "22" - source_ips = var.admin_allowed_cidrs + direction = "in" + protocol = "tcp" + port = "22" + source_ips = var.admin_allowed_cidrs + description = "SSH — admin CIDRs only" } - # HTTP public rule { - direction = "in" - protocol = "tcp" - port = "80" - source_ips = ["0.0.0.0/0", "::/0"] + direction = "in" + protocol = "tcp" + port = "80" + source_ips = ["0.0.0.0/0", "::/0"] + description = "HTTP public" } - # HTTPS public rule { - direction = "in" - protocol = "tcp" - port = "443" - source_ips = ["0.0.0.0/0", "::/0"] + direction = "in" + protocol = "tcp" + port = "443" + source_ips = ["0.0.0.0/0", "::/0"] + description = "HTTPS public" } - # Docker Swarm control plane rule { - direction = "in" - protocol = "tcp" - port = "2377" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "2377" + source_ips = [local.app_subnet_cidr] + description = "Docker Swarm control plane" } - # Docker Swarm node discovery (TCP) rule { - direction = "in" - protocol = "tcp" - port = "7946" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "7946" + source_ips = [local.app_subnet_cidr] + description = "Docker Swarm node discovery (TCP)" } - # Docker Swarm node discovery (UDP) rule { - direction = "in" - protocol = "udp" - port = "7946" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "udp" + port = "7946" + source_ips = [local.app_subnet_cidr] + description = "Docker Swarm node discovery (UDP)" } - # Docker Swarm VXLAN overlay rule { - direction = "in" - protocol = "udp" - port = "4789" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "udp" + port = "4789" + source_ips = [local.app_subnet_cidr] + description = "Docker Swarm VXLAN overlay" } - # Vault API — private only, never public rule { - direction = "in" - protocol = "tcp" - port = "8200" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "2377" + source_ips = [local.db_subnet_cidr] + description = "Docker Swarm control plane from DB subnet" } - # Redis rule { - direction = "in" - protocol = "tcp" - port = "6379" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "7946" + source_ips = [local.db_subnet_cidr] + description = "Docker Swarm node discovery (TCP) from DB subnet" } - # RabbitMQ AMQP rule { - direction = "in" - protocol = "tcp" - port = "5672" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "udp" + port = "7946" + source_ips = [local.db_subnet_cidr] + description = "Docker Swarm node discovery (UDP) from DB subnet" } - # RabbitMQ STOMP rule { - direction = "in" - protocol = "tcp" - port = "61613" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "udp" + port = "4789" + source_ips = [local.db_subnet_cidr] + description = "Docker Swarm VXLAN overlay from DB subnet" } - # RabbitMQ Web STOMP rule { - direction = "in" - protocol = "tcp" - port = "15674" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "8200" + source_ips = [local.app_subnet_cidr] + description = "Vault API — private only, never public" } - # RabbitMQ Management — private only, SWAG arkasindan 443 uzerinden erisim rule { - direction = "in" - protocol = "tcp" - port = "15672" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "6379" + source_ips = [local.app_subnet_cidr] + description = "Redis" } - # APISIX Dashboard — private only, SWAG arkasindan 443 uzerinden (IP kisitli) rule { - direction = "in" - protocol = "tcp" - port = "9000" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "5672" + source_ips = [local.app_subnet_cidr] + description = "RabbitMQ AMQP" } - # APISIX Admin API — sadece Docker overlay icinden Dashboard erisir, insan erisimi gerekmez rule { - direction = "in" - protocol = "tcp" - port = "9180" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "61613" + source_ips = [local.app_subnet_cidr] + description = "RabbitMQ STOMP" } - # Prometheus — private only, SWAG arkasindan 443 uzerinden erisim rule { - direction = "in" - protocol = "tcp" - port = "9090" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "15674" + source_ips = [local.app_subnet_cidr] + description = "RabbitMQ Web STOMP" } - # Grafana — private only, SWAG arkasindan 443 uzerinden erisim rule { - direction = "in" - protocol = "tcp" - port = "3000" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "15672" + source_ips = [local.app_subnet_cidr] + description = "RabbitMQ Management — private, via SWAG on 443" + } + + rule { + direction = "in" + protocol = "tcp" + port = "9000" + source_ips = [local.app_subnet_cidr] + description = "APISIX Dashboard — private, via SWAG on 443 (IP restricted)" + } + + rule { + direction = "in" + protocol = "tcp" + port = "9180" + source_ips = [local.app_subnet_cidr] + description = "APISIX Admin API — internal only, accessed by Dashboard via Docker overlay" + } + + rule { + direction = "in" + protocol = "tcp" + port = "9090" + source_ips = [local.app_subnet_cidr] + description = "Prometheus — private, via SWAG on 443" + } + + rule { + direction = "in" + protocol = "tcp" + port = "3000" + source_ips = [local.app_subnet_cidr] + description = "Grafana — private, via SWAG on 443" } labels = { @@ -144,32 +175,63 @@ resource "hcloud_firewall" "swarm" { } } -# DB node firewall — SSH + DB ports from app/swarm subnet only resource "hcloud_firewall" "db" { name = "${local.name_prefix}-firewall-db" - # SSH — admin CIDRs only rule { - direction = "in" - protocol = "tcp" - port = "22" - source_ips = var.admin_allowed_cidrs + direction = "in" + protocol = "tcp" + port = "22" + source_ips = var.admin_allowed_cidrs + description = "SSH — admin CIDRs only" } - # PostgreSQL from app/swarm subnet rule { - direction = "in" - protocol = "tcp" - port = "5432" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "5432" + source_ips = [local.app_subnet_cidr] + description = "PostgreSQL from app subnet" } - # MongoDB from app/swarm subnet rule { - direction = "in" - protocol = "tcp" - port = "27017" - source_ips = [local.app_subnet_cidr] + direction = "in" + protocol = "tcp" + port = "27017" + source_ips = [local.app_subnet_cidr] + description = "MongoDB from app subnet" + } + + rule { + direction = "in" + protocol = "tcp" + port = "2377" + source_ips = [local.app_subnet_cidr] + description = "Docker Swarm control plane from app subnet" + } + + rule { + direction = "in" + protocol = "tcp" + port = "7946" + source_ips = [local.app_subnet_cidr] + description = "Docker Swarm node discovery (TCP) from app subnet" + } + + rule { + direction = "in" + protocol = "udp" + port = "7946" + source_ips = [local.app_subnet_cidr] + description = "Docker Swarm node discovery (UDP) from app subnet" + } + + rule { + direction = "in" + protocol = "udp" + port = "4789" + source_ips = [local.app_subnet_cidr] + description = "Docker Swarm VXLAN overlay from app subnet" } labels = { diff --git a/terraform/hetzner/test/terraform.tfvars.example b/terraform/hetzner/test/terraform.tfvars.example index 60ebd18..e0aa871 100644 --- a/terraform/hetzner/test/terraform.tfvars.example +++ b/terraform/hetzner/test/terraform.tfvars.example @@ -2,7 +2,7 @@ hcloud_token = "YOUR_HETZNER_TEST_PROJECT_API_TOKEN" location = "fsn1" image = "rocky-10" -server_type_swarm = "cx32" -server_type_db = "cx42" +server_type_swarm = "cpx42" +server_type_db = "cpx42" admin_ssh_public_key_path = "~/.ssh/id_ed25519.pub" admin_allowed_cidrs = ["1.2.3.4/32", "5.6.7.8/32"] diff --git a/terraform/hetzner/test/variables.tf b/terraform/hetzner/test/variables.tf index 994b14e..8cb47e8 100644 --- a/terraform/hetzner/test/variables.tf +++ b/terraform/hetzner/test/variables.tf @@ -1,6 +1,6 @@ variable "hcloud_token" { - type = string - sensitive = true + type = string + sensitive = true description = "Hetzner Cloud API token for the test project" } @@ -18,13 +18,13 @@ variable "image" { variable "server_type_swarm" { type = string - default = "cx32" + default = "cpx42" description = "Hetzner server type for the Swarm node" } variable "server_type_db" { type = string - default = "cx42" + default = "cpx42" description = "Hetzner server type for the DB node" } diff --git a/test-app-graphs.png b/test-app-graphs.png new file mode 100644 index 0000000..58e3a8c Binary files /dev/null and b/test-app-graphs.png differ diff --git a/test-db-graphs.png b/test-db-graphs.png new file mode 100644 index 0000000..856fd54 Binary files /dev/null and b/test-db-graphs.png differ