UFW ile Docker Güvenlik Duvarı Entegrasyonu

Docker kullanmaya başladığınızda fark ettiğiniz ilk şeylerden biri, UFW kurallarınızın container’larla pek iyi anlaşmadığıdır. UFW’yi sıkı sıkıya yapılandırırsınız, her şey güzel görünür, sonra Docker bir container ayağa kaldırır ve o port dışarıya açılıverir. Bu durum, özellikle production sunucularında ciddi güvenlik açıklarına yol açabiliyor. Bu yazıda bu problemi kökten çözeceğiz ve UFW ile Docker’ı gerçekten birlikte çalışır hale getireceğiz.

Sorunun Kökeni: Docker ve iptables

Docker, varsayılan olarak kendi iptables kurallarını yönetir. Her container başlatıldığında Docker, DOCKER chain’ine kurallar ekler ve bu kurallar UFW’nin önüne geçer. UFW, arka planda iptables kullanır ama Docker’ın eklediği kurallar UFW’nin FORWARD chain engellemelerini bypass eder.

Pratik bir örnekle açıklayalım: Diyelim ki 8080 portunu UFW ile kapattınız.

sudo ufw deny 8080
sudo ufw status

Çıktıda 8080 portu DENY olarak görünür. Ama Docker ile bir container çalıştırdığınızda:

docker run -d -p 8080:80 nginx

Bu container’a dışarıdan erişebilirsiniz. UFW kuralınız hiç yokmuş gibi davranır. Çünkü Docker, iptables -I DOCKER -p tcp --dport 8080 -j ACCEPT gibi kurallar ekler ve bu kurallar UFW’nin işlediği INPUT chain yerine FORWARD chain’de devreye girer.

Temel Yapılandırma: Docker’ın iptables Müdahalesini Sınırlamak

İlk ve en etkili çözüm, Docker’ın iptables kurallarını doğrudan değiştirmesini engellemektir. Bunun için Docker daemon yapılandırmasını düzenleriz.

sudo nano /etc/docker/daemon.json

Dosyaya şu içeriği ekleyin:

{
  "iptables": false
}

Docker’ı yeniden başlatın:

sudo systemctl restart docker

Dikkat: Bu yaklaşım Docker’ın ağ yönetimini tamamen devre dışı bırakır. Container’lar arası iletişim için manuel iptables kuralları yazmanız gerekir. Production ortamında bu yöntemi tercih etmiyorum çünkü Docker Swarm veya karmaşık ağ topolojilerinde yönetmesi çok zorlaşıyor.

Daha dengeli bir yaklaşım için farklı bir yol izleyelim.

Dengeli Yaklaşım: DOCKER-USER Chain Kullanımı

Docker, iptables’a müdahale edebileceğiniz özel bir chain sağlar: DOCKER-USER. Bu chain, Docker’ın kendi kurallarından önce işlenir ve override edilmez. UFW entegrasyonu için ideal nokta burasıdır.

Önce mevcut durumu kontrol edelim:

sudo iptables -L DOCKER-USER -n --line-numbers

Şu an bu chain muhtemelen boştur ya da sadece bir RETURN kuralı vardır. Şimdi bu chain’e güvenlik kuralları ekleyelim.

# Loopback interface'e her zaman izin ver
sudo iptables -I DOCKER-USER -i lo -j ACCEPT

# Kurulu bağlantılara izin ver
sudo iptables -I DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# Belirli bir IP'ye container erişimi ver (örneğin ofis IP'si)
sudo iptables -I DOCKER-USER -s 203.0.113.10 -j ACCEPT

# Diğer tüm dış erişimleri engelle
sudo iptables -A DOCKER-USER -j DROP

Bu kuralları kalıcı hale getirmek için iptables-persistent kullanın:

sudo apt install iptables-persistent
sudo netfilter-persistent save

UFW Varsayılan Yapılandırması

Şimdi UFW tarafını doğru yapılandıralım. Önce UFW’nin forward politikasını ayarlamamız gerekiyor.

sudo nano /etc/default/ufw

Şu satırı bulun ve değiştirin:

DEFAULT_FORWARD_POLICY="ACCEPT"

Bu ayar Docker container’larının birbirleriyle ve dış dünyayla iletişimini sağlar. Ardından UFW’nin before.rules dosyasını düzenleyelim:

sudo nano /etc/ufw/before.rules

Dosyanın en başına, *filter satırından önce şunu ekleyin:

*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING ! -o docker0 -s 172.17.0.0/16 -j MASQUERADE
COMMIT

UFW’yi yeniden başlatın:

sudo ufw reload

Gerçek Dünya Senaryosu 1: Web Sunucusu Güvenliği

Diyelim ki bir VPS üzerinde Docker ile birkaç web uygulaması çalıştırıyorsunuz. Nginx reverse proxy arkasında 3 farklı uygulama var. Sadece 80 ve 443 portları dışarıya açık olmalı, uygulama portları (3000, 4000, 5000) sadece localhost üzerinden erişilebilir olmalı.

Önce UFW temel kuralları:

# Temel UFW kurulumu
sudo ufw default deny incoming
sudo ufw default allow outgoing

# SSH erişimi (kendi IP'nizden)
sudo ufw allow from 203.0.113.10 to any port 22

# Web trafiği
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# UFW'yi etkinleştir
sudo ufw enable

Docker Compose dosyanızda container’ları sadece localhost’a bağlayın:

version: '3.8'
services:
  app1:
    image: myapp:latest
    ports:
      - "127.0.0.1:3000:3000"
  
  app2:
    image: anotherapp:latest
    ports:
      - "127.0.0.1:4000:4000"
  
  nginx:
    image: nginx:alpine
    ports:
      - "0.0.0.0:80:80"
      - "0.0.0.0:443:443"

127.0.0.1:3000:3000 şeklinde bind etmek, Docker’ın bu portu sadece localhost’ta dinlemesini sağlar. Dış erişim tamamen engellenir ve bu yaklaşım UFW kurallarından bağımsız olarak güvenli çalışır.

Gerçek Dünya Senaryosu 2: Veritabanı Güvenliği

Production ortamında en sık yapılan hatalardan biri, veritabanı portlarını yanlışlıkla dışarıya açmaktır. PostgreSQL varsayılan olarak 5432 portunu dinler ve Docker ile çalıştırıldığında bu port dışarıya sızabilir.

# Güvensiz yaklaşım - KULLANMAYIN
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=secret postgres

# Güvenli yaklaşım
docker run -d -p 127.0.0.1:5432:5432 -e POSTGRES_PASSWORD=secret postgres

Eğer uzak bir uygulama sunucusundan veritabanına erişmeniz gerekiyorsa, sadece o sunucunun IP’sine izin verin:

# Uygulama sunucusuna (10.0.0.5) PostgreSQL erişimi ver
sudo iptables -I DOCKER-USER -s 10.0.0.5 -p tcp --dport 5432 -j ACCEPT

# Diğer tüm kaynaklara PostgreSQL erişimini engelle
sudo iptables -I DOCKER-USER -p tcp --dport 5432 -j DROP

Bu kuralları /etc/iptables/rules.v4 dosyasına kaydedin:

sudo netfilter-persistent save
sudo netfilter-persistent reload

UFW ve Docker Entegrasyonu için ufw-docker Aracı

Manuel iptables kuralları yazmak yorucu ve hata yapmaya açık. ufw-docker scripti bu süreci otomatikleştirir ve UFW komut sözdizimine benzer bir arayüz sunar.

Kurulum:

sudo wget -O /usr/local/bin/ufw-docker 
  https://github.com/chaifeng/ufw-docker/raw/master/ufw-docker
sudo chmod +x /usr/local/bin/ufw-docker

Kurulum sonrası UFW after.rules dosyasını güncellemeniz gerekir:

sudo ufw-docker install
sudo systemctl restart ufw

Artık Docker container’larına UFW benzeri komutlarla kural ekleyebilirsiniz:

# nginx container'ının 80 portunu dışarıya aç
sudo ufw-docker allow nginx 80

# Belirli bir IP'ye container erişimi ver
sudo ufw-docker allow nginx 80/tcp 203.0.113.10

# Mevcut kuralları listele
sudo ufw-docker status

# Kural sil
sudo ufw-docker delete allow nginx 80

Bu araç özellikle birden fazla container yönettiğinizde hayat kurtarıcı oluyor.

Docker Network Segmentasyonu ile Güvenlik Katmanı

Güvenliği katmanlı düşünmek gerekiyor. Container’ları izole ağlarda çalıştırmak, hem lateral movement’ı engeller hem de UFW kurallarını daha anlamlı kılar.

# Frontend ve backend için ayrı ağlar oluştur
docker network create frontend-net
docker network create backend-net

# Veritabanı sadece backend ağında
docker run -d 
  --name postgres 
  --network backend-net 
  -e POSTGRES_PASSWORD=güçlüşifre 
  postgres:14

# Uygulama sunucusu her iki ağda
docker run -d 
  --name api 
  --network backend-net 
  myapi:latest

docker network connect frontend-net api

# Nginx sadece frontend ağında ve dışarıya açık
docker run -d 
  --name nginx 
  --network frontend-net 
  -p 80:80 
  -p 443:443 
  nginx:alpine

Bu yapıda postgres container’ı hiçbir şekilde dışarıdan erişilemez. Sadece aynı backend-net ağındaki container’lar ulaşabilir. UFW kurallarınızla bu izolasyonu tamamlayın:

# Sadece 80 ve 443 portlarına dış erişim
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw default deny incoming
sudo ufw enable

Mevcut Güvenlik Durumunu Test Etmek

Yapılandırmanızın gerçekten çalışıp çalışmadığını test etmek kritik önem taşır. Birkaç yöntem kullanabilirsiniz.

Önce aktif iptables kurallarını inceleyin:

# Tüm chain'leri görüntüle
sudo iptables -L -n -v

# Sadece DOCKER chain
sudo iptables -L DOCKER -n -v

# DOCKER-USER chain
sudo iptables -L DOCKER-USER -n -v

# NAT tablosunu kontrol et
sudo iptables -t nat -L -n -v

Dış bir makineden port taraması yapın:

# nmap ile tarama (başka bir sunucudan veya VPN üzerinden)
nmap -sS -p 1-65535 SUNUCU_IP_ADRESI

# Belirli portları kontrol et
nmap -p 80,443,3000,4000,5432 SUNUCU_IP_ADRESI

Sunucu üzerinden dinlenen portları kontrol edin:

# ss ile aktif bağlantılar
sudo ss -tlnp

# Docker container portları
docker ps --format "table {{.Names}}t{{.Ports}}"

Logları İzleme ve Anomali Tespiti

UFW loglarını aktif etmek, şüpheli bağlantı girişimlerini tespit etmenizi sağlar:

# UFW loglama seviyesini ayarla
sudo ufw logging medium

# Logları izle
sudo tail -f /var/log/ufw.log

# Docker ile ilgili DROP edilen paketleri filtrele
sudo grep "BLOCK" /var/log/ufw.log | grep -E "DPT=(3000|4000|5432)"

Düzenli log analizini otomatikleştirmek için basit bir script:

#!/bin/bash
# /usr/local/bin/docker-firewall-check.sh

echo "=== UFW Durumu ==="
sudo ufw status numbered

echo ""
echo "=== DOCKER-USER Chain ==="
sudo iptables -L DOCKER-USER -n -v

echo ""
echo "=== Dışarıya Açık Container Portları ==="
docker ps --format "{{.Names}}: {{.Ports}}" | grep -v "127.0.0.1"

echo ""
echo "=== Son 20 UFW BLOCK Kaydı ==="
sudo grep "BLOCK" /var/log/ufw.log | tail -20

Bu scripti crontab’a ekleyerek günlük rapor alabilirsiniz:

sudo chmod +x /usr/local/bin/docker-firewall-check.sh
echo "0 8 * * * root /usr/local/bin/docker-firewall-check.sh | mail -s 'Firewall Raporu' [email protected]" | sudo tee -a /etc/crontab

Sistem Yeniden Başlatmada Kalıcılık

UFW kuralları yeniden başlatmada kalır ama DOCKER-USER’a eklediğiniz iptables kuralları kaybolabilir. Kalıcılık için iki yaklaşım var.

iptables-persistent yaklaşımı:

sudo apt install iptables-persistent

# Kuralları kaydet
sudo netfilter-persistent save

# Sistem başlangıcında yüklendiğini doğrula
sudo systemctl enable netfilter-persistent
sudo systemctl status netfilter-persistent

Systemd service yaklaşımı:

sudo nano /etc/systemd/system/docker-firewall.service
[Unit]
Description=Docker Firewall Kuralları
After=docker.service
Requires=docker.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/bin/apply-docker-firewall.sh
ExecStop=/usr/local/bin/remove-docker-firewall.sh

[Install]
WantedBy=multi-user.target
sudo systemctl enable docker-firewall
sudo systemctl start docker-firewall

Sık Yapılan Hatalar ve Çözümleri

Hata 1: UFW reload sonrası Docker erişimi kesilmesi

UFW reload yapıldığında DOCKER-USER chain’indeki manuel kurallar temizlenir. Çözüm olarak kuralları UFW before.rules içine taşıyın veya yukarıdaki systemd servisi kullanın.

Hata 2: IPv6 için ayrı kurallar yazılmaması

IPv4 kurallarınız mükemmel olsa bile IPv6 üzerinden erişim açık kalabilir. Docker genellikle IPv4 kullanır ama kontrol etmek gerekir:

# IPv6 için de benzer kurallar
sudo ip6tables -I DOCKER-USER -j DROP
sudo ip6tables -I DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

Hata 3: docker0 interface’ini unutmak

Container’lar arası iletişim docker0 bridge üzerinden geçer. Bu interface’i engellerseniz container’lar birbirleriyle konuşamaz:

# docker0 interface'ine her zaman izin ver
sudo ufw allow in on docker0
sudo ufw allow out on docker0

Sonuç

UFW ve Docker entegrasyonu, varsayılan kurulumda gerçekten problemli bir tablo ortaya koyuyor. Docker’ın iptables’a müdahale etme davranışı, UFW’ye güvenerek kurduğunuz güvenlik duvarını bypass edebiliyor. Ancak doğru yaklaşımla bu iki araç uyumlu çalışabilir.

Özetlemek gerekirse: Container portlarını 127.0.0.1’e bind etmek ilk savunma hattınız olmalı. DOCKER-USER chain’ini aktif kullanmak ikinci katmanı oluşturur. Network segmentasyonu ile container izolasyonu üçüncü katmanı sağlar. Bunların üzerine UFW’nin temel kuralları ve düzenli log analizi eklendiğinde oldukça sağlam bir güvenlik altyapısı elde edersiniz.

Production’da bu yapılandırmayı uygulamadan önce mutlaka test ortamında deneyin ve nmap ile dışarıdan tarama yaparak gerçekten neyin açık neyin kapalı olduğunu doğrulayın. Kağıt üzerinde mükemmel görünen bir güvenlik yapılandırması, test edilmediği sürece güvenilir değildir.

Yorum yapın