1. Гайды
  2. Сервер с нуля

nftables: минимальный файрвол для VPS

Закрыть всё лишнее, оставить SSH и сервисы, притормозить перебор паролей. Без ufw, без iptables-legacy и без блокировки самого себя.

Актуально для nftables 1.0+, Debian 12 / Ubuntu 24.04Обновлено 24.09.2026 4 мин чтения

Голый VPS в интернете начинают сканировать через минуты после запуска. Файрвол не делает сервер неуязвимым, но убирает очевидное: торчащие наружу базы, панели и отладочные порты, про которые ты забыл.

Сначала страховка

Главная ошибка с файрволом — применить правила и потерять SSH. Поэтому перед применением ставим таймер, который через две минуты всё откатит. nohup нужен, чтобы таймер не умер вместе с сессией: при обрыве SSH shell рассылает своим фоновым задачам SIGHUP.

nohup sh -c 'sleep 120 && nft flush ruleset' >/dev/null 2>&1 &

Применил правила, зашёл новым SSH-соединением, всё работает — убиваем таймер из той же сессии:

kill %1

Не зашёл — ждёшь две минуты, правила слетают сами.

Конфиг

/etc/nftables.confbash
#!/usr/sbin/nft -f
flush ruleset

table inet filter {
  # счётчик частоты новых SSH-подключений на каждый адрес источника
  set ssh_flood4 { type ipv4_addr; flags dynamic; timeout 10m; size 65536; }
  set ssh_flood6 { type ipv6_addr; flags dynamic; timeout 10m; size 65536; }

  chain input {
    type filter hook input priority filter; policy drop;

    ct state established,related accept
    ct state invalid drop
    iif "lo" accept

    # ICMP нужен для MTU и диагностики, но без флуда
    ip protocol icmp icmp type { echo-request, destination-unreachable, time-exceeded } limit rate 20/second accept
    # без ICMPv6 IPv6 просто не работает: соседи, MTU, SLAAC.
    # meta l4proto, а не ip6 nexthdr: MLD приходит с hop-by-hop заголовком,
    # и nexthdr там не icmpv6
    meta l4proto ipv6-icmp accept

    # SSH с ограничением частоты новых подключений
    tcp dport 22 ct state new add @ssh_flood4 { ip saddr limit rate over 10/minute } drop
    tcp dport 22 ct state new add @ssh_flood6 { ip6 saddr limit rate over 10/minute } drop
    tcp dport 22 accept

    # веб и прокси
    tcp dport { 80, 443 } accept
    udp dport 443 accept   # HTTP/3, Hysteria2

    counter comment "отброшено политикой"
  }

  chain forward {
    type filter hook forward priority filter; policy drop;
  }

  chain output {
    type filter hook output priority filter; policy accept;
  }
}

Проверить синтаксис, ничего не применяя:

nft -c -f /etc/nftables.conf

Применить и включить при загрузке:

nft -f /etc/nftables.conf && systemctl enable nftables

Если на сервере Docker

Грабли

flush ruleset сносит все таблицы, включая правила Docker (с iptables-nft они живут в тех же nftables). После применения конфига сделай systemctl restart docker, чтобы он создал их заново, иначе контейнеры потеряют сеть. И помни: трафик на порты, опубликованные через ports: в compose, Docker перенаправляет в таблице nat ещё до цепочки input, так что она его не видит.

Чтобы база или панель в контейнере не торчала в интернет, публикуй её только на localhost:

ports:
  - "127.0.0.1:3000:3000"

Цепочка forward с policy drop выше подходит для сервера без Docker и без маршрутизации. С Docker её нужно удалить (или поставить policy accept), а не просто оставить пустой. В nftables accept в одной базовой цепочке не финальный: пакет проходит и остальные базовые цепочки на том же хуке, и drop в твоей таблице выбросит трафик контейнеров, даже если Docker его разрешил.

Посмотреть, что происходит

nft list ruleset          # что сейчас загружено
nft list set inet filter ssh_flood4   # адреса, которые недавно открывали SSH, с их лимитерами

Счётчик в конце цепочки input покажет, сколько мусора отбрасывается. Обычно цифра впечатляет уже через час.

Дальше

  • Для защиты SSH одного файрвола мало. Ключи вместо паролей, отключённый root по паролю и прочее — в гайде SSH без боли.
  • Hashlimit, ipset, XDP и анти-DDoS-схемы, которые реально использует сообщество: Firewall и анти-DDoS.
  • Проверить, какие порты видит мир: досье своего IP, карточка «Открытые порты и сервисы». Это данные Shodan InternetDB, они обновляются раз в неделю, так что свежие изменения там появятся не сразу.

Источники