UFW wymyślono po to, żeby zaporę dało się skonfigurować trzema poleceniami, i z tym sobie radzi. Problem leży gdzie indziej: ufw status pokazuje zamiary, a nie wynik. Reguła może stać na liście i nie zamykać zupełnie niczego — z trzech różnych powodów, a wszystkie trzy zdarzają się na prawdziwych serwerach regularnie.
Początek, przy którym nie zamykamy się na zewnątrz
Kolejność poleceń ma znaczenie. Najpierw zezwolić na SSH, potem włączać:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw enable
ufw enable przed zezwoleniem na SSH zrywa własną sesję, a dalej zostaje tylko konsola hostingodawcy. Jeśli serwer jest już skonfigurowany, a pewności nie ma — trzymajmy otwarte drugie połączenie: przeżyje nieudaną zmianę.
Patrzeć trzeba na status szczegółowy; krótki ukrywa politykę domyślną:
sudo ufw status verbose
Błąd pierwszy: reguła jest, a port otwarty dla całego świata
Najczęstszy wiersz w cudzych konfiguracjach:
sudo ufw allow 3306
Tak otwiera się MySQL „żeby móc się połączyć z domu” — i otwiera się go całemu internetowi. Poprawne są dwa warianty i oba lepsze:
sudo ufw allow from 203.0.113.25 to any port 3306
albo, co pewniejsze, w ogóle nie wypuszczać usługi na zewnątrz: bind-address = 127.0.0.1 w konfiguracji MySQL, a dostęp z zewnątrz przez tunel SSH. Zapora to druga linia; pierwszą jest to, żeby usługa nie nasłuchiwała na adresie publicznym. Regułę w UFW można przypadkiem skasować, bind-address sam się nie zmieni.
Błąd drugi: IPv6
Reguła w rodzaju ufw allow from 203.0.113.25 dotyczy tylko IPv4. Jeśli serwer ma adres IPv6 — a większość VPS-ów go ma, w dodatku włączony — usługa pozostaje po nim dostępna. Hostingodawca wydał adres, my o nim nie pamiętamy, skaner o nim wie.
Sprawdźmy, czy filtrowanie v6 jest w ogóle włączone (IPV6=yes w /etc/default/ufw) i czy usługa nie nasłuchuje bez potrzeby na :::
ss -tulpn | grep ':::'
Reguły z jawnie wskazanym adresem trzeba zakładać osobno dla obu wersji protokołu.
Błąd trzeci: Docker
Najbardziej irytujący. Docker publikuje porty, dopisując własne reguły do łańcuchów iptables wcześniej niż te, do których pisze UFW. W efekcie kontener uruchomiony z -p 5432:5432 jest dostępny z internetu, nawet jeśli UFW pokazuje Status: active i politykę deny incoming. Zapora nie jest przy tym zepsuta — po prostu nie dochodzi do niej kolej.
Leczy się to nie w UFW, lecz w sposobie publikowania portu:
ports:
- "127.0.0.1:5432:5432"
Przypisanie do adresu lokalnego to najprostsze i najpewniejsze rozwiązanie. Na zewnątrz powinno patrzeć tylko to, co naprawdę obsługuje odwiedzających: zwykle porty 80 i 443 odwrotnego proxy.
Kolejność reguł
UFW stosuje pierwszą pasującą regułę i na tym kończy. Zakaz dodany po zezwoleniu nie zadziała: nie dojdzie do niego kolej. Sprawdźmy numerację i wstawmy we właściwe miejsce:
sudo ufw status numbered
sudo ufw insert 1 deny from 198.51.100.0/24
sudo ufw delete 7
Reguły usuwa się po numerze, ale numery przesuwają się po każdym usunięciu — usuwajmy po jednej i za każdym razem czytajmy listę na nowo.
Sprawdzenie z zewnątrz
Polecenia lokalne pokazują, co jest skonfigurowane. Co wyszło naprawdę, widać tylko z innej maszyny:
nmap -Pn -p- 203.0.113.25
nc -zv 203.0.113.25 3306
Wystarczy dowolny inny serwer albo domowy komputer. To jedyne sprawdzenie, które nie kłamie, i warto je wykonać po każdej większej przebudowie — zwłaszcza po instalacji czegoś, co „samo konfiguruje sieć”: Dockera, paneli zarządzania, VPN-a.
Dzienniki
Domyślnie UFW prawie nic nie zapisuje. Włącza się to tak:
sudo ufw logging low
Wpisy trafiają do /var/log/ufw.log — ale tylko wtedy, gdy w systemie jest rsyslog. W Debianie 12 i Ubuntu 24.04 może go nie być i wtedy wszystko idzie do dziennika systemd:
sudo journalctl -k | grep -i '\[UFW'
Pusty /var/log/ufw.log sam w sobie nic nie znaczy — najpierw zajrzyjmy do dziennika.
Czego oczekiwać od zapory
UFW zamyka to, co nie powinno być dostępne. Nie analizuje treści żądań do tego, co otwarte: port 443 stoi otworem dla wszystkich i co przyjdzie na witrynę, przyjdzie bez przeszkód. To zadanie innych narzędzi — WAF-a na poziomie aplikacji, IDS-a na poziomie ruchu. Zadanie zapory jest skromniejsze i ważniejsze: żeby lista otwartych portów zgadzała się z tym, co o niej myślimy. Jak ta lista wygląda razem z aktywnymi regułami na jednej stronie, pokazuje demonstracja poniżej.