From 8dae0456825d1ea6e7631ade1e4ba7cac320642d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Murat=20=C3=96ZDEM=C4=B0R?= Date: Sun, 21 Jun 2026 20:20:36 +0300 Subject: [PATCH] fix(wireguard): resolve docker forward drop and firewalld interface binding issues --- ansible/prod/group_vars/all/vars.yml | 8 ++ ansible/prod/prod-db-wireguard.yml | 25 ++++++ ansible/prod/roles/db_stack/tasks/db_node.yml | 18 ++++ ansible/roles/base/tasks/main.yml | 1 + ansible/roles/wireguard/defaults/main.yml | 4 + ansible/roles/wireguard/tasks/main.yml | 83 +++++++++++++++++++ terraform/hetzner/prod/firewall.tf | 23 +++++ 7 files changed, 162 insertions(+) create mode 100644 ansible/prod/prod-db-wireguard.yml diff --git a/ansible/prod/group_vars/all/vars.yml b/ansible/prod/group_vars/all/vars.yml index 91ad744..c7db130 100644 --- a/ansible/prod/group_vars/all/vars.yml +++ b/ansible/prod/group_vars/all/vars.yml @@ -24,6 +24,14 @@ storagebox_managed_directories: mode: "0777" iklim_password: "{{ vault_iklim_password }}" + +# WireGuard — prod (runs on iklim-db-01; routes developer traffic to 10.20.20.0/24) +wireguard_enable_routing: true +wireguard_routed_subnet: "10.20.20.0/24" +wireguard_clients: + - name: murat-inspiron-15-3525 + public_key: "8nYHZvSwxCr2uwOYohLG3DlC85NbVNhKnPxYtOEKvE0=" + allowed_ips: 10.8.0.2/32 act_runner_labels: "prod-runner:docker://catthehacker/ubuntu:act-22.04,ubuntu-24.04,{{ inventory_hostname }}" swarm_manager_ip: "10.20.10.11" mongodb_replset_name: "rs0" diff --git a/ansible/prod/prod-db-wireguard.yml b/ansible/prod/prod-db-wireguard.yml new file mode 100644 index 0000000..2afe967 --- /dev/null +++ b/ansible/prod/prod-db-wireguard.yml @@ -0,0 +1,25 @@ +--- +# WireGuard setup — iklim-db-01 (prod VPN gateway for developer DB access) +# +# Full setup (WireGuard + db_stack firewall rules on all DB nodes): +# ansible-playbook prod-db-wireguard.yml --vault-password-file=../.vault_pass +# +# WireGuard only (re-deploy config or update peers): +# ansible-playbook prod-db-wireguard.yml --vault-password-file=../.vault_pass --tags wireguard +# +# DB node firewall rules only: +# ansible-playbook prod-db-wireguard.yml --vault-password-file=../.vault_pass --tags db_stack + +- name: DB-01 — WireGuard (Prod Developer Access) + hosts: iklim-db-01 + become: yes + roles: + - role: wireguard + tags: [wireguard] + +- name: DB Nodes — Firewalld DB/etcd Port Rules + hosts: iklim-db-* + become: yes + roles: + - role: db_stack + tags: [db_stack] diff --git a/ansible/prod/roles/db_stack/tasks/db_node.yml b/ansible/prod/roles/db_stack/tasks/db_node.yml index f05ab71..700ee75 100644 --- a/ansible/prod/roles/db_stack/tasks/db_node.yml +++ b/ansible/prod/roles/db_stack/tasks/db_node.yml @@ -41,3 +41,21 @@ mode: '0400' owner: "{{ storagebox_uid }}" group: "{{ storagebox_gid }}" + +# Allow DB and etcd ports from within the DB subnet so that: +# - db-02/03 accept WireGuard-masqueraded traffic (src = 10.20.20.11 after NAT on db-01) +# - db-01 handles intra-subnet Patroni/etcd coordination and masquerade return traffic +# WireGuard client direct access to db-01 (src 10.8.0.0/24) is handled by the wireguard role. +- name: Allow DB and etcd ports from DB subnet in firewalld + ansible.posix.firewalld: + rich_rule: >- + rule family="ipv4" source address="10.20.20.0/24" + port port="{{ item }}" protocol="tcp" accept + zone: drop + state: enabled + permanent: true + immediate: true + loop: + - "5432" + - "27017" + - "2379" diff --git a/ansible/roles/base/tasks/main.yml b/ansible/roles/base/tasks/main.yml index 3aeb5fa..764e073 100644 --- a/ansible/roles/base/tasks/main.yml +++ b/ansible/roles/base/tasks/main.yml @@ -30,6 +30,7 @@ - python3-passlib - htop - btop + - tmux state: present - name: Set timezone diff --git a/ansible/roles/wireguard/defaults/main.yml b/ansible/roles/wireguard/defaults/main.yml index 12e5ff9..b01c8d0 100644 --- a/ansible/roles/wireguard/defaults/main.yml +++ b/ansible/roles/wireguard/defaults/main.yml @@ -11,3 +11,7 @@ wireguard_db_mongo_proxy_port: 27017 # Her client için: name, public_key, allowed_ips # group_vars/all/vars.yml içinde tanımlanır wireguard_clients: [] + +# IP forwarding + firewalld policy — prod db-01 enables this +wireguard_enable_routing: false +wireguard_routed_subnet: "" # prod: "10.20.20.0/24" diff --git a/ansible/roles/wireguard/tasks/main.yml b/ansible/roles/wireguard/tasks/main.yml index 6518b87..03ecf01 100644 --- a/ansible/roles/wireguard/tasks/main.yml +++ b/ansible/roles/wireguard/tasks/main.yml @@ -82,3 +82,86 @@ - name: Print server public key (client config için gerekli) ansible.builtin.debug: msg: "WireGuard server public key: {{ wg_server_public_key }}" + +- name: Enable IP forwarding (persistent) + ansible.posix.sysctl: + name: net.ipv4.ip_forward + value: '1' + sysctl_file: /etc/sysctl.d/99-wireguard.conf + state: present + reload: yes + when: wireguard_enable_routing + +- name: Bind WireGuard interface to drop zone permanently + ansible.builtin.command: firewall-cmd --permanent --zone=drop --add-interface={{ wireguard_interface }} + register: _wg_zone + failed_when: _wg_zone.rc != 0 and 'ALREADY_ENABLED' not in _wg_zone.stderr + changed_when: _wg_zone.rc == 0 + +- name: Bind routed interface to drop zone permanently + ansible.builtin.command: firewall-cmd --permanent --zone=drop --add-interface={{ wireguard_routed_interface | default('eth1') }} + register: _eth1_zone + failed_when: _eth1_zone.rc != 0 and 'ALREADY_ENABLED' not in _eth1_zone.stderr + changed_when: _eth1_zone.rc == 0 + when: wireguard_enable_routing and wireguard_routed_subnet != "" + +- name: Create firewalld policy for WireGuard routing + ansible.builtin.command: firewall-cmd --permanent --new-policy=wg-to-db + register: _policy_create + failed_when: _policy_create.rc != 0 and 'NAME_CONFLICT' not in _policy_create.stderr and 'already exists' not in _policy_create.stderr + changed_when: _policy_create.rc == 0 + when: wireguard_enable_routing + +- name: Set policy ingress zone + ansible.builtin.command: firewall-cmd --permanent --policy=wg-to-db --add-ingress-zone=drop + register: _ingress + failed_when: _ingress.rc != 0 and 'already' not in _ingress.stderr | lower + changed_when: _ingress.rc == 0 + when: wireguard_enable_routing + +- name: Set policy egress zone + ansible.builtin.command: firewall-cmd --permanent --policy=wg-to-db --add-egress-zone=drop + register: _egress + failed_when: _egress.rc != 0 and 'already' not in _egress.stderr | lower + changed_when: _egress.rc == 0 + when: wireguard_enable_routing + +- name: Add forward rule to policy (WG subnet to DB subnet only) + ansible.builtin.command: > + firewall-cmd --permanent --policy=wg-to-db + --add-rich-rule='rule family="ipv4" source address="{{ wireguard_subnet }}" + destination address="{{ wireguard_routed_subnet }}" accept' + register: _fwd_rule + failed_when: _fwd_rule.rc != 0 and 'already' not in _fwd_rule.stderr | lower + changed_when: _fwd_rule.rc == 0 + when: wireguard_enable_routing and wireguard_routed_subnet != "" + +- name: Enable masquerade on policy + ansible.builtin.command: firewall-cmd --permanent --policy=wg-to-db --add-masquerade + register: _masq + failed_when: _masq.rc != 0 and 'already' not in _masq.stderr | lower + changed_when: _masq.rc == 0 + when: wireguard_enable_routing + +- name: Add direct firewalld rule to allow wg0 to eth1 forwarding in iptables (Docker fix) + ansible.builtin.command: > + firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -i {{ wireguard_interface }} -o {{ wireguard_routed_interface | default('eth1') }} -j ACCEPT + register: _fw_dir_fwd + failed_when: _fw_dir_fwd.rc != 0 and 'ALREADY_ENABLED' not in _fw_dir_fwd.stderr + changed_when: _fw_dir_fwd.rc == 0 + when: wireguard_enable_routing and wireguard_routed_subnet != "" + notify: restart wireguard + +- name: Add direct firewalld rule to allow eth1 to wg0 forwarding in iptables (Docker fix) + ansible.builtin.command: > + firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -i {{ wireguard_routed_interface | default('eth1') }} -o {{ wireguard_interface }} -j ACCEPT + register: _fw_dir_rev + failed_when: _fw_dir_rev.rc != 0 and 'ALREADY_ENABLED' not in _fw_dir_rev.stderr + changed_when: _fw_dir_rev.rc == 0 + when: wireguard_enable_routing and wireguard_routed_subnet != "" + notify: restart wireguard + +- name: Reload firewalld to activate routing policy + ansible.builtin.command: firewall-cmd --reload + changed_when: false + when: wireguard_enable_routing diff --git a/terraform/hetzner/prod/firewall.tf b/terraform/hetzner/prod/firewall.tf index eee87d2..ac66bfe 100644 --- a/terraform/hetzner/prod/firewall.tf +++ b/terraform/hetzner/prod/firewall.tf @@ -279,3 +279,26 @@ resource "hcloud_firewall" "db" { role = "db" } } + +resource "hcloud_firewall" "db_wireguard" { + name = "${local.name_prefix}-firewall-db-wireguard" + + rule { + direction = "in" + protocol = "udp" + port = "51820" + source_ips = ["0.0.0.0/0", "::/0"] + description = "WireGuard VPN — public (auth via cryptographic key, db-01 only)" + } + + labels = { + environment = local.environment + role = "db" + purpose = "wireguard" + } +} + +resource "hcloud_firewall_attachment" "db_wireguard" { + firewall_id = hcloud_firewall.db_wireguard.id + server_ids = [hcloud_server.db["iklim-db-01"].id] +}