feat(vpn): add WireGuard and DB proxy services for secure management

- Add new Ansible role `wireguard` to set up WireGuard VPN server on
  DB node with key generation, firewalld rules, and client peer config.
- Introduce `pg-proxy` and `mongo-proxy` socat containers in db_stack
  to expose PostgreSQL (15432) and MongoDB (17017) on host ports,
  restricted to WireGuard subnet via firewalld.
- Update test environment group_vars with WireGuard client entry for
  `murat-inspiron-15-3525`.
- Modify act_runner config: set `docker_host` to unix socket, remove
  explicit socket mount from options, and change runner label image to
  `catthehacker/ubuntu:act-22.04`.
- Open UDP port 51820 in Hetzner firewall for WireGuard inbound.
- Adjust test-db-post-stack playbook to include wireguard role (tagged).
- Update roadmap document with APISIX init step order.
This commit is contained in:
Murat ÖZDEMİR 2026-05-13 18:50:40 +03:00
commit a9fc0b1234
12 changed files with 189 additions and 9 deletions

View File

@ -2,7 +2,8 @@
act_runner_version: "0.2.12"
act_runner_arch: "linux-amd64"
act_runner_gitea_url: "https://git.tarla.io"
# -> bunu değişkene ata ve test ve prod için farklı isimler oluştur!
act_runner_name: "iklim-test-app"
act_runner_labels: "ubuntu-latest,ubuntu-22.04,ubuntu-20.04,test-runner:docker://ubuntu:22.04"
act_runner_labels: "ubuntu-latest,ubuntu-22.04,ubuntu-20.04,test-runner:docker://catthehacker/ubuntu:act-22.04"
# Gitea'dan alınan tek seferlik registration token; kayıt olmadıysa boş bırakılır.
act_runner_registration_token: "{{ vault_gitea_runner_token | default('') }}"

View File

@ -26,12 +26,14 @@ container:
network: "iklimco-net"
enable_ipv6: false
privileged: false
# Docker socket mount: docker build/push/stack komutlarının çalışması için gerekli.
options: "-v /var/run/docker.sock:/var/run/docker.sock"
options: ""
workdir_parent: ""
valid_volumes:
- "/var/run/docker.sock"
docker_host: ""
# docker_host set edilince act_runner socket'i tek seferlik mount eder ve
# DOCKER_HOST env'ini job container'a iletir; options'daki manuel mount ile
# çakışıp "Duplicate mount point" hatasına yol açmaz.
docker_host: "unix:///var/run/docker.sock"
force_pull: false
force_rebuild: false

View File

@ -40,3 +40,41 @@ services:
placement:
constraints:
- node.labels.role == db
# WireGuard üzerinden DB manager erişimi için köprü servisler.
# Host portları firewalld ile sadece WireGuard subnet'ine (10.8.0.0/24) açılır.
pg-proxy:
image: alpine/socat:latest
command: TCP-LISTEN:5432,fork,reuseaddr TCP:postgresql:5432
ports:
- target: 5432
published: 15432
protocol: tcp
mode: host
networks:
- iklimco-net
deploy:
placement:
constraints:
- node.labels.role == db
restart_policy:
condition: on-failure
delay: 5s
mongo-proxy:
image: alpine/socat:latest
command: TCP-LISTEN:27017,fork,reuseaddr TCP:mongodb:27017
ports:
- target: 27017
published: 17017
protocol: tcp
mode: host
networks:
- iklimco-net
deploy:
placement:
constraints:
- node.labels.role == db
restart_policy:
condition: on-failure
delay: 5s

View File

@ -0,0 +1,13 @@
---
wireguard_interface: wg0
wireguard_address: "10.8.0.1/24"
wireguard_port: 51820
wireguard_subnet: "10.8.0.0/24"
# DB proxy portları — host ağında dinlenecek, sadece wireguard_subnet'ten erişilebilir
wireguard_db_pg_proxy_port: 15432
wireguard_db_mongo_proxy_port: 17017
# Her client için: name, public_key, allowed_ips
# group_vars/all/vars.yml içinde tanımlanır
wireguard_clients: []

View File

@ -0,0 +1,5 @@
---
- name: restart wireguard
ansible.builtin.systemd:
name: "wg-quick@{{ wireguard_interface }}"
state: restarted

View File

@ -0,0 +1,84 @@
---
- name: Install WireGuard
ansible.builtin.dnf:
name: wireguard-tools
state: present
- name: Ensure /etc/wireguard directory exists
ansible.builtin.file:
path: /etc/wireguard
state: directory
mode: "0700"
owner: root
group: root
- name: Check if WireGuard private key exists
ansible.builtin.stat:
path: /etc/wireguard/private.key
register: wg_key_stat
- name: Generate WireGuard keypair
ansible.builtin.shell: |
wg genkey | tee /etc/wireguard/private.key | wg pubkey > /etc/wireguard/public.key
chmod 600 /etc/wireguard/private.key
chmod 644 /etc/wireguard/public.key
when: not wg_key_stat.stat.exists
- name: Read WireGuard private key
ansible.builtin.slurp:
src: /etc/wireguard/private.key
register: wg_private_key_raw
- name: Read WireGuard public key
ansible.builtin.slurp:
src: /etc/wireguard/public.key
register: wg_public_key_raw
- name: Set WireGuard key facts
ansible.builtin.set_fact:
wg_server_private_key: "{{ wg_private_key_raw.content | b64decode | trim }}"
wg_server_public_key: "{{ wg_public_key_raw.content | b64decode | trim }}"
- name: Deploy wg0.conf
ansible.builtin.template:
src: wg0.conf.j2
dest: "/etc/wireguard/{{ wireguard_interface }}.conf"
mode: "0600"
owner: root
group: root
notify: restart wireguard
- name: Enable and start WireGuard
ansible.builtin.systemd:
name: "wg-quick@{{ wireguard_interface }}"
enabled: true
state: started
daemon_reload: true
- name: Allow WireGuard UDP port from admin CIDRs
ansible.posix.firewalld:
rich_rule: >-
rule family="ipv4" source address="{{ item }}"
port port="{{ wireguard_port }}" protocol="udp" accept
zone: drop
state: enabled
permanent: true
immediate: true
loop: "{{ admin_allowed_cidrs.split(' ') }}"
- name: Allow DB proxy ports from WireGuard subnet only
ansible.posix.firewalld:
rich_rule: >-
rule family="ipv4" source address="{{ wireguard_subnet }}"
port port="{{ item }}" protocol="tcp" accept
zone: drop
state: enabled
permanent: true
immediate: true
loop:
- "{{ wireguard_db_pg_proxy_port }}"
- "{{ wireguard_db_mongo_proxy_port }}"
- name: Print server public key (client config için gerekli)
ansible.builtin.debug:
msg: "WireGuard server public key: {{ wg_server_public_key }}"

View File

@ -0,0 +1,12 @@
[Interface]
PrivateKey = {{ wg_server_private_key }}
Address = {{ wireguard_address }}
ListenPort = {{ wireguard_port }}
{% for client in wireguard_clients %}
[Peer]
# {{ client.name }}
PublicKey = {{ client.public_key }}
AllowedIPs = {{ client.allowed_ips }}
{% endfor %}

View File

@ -9,6 +9,14 @@ admin_allowed_cidrs: "78.187.87.109/32 95.70.151.248/32"
admin_ssh_public_key_path: "~/.ssh/id_rsa.pub"
timezone: "Europe/Istanbul"
# WireGuard
# Her client için: name, public_key, allowed_ips
# public_key: client makinasında `wg genkey | tee client.key | wg pubkey` ile üretilir
wireguard_clients:
- name: murat-inspiron-15-3525
public_key: "8nYHZvSwxCr2uwOYohLG3DlC85NbVNhKnPxYtOEKvE0="
allowed_ips: 10.8.0.2/32
# DB Stack
db_postgres_image: "postgis/postgis:17-3.5"
db_mongo_image: "mongo:8"

View File

@ -1,10 +1,18 @@
---
- name: DB Node - StorageBox Dizinleri ve MongoDB Konfigürasyonu
# WireGuard client eklemek için group_vars/all/vars.yml içindeki
# wireguard_clients listesine client public key'ini ekleyin.
#
# Sadece WireGuard güncellemek için:
# ansible-playbook test-db-post-stack.yml --vault-password-file=.vault_pass --tags wireguard
- name: DB Node - StorageBox Dizinleri, MongoDB Konfigürasyonu ve WireGuard
hosts: db
become: yes
roles:
- role: db_stack
tags: [db_stack]
- role: wireguard
tags: [wireguard]
- name: App Node - DB Stack Deploy
hosts: app

View File

@ -133,9 +133,10 @@ Final step order in the pipeline:
13. Docker Login to Harbor
14. **Prepare SWAG Directories** ← NEW
15. Deploy Swarm Stack
16. **Bootstrap SWAG Certificate** ← NEW
17. **Run Database Init Scripts** ← NEW (önceki oturumda eklendi)
18. Review Environment
16. **Run APISIX Init** ← NEW (Swarm etcd volume'süz başlar; idempotent PUT)
17. **Bootstrap SWAG Certificate** ← NEW
18. **Run Database Init Scripts** ← NEW (önceki oturumda eklendi)
19. Review Environment
> Steps 8 (Provision Vault) runs before SWAG because it creates Docker secrets and
> AppRole IDs — Vault must be reachable for this. On re-deploys, Vault is already

View File

@ -186,6 +186,14 @@ resource "hcloud_firewall" "db" {
description = "SSH — admin CIDRs only"
}
rule {
direction = "in"
protocol = "udp"
port = "51820"
source_ips = ["0.0.0.0/0", "::/0"]
description = "WireGuard VPN — public (auth kriptografik anahtar ile yapılır)"
}
rule {
direction = "in"
protocol = "tcp"