# nftables: современный firewall для Linux-сервера

Индекс LLMS: [llms.txt](/llms.txt)

---

> [!WARNING]
> Перед работой с nftables убедитесь, что есть физический или console-доступ к серверу. Ошибочная цепочка input может заблокировать SSH и отсечь от машины.

nftables пришёл на смену iptables в ядре Linux начиная с версии 3.13. Если вы всё ещё пишете правила в стиле iptables — пора пересмотреть подход. nftables быстрее, имеет встроенную поддержку IPv4/IPv6 в одном интерфейсе и позволяет работать с ruleset как с целым, а не набирать команды по одной.

## Зачем переходить с iptables

iptables имеет несколько фундаментальных проблем. Каждая таблица (filter, nat, mangle) — отдельный набор правил с собственной семантикой. Нет встроенной поддержки одновременной работы с IPv4 и IPv6 — приходится писать два набора правил. Производительность падает при большом количестве правил из-за линейного поиска.

nftables решает это иначе. Все протоколы работают в единой структуре данных — ruleset. Ядро компилирует правила в эффективные структуры поиска. Атомарная замена ruleset исключает race condition при обновлении правил.

> [!WARNING]
> В дистрибутивах на базе RHEL 8 и новее iptables по умолчанию перенаправляется в nftables. В Ubuntu 22.04 и старше — аналогично. Проверьте `update-alternatives --display iptables`.

## Базовые команды: просмотр и сброс правил

Первая команда, которую стоит запомнить:

```bash
nft list ruleset
```

Вывод показывает все таблицы, цепочки и правила. Без таблиц вывод пуст — это нормально.

Создаём таблицу с именем filter для работы с пакетами:

```bash
nft add table inet filter
```

`inet` означает, что таблица обрабатывает оба протокола. Для IPv4-only — `ip`, для IPv6 — `ip6`.

Добавляем цепочку для входящего трафика:

```bash
nft add chain inet filter input { type filter hook input priority 0 \; policy accept \; }
```

Разберём флаги:

| Флаг | Назначение |
|------|------------|
| `type filter` | Тип цепочки — фильтрация пакетов |
| `hook input` | Точка подключения — входящие пакеты |
| `priority 0` | Порядок обработки относительно других хуков |
| `policy accept` | Дефолтное действие — пропускать всё |

Сброс правил в цепочке:

```bash
nft flush chain inet filter input
```

Удаление всей таблицы:

```bash
nft delete table inet filter
```

## Добавление и удаление правил по handle

Создадим несколько правил и посмотрим их handle:

```bash
nft add rule inet filter input tcp dport 22 accept
nft add rule inet filter input tcp dport 80 accept
nft add rule inet filter input tcp dport 443 accept
nft list ruleset
```

Вывод покажет что-то вроде:

```
table inet filter {
    chain input {
        type filter hook input priority 0; policy accept;
        tcp dport 22 accept
        tcp dport 80 accept
        tcp dport 443 accept
    }
}
```

Handle скрыт в выводе по умолчанию. Для работы с конкретным правилом:

```bash
nft -a list ruleset
```

Добавим `-a` и увидим handle для каждого правила. Теперь можем удалять по номеру:

```bash
nft delete rule inet filter input handle 3
```

> [!TIP]
> Handle меняется при каждом добавлении или удалении правила. Если скрипт модифицирует ruleset, сохраняйте вывод `nft -a list ruleset` в файл для отслеживания.

Добавим правило с приоритетом перед существующими — в начало цепочки:

```bash
nft insert rule inet filter input tcp dport 2222 accept
```

Команда `add` добавляет в конец, `insert` — в начало. Для вставки в конкретную позицию:

```bash
nft add rule inet filter input position 2 tcp dport 8080 accept
```

## Атомарная замена ruleset

Ручное добавление правил по одному создаёт промежуток времени, когда часть правил уже активна, а часть — ещё нет. Для продакшена это неприемлемо.

Решение — записать полный ruleset в файл и загрузить атомарно:

```bash
nft list ruleset > /etc/nftables.conf
```

Файл `/etc/nftables.conf` — стандартное место в большинстве дистрибутивов. Теперь редактируем его:

```
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;
        
        ct state established,related accept
        ct state invalid drop
        
        iif lo accept
        
        tcp dport 22 accept
        tcp dport 80 accept
        tcp dport 443 accept
        
        counter drop
    }
}
```

Загружаем:

```bash
nft -f /etc/nftables.conf
```

> [!NOTE]
> `flush ruleset` перед загрузкой очищает всё. Если нужно добавить правила к существующим — уберите эту строку.

Проверяем без применения:

```bash
nft -c -f /etc/nftables.conf
```

Флаг `-c` выполняет синтаксическую проверку без изменения состояния. Полезно в CI/CD перед деплоем.

Чтобы правила переживали перезагрузку в systemd-дистрибутивах:

```bash
systemctl enable --now nftables
```

`nftables.service` загружает `/etc/nftables.conf` при старте. После правки файла применяйте `systemctl restart nftables`.

## Цепочка forward

Если машина не роутер, `forward` оставляют пустым с политикой drop. Когда включён IP-forwarding, минимум — ответы на установленные соединения и транзит между интерфейсами:

```bash
nft add chain inet filter forward { type filter hook forward priority 0 \; policy drop \; }
nft add rule inet filter forward ct state established,related accept
nft add rule inet filter forward iifname "eth0" oifname "eth1" accept
```

Для отладки можно логировать пакеты перед неявным drop:

```bash
nft add rule inet filter forward log prefix "nft-forward-drop: " level warn
```

Логи появятся в `journalctl -k` или `/var/log/kern.log`.

## Типовые сценарии: блокировка порта и IP

**Блокировка входящего подключения с конкретного IP:**

```bash
nft add rule inet filter input ip saddr 1.2.3.4 drop
```

**Блокировка исходящего на конкретный IP:**

```bash
nft add rule inet filter output ip daddr 5.6.7.8 drop
```

**Блокировка диапазона IP (CIDR):**

```bash
nft add rule inet filter input ip saddr 10.0.0.0/8 drop
```

**Блокировка порта для всех:**

```bash
nft add rule inet filter input tcp dport 25 drop
```

**Разрешить порт только для конкретной подсети:**

```bash
nft add rule inet filter input ip saddr 192.168.1.0/24 tcp dport 5432 accept
```

**Логирование отброшенных пакетов:**

```bash
nft add rule inet filter input counter drop
```

Счётчики видны в выводе `nft list ruleset` — показывают количество пакетов и байт.

## NAT через маскарадинг

Для выхода локальной сети в интернет через один IP-адрес:

```bash
nft add table ip nat
nft add chain ip nat postrouting { type nat hook postrouting priority 100 \; }
nft add rule ip nat postrouting ip saddr 192.168.0.0/24 masquerade
```

Маскарадинг автоматически подставляет внешний IP интерфейса. Для NAT с пробросом портов:

```bash
nft add chain ip nat prerouting { type nat hook prerouting priority -100 \; }
nft add rule ip nat prerouting tcp dport 8080 dnat to 192.168.0.100:80
```

> [!WARNING]
> NAT в nftables работает только для IPv4. Для IPv6 используйте stateless NAT66 или адресацию на уровне маршрутизации.

## Режим совместимости iptables-nft

В некоторых дистрибутивах iptables остаётся «работающей» за счёт трансляции в nftables:

```bash
update-alternatives --set iptables /usr/sbin/iptables-nft
update-alternatives --set ip6tables /usr/sbin/ip6tables-nft
```

Проблема в том, что это два разных мира правил. iptables-nft транслирует команды в nftables, но обратная совместимость не работает. Правила, созданные через iptables, не увидите в `nft list ruleset` напрямую.

> [!WARNING]
> Не используйте одновременно iptables и nftables. Результат непредсказуем. Либо полностью переходите на nftables, либо остаётесь на iptables. Проверьте текущий режим: `iptables -V` покажет, используется iptables-legacy или iptables-nft.

Для миграции с iptables есть утилита `iptables-translate`:

```bash
iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT
```

Вывод: `nft add rule ip filter input tcp dport 22 accept`. Ручная проверка вывода обязательна — автоматическая трансляция не идеальна.

nftables — это не будущее, а настоящее. Если вы администрируете Linux-серверы, потратьте вечер на миграцию. Файл `/etc/nftables.conf` с полным ruleset — это ваш бэкап и деплой в одном флаконе.
