WireGuard ile Docker Ağı Entegrasyonu: Güvenli Konteyner İletişimi

Docker ortamında çalışırken en çok karşılaştığım sorunlardan biri, containerların network izolasyonunu korurken aynı zamanda güvenli bir VPN tüneli üzerinden erişilebilir olmasını sağlamak. Özellikle birden fazla sunucuda dağıtık servisler çalıştırıyorsanız ve bu servislerin birbirleriyle güvenli konuşması gerekiyorsa, WireGuard ile Docker ağını entegre etmek hem performans hem de güvenlik açısından çok iyi bir çözüm sunuyor. Bu yazıda sıfırdan kurulumdan başlayarak production ortamında kullandığım gerçek senaryolara kadar her şeyi ele alacağım.

Neden WireGuard + Docker?

Klasik VPN çözümleri olan OpenVPN veya IPSec’e kıyasla WireGuard, çekirdek seviyesinde çalışması sayesinde çok daha düşük gecikme ve yüksek throughput sağlıyor. Docker tarafında da durum benzer: Container’lar arasındaki network iletişimi zaten Docker’ın bridge ağı üzerinden gidiyor ama bu sadece tek bir host ile sınırlı.

Şöyle bir senaryo düşünün: İstanbul’daki sunucunuzda bir PostgreSQL container’ınız var, Amsterdam’dakinde ise bir uygulama sunucusu. Bu ikisinin birbirleriyle konuşması için ya her şeyi internet üzerinden açıyorsunuz (kötü fikir) ya da bir VPN çözümü kuruyorsunuz. WireGuard burada devreye giriyor ve her iki taraftaki Docker networklerini sanki aynı yerel ağdaymış gibi birbirine bağlıyor.

Temel avantajlar:

  • Kernel space’de çalıştığı için iperf testlerinde OpenVPN’e göre 3-4 kat daha iyi throughput
  • Konfigürasyon dosyası son derece sade, 10 satırla çalışır hale gelir
  • Docker’ın overlay network’ü yerine gerçek bir şifreli tünel sağlar
  • Multi-host Docker ortamlarında Swarm ya da Kubernetes olmadan da çalışır

Temel Kavramlar ve Mimari

Kuruluma geçmeden önce mimariden bahsetmek istiyorum çünkü kafada net bir resim olmadan yapılan kurulumlar genellikle yarım kalıyor.

WireGuard, her node için bir arayüz oluşturuyor (örneğin wg0). Bu arayüz üzerinden gelen/giden trafik şifreleniyor. Docker’ın network’leri ise docker0 bridge’i veya özel oluşturulan bridge network’leri üzerinden çalışıyor.

Yapacağımız şey şu: WireGuard tüneli üzerinden iki sunucuyu birbirine bağlayacağız, ardından her sunucudaki Docker subnet’ini karşı tarafa route edeceğiz. Böylece İstanbul’daki container, Amsterdam’daki container’ın IP’sine direkt erişebilecek.

Örnek mimarimiz:

  • Sunucu A (İstanbul): Public IP 1.2.3.4, WireGuard IP 10.0.0.1, Docker subnet 172.20.0.0/24
  • Sunucu B (Amsterdam): Public IP 5.6.7.8, WireGuard IP 10.0.0.2, Docker subnet 172.21.0.0/24

Sistem Hazırlığı

Her iki sunucuda da kernel modülünü yükleyelim ve gerekli paketleri kuralım.

# Ubuntu/Debian için
apt update && apt install -y wireguard wireguard-tools

# Kernel modülünü yükle
modprobe wireguard

# Modülün kalıcı olması için
echo "wireguard" >> /etc/modules-load.d/wireguard.conf

# IP forwarding'i etkinleştir (kritik!)
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
echo "net.ipv4.conf.all.proxy_arp=1" >> /etc/sysctl.conf
sysctl -p

IP forwarding olmadan container trafiğinin WireGuard arayüzünden geçmesi mümkün değil. Bu adımı atlayan sistemlerin yarısı bu yüzden çalışmıyor.

WireGuard Anahtar Üretimi

Her sunucu için ayrı private/public key çifti oluşturuyoruz.

# Her iki sunucuda da çalıştır
cd /etc/wireguard

# Key üretimi
wg genkey | tee privatekey | wg pubkey > publickey

# İzinleri kısıtla
chmod 600 privatekey
chmod 644 publickey

# Key'leri görüntüle
echo "Private key: $(cat privatekey)"
echo "Public key: $(cat publickey)"

Bu komutları çalıştırdıktan sonra her iki sunucunun public key’ini not alın. Sunucu A’nın public key’ini Sunucu B’ye, Sunucu B’ninkini de Sunucu A’ya ekleyeceğiz.

WireGuard Konfigürasyonu

Sunucu A (İstanbul) Konfigürasyonu

cat > /etc/wireguard/wg0.conf << 'EOF'
[Interface]
PrivateKey = <SUNUCU_A_PRIVATE_KEY>
Address = 10.0.0.1/24
ListenPort = 51820
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
PublicKey = <SUNUCU_B_PUBLIC_KEY>
AllowedIPs = 10.0.0.2/32, 172.21.0.0/24
Endpoint = 5.6.7.8:51820
PersistentKeepalive = 25
EOF

Sunucu B (Amsterdam) Konfigürasyonu

cat > /etc/wireguard/wg0.conf << 'EOF'
[Interface]
PrivateKey = <SUNUCU_B_PRIVATE_KEY>
Address = 10.0.0.2/24
ListenPort = 51820
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
PublicKey = <SUNUCU_A_PUBLIC_KEY>
AllowedIPs = 10.0.0.1/32, 172.20.0.0/24
Endpoint = 1.2.3.4:51820
PersistentKeepalive = 25
EOF

AllowedIPs kısmına dikkat edin. Hem WireGuard peer IP’sini hem de karşı taraftaki Docker subnet’ini ekliyoruz. Bu sayede Docker container trafiği WireGuard tünelinden geçiyor.

Docker Network Yapılandırması

Şimdi her sunucuda ayrı subnet’lere sahip özel Docker network’leri oluşturacağız. Bu önemli çünkü iki sunucudaki Docker networklerinin aynı subnet’te olması routing karmaşasına yol açıyor.

# Sunucu A'da
docker network create 
  --driver bridge 
  --subnet 172.20.0.0/24 
  --gateway 172.20.0.1 
  --opt com.docker.network.bridge.name=docker_vpn 
  vpn_network

# Sunucu B'de
docker network create 
  --driver bridge 
  --subnet 172.21.0.0/24 
  --gateway 172.21.0.1 
  --opt com.docker.network.bridge.name=docker_vpn 
  vpn_network

com.docker.network.bridge.name parametresiyle bridge arayüzüne özel bir isim veriyoruz. Bu ilerleyen adımlarda iptables kuralları yazarken hayat kurtarıyor.

Routing Kuralları

WireGuard arayüzü ayağa kalktıktan sonra Docker subnet’lerine giden trafiğin doğru arayüzden çıkmasını sağlamak için route eklemeliyiz.

# Sunucu A'da - Amsterdam Docker subnet'ine route ekle
ip route add 172.21.0.0/24 via 10.0.0.2 dev wg0

# Bu route'u kalıcı yapmak için /etc/wireguard/wg0.conf'a PostUp ekle
# PostUp satırını şöyle güncelleyebilirsiniz:
# PostUp = ip route add 172.21.0.0/24 via 10.0.0.2 dev wg0; iptables ...
# PostDown = ip route del 172.21.0.0/24; iptables ...

# Sunucu B'de - İstanbul Docker subnet'ine route ekle
ip route add 172.20.0.0/24 via 10.0.0.1 dev wg0

Docker Compose ile Entegrasyon

Gerçek dünyada containerları tek tek çalıştırmak yerine Docker Compose kullanıyoruz. İşte iki sunucu arasındaki servis iletişimini gösteren bir compose dosyası.

Sunucu A (İstanbul) – docker-compose.yml:

version: '3.8'

services:
  postgres:
    image: postgres:15
    container_name: postgres_main
    environment:
      POSTGRES_DB: appdb
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: guclu_sifre_buraya
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      vpn_network:
        ipv4_address: 172.20.0.10
    # Sadece VPN üzerinden erişilebilir, dışarıya port açma!

  redis:
    image: redis:7-alpine
    container_name: redis_cache
    networks:
      vpn_network:
        ipv4_address: 172.20.0.11

networks:
  vpn_network:
    external: true

volumes:
  postgres_data:

Sunucu B (Amsterdam) – docker-compose.yml:

version: '3.8'

services:
  webapp:
    image: myapp:latest
    container_name: webapp
    environment:
      DATABASE_URL: postgresql://appuser:[email protected]:5432/appdb
      REDIS_URL: redis://172.20.0.11:6379
    ports:
      - "80:8080"
      - "443:8443"
    networks:
      vpn_network:
        ipv4_address: 172.21.0.10
    depends_on:
      - nginx

  nginx:
    image: nginx:alpine
    container_name: nginx_proxy
    networks:
      vpn_network:
        ipv4_address: 172.21.0.11

networks:
  vpn_network:
    external: true

Bu yapılandırmada webapp container’ı, İstanbul’daki PostgreSQL’e sanki yerel ağdaymış gibi 172.20.0.10 IP’siyle bağlanıyor. Tüm trafik WireGuard tünelinden şifreli geçiyor.

WireGuard’ı Container İçinde Çalıştırmak

Bazen host seviyesinde WireGuard kurmak yerine WireGuard’ı bir container içinde çalıştırmak isteyebilirsiniz. Bu özellikle managed cloud ortamlarında ya da kernel modülü yükleyemediğiniz durumlarda işe yarıyor.

# wg-easy gibi hazır bir image kullanabilirsiniz
docker run -d 
  --name wireguard 
  --cap-add NET_ADMIN 
  --cap-add SYS_MODULE 
  --sysctl net.ipv4.ip_forward=1 
  --sysctl net.ipv4.conf.all.src_valid_mark=1 
  -e WG_HOST=1.2.3.4 
  -e PASSWORD=admin_sifreniz 
  -e WG_DEFAULT_ADDRESS=10.0.0.x 
  -e WG_DEFAULT_DNS=1.1.1.1 
  -p 51820:51820/udp 
  -p 51821:51821/tcp 
  -v ~/.wireguard:/etc/wireguard 
  --restart unless-stopped 
  ghcr.io/wg-easy/wg-easy

Bu yaklaşımda NET_ADMIN ve SYS_MODULE capability’leri kritik. Bunlar olmadan container network interface oluşturamaz.

Güvenlik Hardening

VPN kurulumu tek başına yeterli değil. Production ortamında şu iptables kurallarını da eklemenizi öneririm:

# Sadece WireGuard portuna izin ver
iptables -A INPUT -p udp --dport 51820 -j ACCEPT

# WireGuard interface'inden gelen trafiğe izin ver
iptables -A INPUT -i wg0 -j ACCEPT
iptables -A FORWARD -i wg0 -j ACCEPT
iptables -A FORWARD -o wg0 -j ACCEPT

# Docker VPN network'ünden internet'e çıkışı engelle (sadece peer-to-peer)
iptables -A FORWARD -s 172.20.0.0/24 -d 0.0.0.0/0 -o eth0 -j DROP
iptables -A FORWARD -s 172.21.0.0/24 -d 0.0.0.0/0 -o eth0 -j DROP

# Bu kuralları kalıcı yap
apt install -y iptables-persistent
netfilter-persistent save

Son iki kural önemli: Docker container’larının WireGuard VPN dışındaki trafiği internet üzerinden gitmesini engelliyoruz. Böylece container bir şekilde yanlış bir IP’ye bağlanmaya çalışırsa trafik internete çıkmıyor.

Bağlantı Testi ve Doğrulama

Kurulum sonrası her şeyin çalıştığını doğrulamak için:

# WireGuard servisini başlat
systemctl enable wg-quick@wg0
systemctl start wg-quick@wg0

# Bağlantı durumunu kontrol et
wg show

# Peer'ın görünüp görünmediğini kontrol et
wg show wg0 peers

# Route tablosunu doğrula
ip route show | grep 172.2

# Sunucu A'dan Sunucu B'deki container'a ping at
ping 172.21.0.10

# Container içinden test
docker exec -it webapp ping 172.20.0.10
docker exec -it webapp curl http://172.20.0.10:5432  # PostgreSQL portuna erişim testi

wg show komutunun çıktısında latest handshake satırı görüyorsanız bağlantı kurulmuş demektir. Eğer bu satır yoksa iki peer henüz el sıkışamamış, büyük ihtimalle firewall veya routing problemi var.

Sorun Giderme

Karşılaştığım en yaygın sorunlar ve çözümleri:

Container’lar birbirini göremiyor:

  • Her iki sunucuda da ip_forward aktif mi kontrol et: sysctl net.ipv4.ip_forward
  • Docker network’lerin farklı subnet’lerde olduğunu doğrula
  • wg show ile handshake gerçekleşmiş mi bak
  • tcpdump -i wg0 icmp ile paketin arayüze ulaşıp ulaşmadığını kontrol et

WireGuard handshake gerçekleşmiyor:

  • UDP 51820 portunu firewall’da açtığından emin ol
  • Endpoint IP ve portun doğru yazıldığını kontrol et
  • Key’lerin doğru sunuculara atandığını doğrula (yanlış tarafa atamak çok sık yapılan hata)

Container restart sonrası route kayboluyor:

  • Route ekleme işlemini WireGuard’ın PostUp direktifine taşı
  • Docker network oluşturma işlemini de bir startup script’e al

Docker ağ adreslemesi çakışıyor:

  • docker network inspect vpn_network ile subnet’i doğrula
  • Gerekirse docker network rm vpn_network yapıp farklı subnet ile yeniden oluştur

Production’da Kullandığım Otomasyon

Bu kurulumu her seferinde elle yapmak yerine basit bir shell script’e döktüm:

#!/bin/bash
# wireguard-docker-setup.sh

set -e

SERVER_ROLE=$1  # "istanbul" veya "amsterdam"
PEER_PUBLIC_IP=$2
PEER_PUBLIC_KEY=$3

if [ "$SERVER_ROLE" = "istanbul" ]; then
    WG_IP="10.0.0.1/24"
    DOCKER_SUBNET="172.20.0.0/24"
    PEER_WG_IP="10.0.0.2"
    PEER_DOCKER_SUBNET="172.21.0.0/24"
else
    WG_IP="10.0.0.2/24"
    DOCKER_SUBNET="172.21.0.0/24"
    PEER_WG_IP="10.0.0.1"
    PEER_DOCKER_SUBNET="172.20.0.0/24"
fi

# Key üret
cd /etc/wireguard
wg genkey | tee privatekey | wg pubkey > publickey
chmod 600 privatekey

PRIVATE_KEY=$(cat privatekey)
PUBLIC_KEY=$(cat publickey)

echo "Bu sunucunun Public Key'i: $PUBLIC_KEY"
echo "Peer konfigürasyonuna bu key'i ekleyin."

# Konfigürasyon yaz
cat > /etc/wireguard/wg0.conf << EOF
[Interface]
PrivateKey = $PRIVATE_KEY
Address = $WG_IP
ListenPort = 51820
PostUp = ip route add $PEER_DOCKER_SUBNET via $PEER_WG_IP dev wg0; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = ip route del $PEER_DOCKER_SUBNET; iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
PublicKey = $PEER_PUBLIC_KEY
AllowedIPs = $PEER_WG_IP/32, $PEER_DOCKER_SUBNET
Endpoint = $PEER_PUBLIC_IP:51820
PersistentKeepalive = 25
EOF

# Docker network oluştur
docker network create 
  --driver bridge 
  --subnet $DOCKER_SUBNET 
  --opt com.docker.network.bridge.name=docker_vpn 
  vpn_network 2>/dev/null || echo "Network zaten mevcut"

# Servisi başlat
systemctl enable wg-quick@wg0
systemctl start wg-quick@wg0

echo "Kurulum tamamlandı. WireGuard durumu:"
wg show

Bu script’i şöyle çalıştırıyorum:

chmod +x wireguard-docker-setup.sh
./wireguard-docker-setup.sh istanbul 5.6.7.8 <SUNUCU_B_PUBLIC_KEY>

Sonuç

WireGuard ile Docker ağını entegre etmek başlangıçta karmaşık görünse de temel mantığı kavradıktan sonra oldukça şık bir çözüm ortaya çıkıyor. Kernel seviyesinde çalışan WireGuard, container trafiğini şifreleyerek taşırken performanstan neredeyse hiç ödün vermiyor.

Bu yazıda anlattığım kurulumla elde ettiğiniz şeyler:

  • Farklı fiziksel sunuculardaki Docker container’ları arasında şifreli, düşük gecikmeli iletişim
  • Veritabanı gibi hassas servislerin internete açık port olmadan erişilebilmesi
  • İptables kurallarıyla kontrol altında tutulan, sıkılaştırılmış bir network güvenlik katmanı
  • Kolayca genişletilebilir bir yapı (üçüncü bir sunucu eklemek [Peer] bloğu eklemekten ibaret)

Birden fazla sunucuya çıkmanız gerektiğinde veya mevcut multi-host Docker ortamınızı güvenli hale getirmek istediğinizde bu yaklaşımı kesinlikle tavsiye ediyorum. Swarm veya Kubernetes gibi orkestrasyon araçları kullanmıyorsanız bu yöntem, sizi gereksiz karmaşıklıktan korurken güvenliği garantiliyor.

Yorum yapın