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.