Короткий ответ
На Ubuntu штатный путь — UFW (обёртка над nftables) либо один ручной nftables.service, не оба вперемешку. Цель: default deny incoming, allow outgoing (пока нет модели egress), явный allow SSH для admin с LAN/jump 10.0.20.0/24, затем точечные порты приложений. Включайте ufw enable только когда вторая сессия SSH уже открыта и правило OpenSSH видно в numbered. Не используйте ufw disable / nft flush / iptables -F как рабочее состояние.
Хост host.example (10.0.20.10).
Симптомы и как отличить
sudo ufw status→inactive;- с интернета открывается
:5432,:6379,:2375; nft list rulesetпустой илиhook input policy acceptбез ограничений;- «защищает облако», при этом SG
0.0.0.0/0.
| Картина | Не «FW не настроен» | Куда |
|---|---|---|
| UFW active, сервис недоступен | лишнее deny / не тот NIC | чинить правило, не disable |
| Порты слушают localhost | ок | лишние порты если 0.0.0.0 |
| Fail2Ban не банит | jail | Fail2Ban |
| Timeout SSH | вы уже отрезали себя | консоль, не новый enable вслепую |
Возможные причины
- Образ cloud с пустым UFW «чтобы не мешал».
- Кто-то сделал
ufw disableиз-за одного BLOCK и не вернул. - Включён
iptables-legacyв голове админа, на диске nft, правила «не те». nftables.servicemasked, UFW тоже off.- Docker публикует порты в обход ожиданий: DOCKER-USER, не только ufw-user.
- IPv6
IPV6=noв UFW при живом AAAA — дыра по v6.
Диагностика
sudo ufw status verbose
sudo ufw status numbered
systemctl is-enabled ufw nftables 2>/dev/null
sudo nft list ruleset | head -n 80
ss -tulnp
ip -br link; ip -br addrСверьте слушателей с матрицей доступов. Скан с jump-хоста (не с самого сервера):
# с admin-станции в LAN 10.0.20.0/24
nc -vz 10.0.20.10 22
nc -vz 10.0.20.10 5432Облако: отдельно выгрузка SG. Гостевой UFW active при SG any-any = периметр открыт.
Docker:
docker ps --format '{{.Names}} {{.Ports}}' 2>/dev/null
sudo nft list table ip filter 2>/dev/null | grep -A20 DOCKER || trueПроверка IPv6:
cat /etc/default/ufw | grep -E '^IPV6='
sudo ufw status verbose | head
ip -6 addrРешение
Выберите один движок.
Сценарий A. UFW (рекомендуемый на Ubuntu)
Порядок, консоль открыта:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from 10.0.20.0/24 to any port 22 proto tcp comment 'ssh-admin'
sudo ufw show addedЕсли нужен SSH ещё с одного /32 jump:
sudo ufw allow from 10.0.20.10 to any port 22 proto tcp comment 'self-or-jump'10.0.20.10 здесь сам хост — для allow с LAN используйте адрес клиента, не путайте. Jump-хост подставьте свой.
Приложение, которое должно быть с LAN:
sudo ufw allow from 10.0.20.0/24 to any port 5432 proto tcp comment 'postgres-lan'Не allow 5432/tcp в Anywhere, если база не публичная.
Включение:
sudo ufw --force enable
sudo ufw status numbered
sudo systemctl enable ufw--force не спрашивает tty. Без allow SSH не используйте.
Проверка новой SSH-сессии до закрытия старой. Дальше — матрица портов.
IPv6: в /etc/default/ufw IPV6=yes, правила dual-stack. Не отключайте IPv6 в sysctl «вместо firewall».
Сценарий B. Чистый nftables без UFW
UFW должен быть inactive, не два фильтра.
sudo ufw status
sudo systemctl disable --now ufw
sudo apt-get install -y nftables
sudo systemctl enable nftablesБазовый /etc/nftables.conf (идея; адаптируйте NIC ens3):
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif lo accept
ip saddr 10.0.20.0/24 tcp dport 22 accept
icmp type echo-request accept
icmpv6 type { echo-request, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
}
chain forward { type filter hook forward priority 0; policy drop; }
chain output { type filter hook output priority 0; policy accept; }
}sudo nft -c -f /etc/nftables.conf
sudo systemctl restart nftablesflush внутри загружаемого файла при nft -f — штатно для этого файла, это не то же самое, что интерактивный flush на живой сессии без готового allow. Сначала -c синтаксис, консоль открыта.
Не ставьте policy accept на input «на время отладки» и не уходите с работы.
Сценарий C. Docker публикует минуя UFW
Правило в DOCKER-USER / документация Docker+UFW. Не публикуйте -p 0.0.0.0:3306:3306. Слушайте 127.0.0.1 или внутреннюю сеть. См. лишние порты и docker.sock.
Сценарий D. Fail2Ban рядом
Один datapath: при UFW — banaction = ufw. Не выключайте UFW, чтобы Fail2Ban «заработал».
Как проверить, что проблема устранена
sudo ufw status verbose
# или
sudo nft list chain inet filter input
ss -tulnpОжидание: incoming default deny; 22 разрешён с admin-сети; лишние порты с «интернет- hop» не коннектятся (nc -vz timeout). SSH admin@10.0.20.10 жив. systemctl is-enabled ufw или nftables — enabled. Firewall active.
Скан с внешней точки должен совпасть с матрицей, не с ss на localhost.
Если не помогло
- После enable нет SSH: консоль,
ufw status numbered, добавьте allow, не disable навсегда. - Правила есть, трафик не режется: смотрите облако, второй интерфейс, IPv6, Docker.
ufw denyсервиса, который слушает127.0.0.1— UFW ни при чём, это не inbound WAN.- Конфликт UFW+nftables.service: оставьте один.
- AppArmor DENIED на сервис — не firewall; AppArmor, не
ufw disable.
Профилактика
- Cloud-init/ansible: default deny + SSH CIDR в образе.
- Алерт:
ufw statusinactive. - Любой новый listen — тикет и правило с source, не Anywhere.
- Регулярный baseline.
- Не держать «временный»
allow 22 from anyмесяцами.
FAQ
UFW или nft напрямую?
На Ubuntu Server UFW достаточно и проще не сломать. nft — если уже есть сложные таблицы и вы ими владеете. Не оба.
Нужен ли deny outgoing?
Для high-security — да, с allow DNS/NTP/HTTP(S) к репозиториям. На обычном app-сервере allow outgoing приемлемо. Не копируйте deny outgoing без DNS: сломаете apt и обновления.
iptables -L пустой, ufw active
Нормально: backend nft. Смотрите nft list / ufw status, не только iptables-legacy.
Можно ли policy drop на forward и сломать VM-роутер?
На хосте-роутере/гипервизоре forward не drop вслепую. Эта статья про сервер приложений, не про шлюз. Для шлюза пишите явный forward.
ufw reset
Сносит правила. Только с консолью и готовым списком allow. Не как «ремонт».