UFW (Uncomplicated Firewall), Linux güvenlik duvarı (firewall) kurallarını okunabilir komutlarla yönetmek için kullanılan bir ön yüzdür. Uzak bir sunucuda kural değiştirirken önce yönetim erişimini açık tutacak SSH kuralını doğrulamak ve olası kilitlenmeye karşı ikinci bir oturum bulundurmak gerekir.
UFW nedir?
UFW, kullanımı kolay bir güvenlik duvarı yönetim aracıdır. Kuralları kendisi uygulamaz; yazdığınız komutları iptables kurallarına çevirir. Güncel Ubuntu sürümlerinde iptables komutu iptables-nft arka ucuyla çalışır ve kuralları çekirdekteki nftables altyapısına yazar; bunu sudo iptables --version çıktısındaki (nf_tables) ibaresinden görebilirsiniz. UFW, Ubuntu’nun varsayılan güvenlik duvarı yapılandırma aracıdır ve başlangıçta devre dışıdır. Debian’da ise sudo apt install ufw ile ayrıca kurulur.
Durum ve etkinleştirme komutları
# Durum
sudo ufw status
# Ayrıntılı durum: varsayılan politikalar ve günlük düzeyi
sudo ufw status verbose
# Numaralı liste (kural silme için)
sudo ufw status numbered
# Etkinleştirme: uzak sunucuda önce aşağıdaki SSH adımlarını izleyin
sudo ufw enable
# Devre dışı bırakma
sudo ufw disable
Önce: uzak sunucuda erişimi kaybetmemek
ufw enable komutu, kuralları yazmadan önce çalıştırıldığında yönetim erişimini
kesebilir. Uzak bir sunucuda bu, konsol erişimi olmadan geri dönülemeyen bir durumdur.
Kural değiştirmeye başlamadan önce aşağıdaki sıra izlenmelidir.
1. Gerçek SSH portunu doğrulayın. ufw allow ssh komutu servis adını
/etc/services üzerinden çözer ve 22 numaralı portu açar. SSH başka bir porta
taşınmışsa bu kural işe yaramaz ve ufw enable bağlantıyı keser. Dinlenen portu ve
şu an bağlı olduğunuz oturumun sunucu tarafındaki portunu kontrol edin:
# sshd'nin dinlediği portlar
sudo ss -tlnp | grep sshd
# Mevcut SSH oturumunun sunucu portu (SSH_CONNECTION'ın dördüncü alanı)
echo "$SSH_CONNECTION" | awk '{print $4}'
Ubuntu 22.10 ve sonrasında SSH soket etkinleştirmeyle başlar ve portu systemd
tutar; ilk komut bu yüzden boş dönebilir. Böyle bir durumda
systemctl status ssh.socket çıktısındaki Listen: satırına bakın. İkinci komut her
durumda o an bağlı olduğunuz portu gösterir.
2. Kuralı UFW’yi etkinleştirmeden önce ekleyin. Çıkan portu açıkça yazın:
sudo ufw allow 22/tcp
3. Kuralları uygulamadan önce sonucu görün. --dry-run, oluşacak kural setini
hiçbir değişiklik yapmadan yazdırır:
sudo ufw --dry-run enable
4. İkinci bir oturum açık tutun. Etkinleştirme sırasında mevcut SSH oturumunuzu
kapatmayın. UFW enable komutu bu nedenle onay sorar.
5. Etkinleştirin ve üçüncü bir oturumla doğrulayın. Yeni bir bağlantı kurulabildiğini görmeden mevcut oturumları kapatmayın:
sudo ufw enable
sudo ufw status verbose
Sağlayıcınızın web konsolu (KVM, seri konsol veya kurtarma modu) varsa bunu önceden denemiş olmak, kilitlenme durumunda tek geri dönüş yoludur.
Temel UFW komutları
1. Port yönetimi
# Belirli bir portu aç
sudo ufw allow 22 # SSH
sudo ufw allow 80 # HTTP
sudo ufw allow 443 # HTTPS
# Port aralığı (aralıklarda protokol belirtmek gerekir)
sudo ufw allow 6000:6007/tcp
# Protokol belirterek
sudo ufw allow 22/tcp
sudo ufw allow 53/udp
# Portu kapat
sudo ufw deny 23 # Telnet'i engelle
Kaynak belirtilmeden yazılan bir allow kuralı portu tüm adreslere açar. Bu,
herkese açık olması gereken web servisleri için doğrudur; geliştirme sunucuları,
veritabanları ve yönetim arayüzleri için değildir. Bu tür servisler için bir sonraki
başlıktaki kaynak kısıtlı biçimi kullanın.
2. Servis adları ve uygulama profilleri
# Servis adıyla (/etc/services üzerinden porta çözülür)
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
# Uygulama profillerini listele (/etc/ufw/applications.d)
sudo ufw app list
# Uygulama profiliyle
sudo ufw allow 'Apache Full'
sudo ufw allow 'Nginx Full'
sudo ufw allow 'OpenSSH'
Servis adları /etc/services üzerinden sabit port numaralarına çözülür: ssh her
zaman 22, http 80, https 443 anlamına gelir. Servisi standart dışı bir portta
çalıştırıyorsanız servis adı yerine port numarasını yazın. Uygulama profilleri ise
ilgili paketlerin /etc/ufw/applications.d altına bıraktığı dosyalardır; Apache Full
ya da Nginx Full yalnız o web sunucusu kuruluysa listede görünür.
ufw limit komutu da burada anılmalıdır, çünkü davranışı adından anlaşılmıyor:
sudo ufw limit 22/tcp
Bu kural, aynı kaynak IP adresinden 30 saniye içinde altıncı ve sonraki bağlantı girişimlerini reddeder. Kaba kuvvet denemelerini yavaşlatır, fakat tek başına bir kimlik doğrulama önlemi değildir; anahtar tabanlı SSH girişi ve parola girişinin kapatılmasıyla birlikte düşünülmelidir. Anahtar kurulumu ve SSH üzerinden dosya aktarımı için scp yazısına bakabilirsiniz.
3. IP adresine göre kurallar
# Belirli bir IP'den gelen bağlantılara izin ver
sudo ufw allow from 192.168.1.100
# IP aralığı
sudo ufw allow from 192.168.1.0/24
# Belirli bir IP'yi engelle
sudo ufw deny from 192.168.1.50
# Belirli bir IP'den belirli bir porta
sudo ufw allow from 192.168.1.100 to any port 22 proto tcp
Geliştirme sunucusu için örnek kural seti
Komutları tek bir betiğe koyup topluca çalıştırmak yerine sırayla uygulamak ve her
adımda ufw status ile sonucu görmek daha güvenlidir; kural hatası ancak erişim
kaybedilmeden fark edilirse ucuza kapanır.
ufw reset mevcut tüm kuralları siler ve güvenlik duvarını devre dışı bırakır.
Çalışan bir sunucuda kullanmadan önce mevcut durumu kaydedin:
sudo ufw status numbered > ~/ufw-kurallar-yedek.txt
Temel politika ve herkese açık servisler:
# Varsayılan politika: gelen kapalı, giden açık
sudo ufw default deny incoming
sudo ufw default allow outgoing
# SSH: gerçek portu yukarıdaki bölümde doğruladıktan sonra
sudo ufw limit 22/tcp
# Herkese açık web servisleri
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
Geliştirme sunucuları ve veritabanları kaynak kısıtı olmadan açılmamalıdır.
React, Flask ve Django’nun geliştirme sunucuları üretim için tasarlanmamıştır.
Flask’ın hata ayıklama konsolu tarayıcı üzerinden kod çalıştırmaya izin verir.
Django DEBUG=True iken hata sayfasında ayarları ve traceback’teki yerel
değişkenleri, 404 sayfasında URL yapılandırmasını gösterir. SECRET_KEY ile adında
PASS, KEY veya TOKEN gibi ifadeler geçen ayarları maskeler; yerel değişkenlerdeki
sırlar, dosya yolları ve yapılandırma ayrıntıları yine de sızabilir.
İnternete açık bir port bu yüzeyleri doğrudan erişilebilir kılar.
# Geliştirme sunucuları: yalnızca yerel ağdan
sudo ufw allow from 192.168.1.0/24 to any port 3000 proto tcp
sudo ufw allow from 192.168.1.0/24 to any port 5000 proto tcp
sudo ufw allow from 192.168.1.0/24 to any port 8000 proto tcp
# Veritabanları: yalnızca bilinen kaynaklardan
sudo ufw allow from 192.168.1.0/24 to any port 3306 proto tcp
sudo ufw allow from 192.168.1.0/24 to any port 5432 proto tcp
sudo ufw allow from 192.168.1.0/24 to any port 27017 proto tcp
Daha güvenli bir yaklaşım, geliştirme sunucusunu yalnızca geri döngü arayüzüne bağlamak ve uzaktan erişimi SSH tüneliyle sağlamaktır. Bu durumda güvenlik duvarında hiçbir ek port açmanız gerekmez:
# Sunucuda: yalnızca 127.0.0.1 dinlenir
python manage.py runserver 127.0.0.1:8000
# İstemcide: tünel açılır
ssh -L 8000:127.0.0.1:8000 kullanici@sunucu
Kural setini uyguladıktan sonra etkin politikayı ve gerçek dinleme durumunu ayrı ayrı doğrulayın:
sudo ufw status verbose
sudo ss -tlnp
Docker kullanıyorsanız UFW kurallarınız geçersiz olabilir
Bu sorun, UFW kullanılan Docker sunucularında kolayca gözden kaçar.
Docker, -p ile yayımlanan portlara gelen trafiği iptables’ın nat tablosunda,
PREROUTING aşamasında konteyner adresine yönlendirir (DNAT). Bu trafik ana
makinenin INPUT zinciri yerine FORWARD zincirinden geçer; UFW’nin allow ve
deny kuralları ise INPUT ve OUTPUT tarafındadır. Docker’ın kendi kuralları
DOCKER ve ilişkili zincirlerde durur. Sonuç olarak bir konteyneri -p ile
yayımladığınızda port, UFW’de ne yazdığından bağımsız olarak dışarıdan erişilebilir
hâle gelir:
docker run -d -p 3306:3306 mysql # port dışarıya açıldı
sudo ufw deny 3306 # bu kuralın etkisi yok
sudo ufw status # kural yine de "aktif" görünür
Pratik çözüm, portu yalnızca geri döngü arayüzüne yayımlamaktır:
docker run -d -p 127.0.0.1:3306:3306 mysql
Konteyner portlarının gerçekten dışarıya açılması gerekiyorsa filtreleme
DOCKER-USER zincirine yazılmalıdır. Docker bu zinciri kullanıcı kuralları için boş
bırakır ve kendi kurallarından önce işler; UFW bu zinciri yönetmez. Zincirdeki
kuralları görmek için:
sudo iptables -L DOCKER-USER -n -v
Docker 29.0 ile deneysel olarak gelen nftables arka ucunda
("firewall-backend": "nftables") DOCKER-USER zinciri yoktur. Bu arka uca geçerseniz kurallarınızı,
Docker’ın belgesinde anlatıldığı gibi ayrı bir nftables tablosuna taşımanız gerekir.
Sıkı politika: giden trafiği de kısıtlamak
Giden trafiği varsayılan olarak kapatmak, ele geçirilmiş bir sistemden veri sızdırılmasını ve zararlı yazılımın dışarıya bağlanmasını zorlaştırır. Bedeli, sunucunun kurduğu her dış bağlantının açıkça izinlendirilmesi gerekmesidir.
Bu politikaya geçmeden önce hangi işlerin kesileceğini bilmek gerekir. Kesilen
bağlantılar çoğunlukla zaman aşımı olarak görünür ve sebebinin güvenlik duvarı olduğu
hemen anlaşılmaz. Bu yüzden önce sunucunun kurduğu dış bağlantıları bir süre
gözlemleyin. ss, kurulu TCP bağlantılarını ve onları açan süreçleri gösterir;
yedekleme ve güncelleme gibi zamanlanmış işlerin çalıştığı saatler dahil birkaç kez
alınan çıktı bir envanter oluşturur:
sudo ss -tnp state established
Karşı taraf (Peer Address:Port) portu 443, 22 veya 587 gibi bilinen bir servis
portu olan satırlar genellikle giden bağlantılardır. Envanter hazır olunca önce izin
kurallarını ekleyin, varsayılan politikayı en son değiştirin:
sudo ufw allow out 53 # DNS
sudo ufw allow out 80/tcp # HTTP
sudo ufw allow out 443/tcp # HTTPS
sudo ufw allow out 123/udp # NTP
# İzinler eklendikten sonra
sudo ufw default deny outgoing
Yukarıdaki izinler yalnızca ad çözümleme, paket deposu erişimi ve saat eşitlemesi için yeterlidir. Şunlar açıkça izinlendirilmedikçe çalışmaz:
- SSH üzerinden git erişimi (
git clone git@github.com:...), yani giden 22 - standart dışı portlarda çalışan dış API ve veritabanı bağlantıları
- giden e-posta (SMTP 587 veya 465)
- kendi izleme veya günlük toplama sisteminize gönderim
IPv6 kuralları ayrıdır
UFW varsayılan olarak IPv6 kurallarını da üretir; bu davranış /etc/default/ufw
dosyasındaki IPV6=yes satırıyla belirlenir ve ufw status çıktısında (v6) ekli
satırlar olarak görünür.
Dikkat edilmesi gereken nokta, adres tabanlı kuralların adres ailesine özgü olmasıdır. Bir IPv4 adresini engellemek aynı sunucunun IPv6 adresini engellemez:
sudo ufw deny from 203.0.113.50 # yalnızca IPv4
sudo ufw deny from 2001:db8::50 # IPv6 için ayrı kural gerekir
Sunucunuz IPv6 üzerinden erişilebilir durumdaysa port kurallarınızın her iki adres
ailesinde de beklediğiniz gibi göründüğünü sudo ufw status verbose ile doğrulayın.
İzleme
Engellenen bağlantıları görüntüleme
UFW etkinleştirildiğinde günlük kaydı low düzeyinde açıktır: /etc/ufw/ufw.conf
dosyasındaki varsayılan değer LOGLEVEL=low’dur ve sudo ufw status verbose
çıktısında Logging: on (low) olarak görünür. low, politikaya uymadığı için
engellenen paketleri hız sınırıyla kaydeder. medium ve üstü çok daha fazla kayıt
üretir ve yoğun bir sistemde diski hızla doldurabilir. Günlük kapatılmışsa yeniden
açmak için:
sudo ufw logging low
Günlüklerin nereye yazıldığı sistemin yapılandırmasına bağlıdır. Rsyslog kuruluysa
kayıtlar /var/log/ufw.log dosyasına da düşer. Rsyslog’un bulunmadığı minimal veya
konteyner imajlarında kayıtlar yalnızca çekirdek günlüğündedir:
# Rsyslog kuruluysa
sudo grep 'UFW BLOCK' /var/log/ufw.log | tail -20
# Her durumda çalışır
sudo journalctl -k --grep='UFW BLOCK' --since '1 hour ago'
Kaynak adresi ve hedef portu alan numarasına göre değil alan adına göre ayıklamak daha dayanıklıdır; UFW satırlarındaki alan sayısı arayüze ve protokole göre değişir:
# En çok engellenen kaynak adresler
sudo journalctl -k --grep='UFW BLOCK' --since today \
| grep -oE 'SRC=[^ ]+' | sort | uniq -c | sort -nr | head -10
# En çok hedeflenen portlar
sudo journalctl -k --grep='UFW BLOCK' --since today \
| grep -oE 'DPT=[0-9]+' | sort | uniq -c | sort -nr | head -10
Düzenli rapor için systemd timer
Sürekli çalışan bir kabuk döngüsü yerine zamanlanmış bir servis kullanmak daha öngörülebilirdir: zamanlama, günlükleme ve hata durumunda davranış sistem tarafından yönetilir, PID dosyası tutmak gerekmez. Servis birimlerinin yapısı systemd servis yazısında ayrıntılı anlatılıyor.
/usr/local/bin/ufw-rapor betiği:
#!/usr/bin/env bash
set -euo pipefail
esik=100
# journalctl --grep eşleşme bulamazsa sıfır dışı kodla çıkar; || true betiğin
# sakin saatlerde hata vermesini önler. -q "-- No entries --" satırını bastırır.
sayi=$({ journalctl -k -q -o cat --grep='UFW BLOCK' --since '1 hour ago' || true; } | wc -l)
echo "Son bir saatte engellenen bağlantı: ${sayi}"
if (( sayi > esik )); then
echo "UYARI: eşik (${esik}) aşıldı" >&2
exit 1
fi
journalctl --grep, eşleşme bulamadığında grep gibi sıfır dışı bir kodla çıkar.
set -euo pipefail altında bu, engelleme olmayan bir saatte betiğin hata vermesine,
servisin başarısız görünmesine ve yalancı alarma yol açardı. -o cat yalnızca ileti
metnini yazar, -q ise eşleşme yokken basılan -- No entries -- satırının sayıma
girmesini önler.
/etc/systemd/system/ufw-rapor.service:
[Unit]
Description=UFW engelleme raporu
[Service]
Type=oneshot
ExecStart=/usr/local/bin/ufw-rapor
/etc/systemd/system/ufw-rapor.timer:
[Unit]
Description=UFW engelleme raporunu saatlik çalıştır
[Timer]
OnCalendar=hourly
Persistent=true
[Install]
WantedBy=timers.target
Devreye alma ve izleme:
sudo systemctl daemon-reload
sudo systemctl enable --now ufw-rapor.timer
journalctl -u ufw-rapor.service
Betik standart çıktıya yazdığı için ayrı bir günlük dosyası tutmak gerekmez;
çıktıyı journald toplar. Eşik aşıldığında servis başarısız duruma geçer ve bu
durum OnFailure= ile bir bildirim unit’ine bağlanabilir.
Kuralları yedeklemek ve geri yüklemek
UFW kurallarını /etc/ufw/user.rules (IPv4) ve /etc/ufw/user6.rules (IPv6)
dosyalarında tutar. Yedek alırken ikisini birden almak gerekir; yalnızca IPv4
dosyasını geri yüklemek IPv6 kurallarının eski hâlde kalmasına yol açar.
Okunabilir bir anlık görüntü için:
sudo ufw status numbered > ~/ufw-durum-$(date +%F).txt
Bu dosya doğrudan geri yükleme için kullanılamaz, fakat neyin değiştiğini karşılaştırmak ve kuralları elle yeniden kurmak için en güvenilir kayıttır.
Yapılandırmanın tamamını yedeklemek:
sudo tar czf ~/ufw-yedek-$(date +%F).tar.gz -C / etc/ufw etc/default/ufw
Geri yükleme güvenlik duvarını durdurmayı gerektirir. Uzak bir sunucuda bu işlem erişimi kesme riski taşır; öncesinde konsol erişiminizin olduğundan emin olun:
sudo ufw disable
sudo tar xzf ~/ufw-yedek-2026-09-06.tar.gz -C /
sudo ufw enable
sudo ufw status verbose
Dosyaları elle düzenlediyseniz etkinleştirmeden önce oluşacak kural setini
sudo ufw --dry-run enable ile gözden geçirin.
Sık karşılaşılan sorunlar
SSH erişimini kaybettim
Bağlantı koptuktan sonra sunucuya SSH ile ulaşamazsınız; geriye yalnızca sağlayıcınızın web konsolu, seri konsol veya fiziksel erişim kalır. Konsola düştükten sonra:
sudo ufw status numbered # hangi kuralın eksik olduğunu görün
sudo ss -tlnp | grep sshd # SSH'nin gerçek portunu doğrulayın
sudo ufw allow 22/tcp # çıkan portu açın
sudo ufw reload
Bu bölümün asıl değeri sonradan düzeltmekte değil, duruma hiç düşmemektedir.
Yazının başındaki sıra bu yüzden vardır: gerçek portu doğrulayın, kuralı enable
öncesinde ekleyin, ikinci bir oturum açık tutun, üçüncü bir oturumla doğrulayın.
Port erişilemiyor
Sorunun güvenlik duvarında mı uygulamada mı olduğunu ayırmak ilk adımdır. Uygulama
yalnızca 127.0.0.1 üzerinde dinliyorsa hiçbir UFW kuralı onu dışarıya açmaz:
# Hangi süreç hangi adres ve portu dinliyor
sudo ss -tlnp | grep :3000
# Güvenlik duvarı tarafındaki kural
sudo ufw status | grep 3000
ss çıktısındaki yerel adres 127.0.0.1:3000 ise sorun güvenlik duvarında
değildir; uygulamanın 0.0.0.0 veya ilgili arayüz üzerinde dinlemesi gerekir.
Süreci sonlandırmanız gerekiyorsa önce SIGTERM gönderin; SIGKILL ancak yanıt
alınamadığında kullanılır. PID’yi doğrulama ve sinyal sırası
kill ve PID yazısında anlatılıyor:
sudo kill -s TERM <PID>
Kural çakışmaları ve tekrarlar
UFW kuralları sırayla değerlendirir ve eşleşen ilk kuralı uygular. Bu nedenle
geniş bir allow kuralı, altında yer alan daha dar bir deny kuralını etkisiz
bırakabilir. Sıra numaralarını görüntüleyip gereksiz kuralları tek tek silmek,
kural setini sıfırlamaktan güvenlidir:
sudo ufw status numbered
sudo ufw delete 5
Kuralın belirli bir konuma eklenmesi gerekiyorsa:
sudo ufw insert 1 deny from 203.0.113.50
ufw reset yalnızca kural setini baştan kurmaya hazır olduğunuzda ve yedek
aldıktan sonra kullanılmalıdır; komut güvenlik duvarını devre dışı bırakır ve tüm
kuralları siler.
Sonuç
UFW, tek bir sunucunun güvenlik duvarı politikasını okunabilir komutlarla yönetmek için yeterli ve pratiktir: gelen trafiği varsayılan olarak kapatın, yalnız gereken portları mümkünse kaynak kısıtıyla açın, SSH için limit kullanın ve kuralları yedekleyin. Yanlış kural sırası veya eksik envanter yine de erişimi kesebileceği için değişiklikten önce mevcut servisleri ve kaynak ağları çıkarın, değişiklikten sonra etkin politika ile gerçek bağlantıyı ayrı ayrı doğrulayın. Docker’ın yayımladığı portlar veya karmaşık yönlendirme kuralları söz konusuysa UFW tek başına yetmez; DOCKER-USER zinciri ya da doğrudan nftables gerekir.