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.