Product SiteDocumentation Site

14.2. Сетевой экран или Фильтрация пакетов

Брандмауэр является фильтрующим сетевым шлюзом и влияет только на пакеты, которые должны пройти через него. Таким образом, безопасность может быть эффективной только когда путь через брандмауэр является единственным для этих пакетов.
Ядро Linux содержит брандмауэрnetfilter. Управлять им можно из пространства пользователя с помощью команд iptables, ip6tables, arptables и ebtables.
Тем не менее, команды NetFilter Iptables заменяются nftables, что позволяет избежать многих его проблем. Его дизайн включает в себя меньшее дублирование кода, и им можно управлять только с помощью команды nft. С релиза Debian Buster, фреймворк nftables используется по умолчанию. Команды, упомянутые ранее, предоставляются версиями, которые по умолчанию используют nftables API ядра. Если требуются «классические» команды, соответствующие двоичные файлы можно настроить с помощью update-alternatives.
Чтобы включить брандмауэр по умолчанию в Debian выполните:
# apt install -y nftables
Reading package lists... Done
...
# systemctl enable nftables.service
Created symlink /etc/systemd/system/sysinit.target.wants/nftables.service → /lib/systemd/system/nftables.service.

14.2.1. Поведение nftables

Поскольку ядро обрабатывает сетевой пакет, оно делает паузу и позволяет нам проверить пакет и решить, что делать с этим пакетом. Например, мы можем захотеть сбросить или отбросить определённые входящие пакеты, модифицировать пакеты различными способами, блокировать определённые исходящие пакеты для управления вредоносными программами или перенаправить некоторые пакеты на как можно более ранней стадии, чтобы сбриджевать сетевые интерфейсы или распределить нагрузку от входящих пакетов между системами.
Хорошее понимание уровней 3, 4 и 5 модели OSI (Open Systems Interconnection) имеет важное значение для получения максимума от сетевого фильтра.
Брандмауэр сконфигурирован с таблицами, которые содержат правила, содержащиеся в цепочках. В отличие от iptables, nftables не имеет таблицы по умолчанию. Пользователь решает, какие и сколько таблиц создавать. Каждая таблица должна иметь только одну из следующих назначенных пяти семей: ip, ip6, inet, arp и bridge. ip используется, если семья не указана.
Существует два типа цепочек: базовые цепочки и обычные цепочки. Базовая цепочка является точкой входа для пакетов из сетевого стека. Базовые цепочки зарегистрированы в хуках Netfilter, например, они видят пакеты, проходящие через TCP/IP-стек. С другой стороны, обычная цепочка не прикреплена к какому-либо хуку, поэтому она не видит никакого трафика. Но может быть использована как цель для прыжка чтобы лучше организовать правила.
Правила состоят из операторов, которые включают в себя выражения, которые должны совпасть, после этого располагается решающий оператор, например accept, drop, queue, continue, return, jump chain and goto chain.

14.2.2. Движение от iptables до nftables

Команды iptables-translate и ip6tables-translate могут использоваться для перевода старых команд iptables в новый синтаксис nftables. Целые наборы правил также могут быть переведены, в этом случае мы мигрируем правила, настроенные на одном компьютере, с установленным Docker:
# iptables-save > iptables-ruleset.txt
# iptables-restore-translate -f iptables-ruleset.txt

# Translated by iptables-restore-translate v1.8.7 on Wed Mar 16 22:06:32 2022
add table ip filter
add chain ip filter INPUT { type filter hook input priority 0; policy accept; }
add chain ip filter FORWARD { type filter hook forward priority 0; policy drop; }
add chain ip filter OUTPUT { type filter hook output priority 0; policy accept; }
add chain ip filter DOCKER
add chain ip filter DOCKER-ISOLATION-STAGE-1
add chain ip filter DOCKER-ISOLATION-STAGE-2
add chain ip filter DOCKER-USER
add rule ip filter FORWARD counter jump DOCKER-USER
add rule ip filter FORWARD counter jump DOCKER-ISOLATION-STAGE-1
add rule ip filter FORWARD oifname "docker0" ct state related,established counter accept
add rule ip filter FORWARD oifname "docker0" counter jump DOCKER
add rule ip filter FORWARD iifname "docker0" oifname != "docker0" counter accept
add rule ip filter FORWARD iifname "docker0" oifname "docker0" counter accept
add rule ip filter DOCKER-ISOLATION-STAGE-1 iifname "docker0" oifname != "docker0" counter jump DOCKER-ISOLATION-STAGE-2
add rule ip filter DOCKER-ISOLATION-STAGE-1 counter return
add rule ip filter DOCKER-ISOLATION-STAGE-2 oifname "docker0" counter drop
add rule ip filter DOCKER-ISOLATION-STAGE-2 counter return
add rule ip filter DOCKER-USER counter return
add table ip nat
add chain ip nat PREROUTING { type nat hook prerouting priority -100; policy accept; }
add chain ip nat INPUT { type nat hook input priority 100; policy accept; }
add chain ip nat OUTPUT { type nat hook output priority -100; policy accept; }
add chain ip nat POSTROUTING { type nat hook postrouting priority 100; policy accept; }
add chain ip nat DOCKER
add rule ip nat PREROUTING fib daddr type local counter jump DOCKER
add rule ip nat OUTPUT ip daddr != 127.0.0.0/8 fib daddr type local counter jump DOCKER
add rule ip nat POSTROUTING oifname != "docker0" ip saddr 172.17.0.0/16 counter masquerade
add rule ip nat DOCKER iifname "docker0" counter return
# Completed on Wed Mar 16 22:06:32 2022
# iptables-restore-translate -f iptables-ruleset.txt > ruleset.nft
# nft -f ruleset.nft
# nft list ruleset
table inet filter {
	chain input {
		type filter hook input priority filter; policy accept;
	}

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

	chain output {
		type filter hook output priority filter; policy accept;
	}
}
table ip nat {
	chain DOCKER {
		iifname "docker0" counter packets 0 bytes 0 return
		iifname "docker0" counter packets 0 bytes 0 return
	}

	chain POSTROUTING {
		type nat hook postrouting priority srcnat; policy accept;
		oifname != "docker0" ip saddr 172.17.0.0/16 counter packets 0 bytes 0 masquerade
		oifname != "docker0" ip saddr 172.17.0.0/16 counter packets 0 bytes 0 masquerade
	}

	chain PREROUTING {
		type nat hook prerouting priority dstnat; policy accept;
		fib daddr type local counter packets 1 bytes 60 jump DOCKER
		fib daddr type local counter packets 0 bytes 0 jump DOCKER
	}

	chain OUTPUT {
		type nat hook output priority -100; policy accept;
		ip daddr != 127.0.0.0/8 fib daddr type local counter packets 0 bytes 0 jump DOCKER
		ip daddr != 127.0.0.0/8 fib daddr type local counter packets 0 bytes 0 jump DOCKER
	}

	chain INPUT {
		type nat hook input priority 100; policy accept;
	}
}
table ip filter {
	chain DOCKER {
	}

	chain DOCKER-ISOLATION-STAGE-1 {
		iifname "docker0" oifname != "docker0" counter packets 0 bytes 0 jump DOCKER-ISOLATION-STAGE-2
		counter packets 0 bytes 0 return
		iifname "docker0" oifname != "docker0" counter packets 0 bytes 0 jump DOCKER-ISOLATION-STAGE-2
		counter packets 0 bytes 0 return
	}

	chain DOCKER-ISOLATION-STAGE-2 {
		oifname "docker0" counter packets 0 bytes 0 drop
		counter packets 0 bytes 0 return
		oifname "docker0" counter packets 0 bytes 0 drop
		counter packets 0 bytes 0 return
	}

	chain FORWARD {
		type filter hook forward priority filter; policy drop;
		counter packets 0 bytes 0 jump DOCKER-USER
		counter packets 0 bytes 0 jump DOCKER-ISOLATION-STAGE-1
		oifname "docker0" ct state related,established counter packets 0 bytes 0 accept
		oifname "docker0" counter packets 0 bytes 0 jump DOCKER
		iifname "docker0" oifname != "docker0" counter packets 0 bytes 0 accept
		iifname "docker0" oifname "docker0" counter packets 0 bytes 0 accept
		counter packets 0 bytes 0 jump DOCKER-USER
		counter packets 0 bytes 0 jump DOCKER-ISOLATION-STAGE-1
		oifname "docker0" ct state established,related counter packets 0 bytes 0 accept
		oifname "docker0" counter packets 0 bytes 0 jump DOCKER
		iifname "docker0" oifname != "docker0" counter packets 0 bytes 0 accept
		iifname "docker0" oifname "docker0" counter packets 0 bytes 0 accept
	}

	chain DOCKER-USER {
		counter packets 0 bytes 0 return
		counter packets 0 bytes 0 return
	}

	chain INPUT {
		type filter hook input priority filter; policy accept;
	}

	chain OUTPUT {
		type filter hook output priority filter; policy accept;
	}
}
Инструменты iptables-nft, ip6tables-nft, arptables-nft, ebtables-nft - это версии iptables, которые используют nftables API, поэтому пользователи могут продолжать использовать старый синтаксис iptables, но это не рекомендуется; эти инструменты должны использоваться только для обратной совместимости.

14.2.3. Syntax of nft

Команды nft позволяют манипулировать таблицами, цепочками и правилами. Опция table поддерживает множество операций: add, create, delete, list и flush. nft add table ip6 mangle добавил новую таблицу из семейства ip6.
Чтобы вставить новую базовую цепочку в таблицу filter, вы можете выполнить следующую команду (заметьте, что точка с запятой экранируется символом backslash при использовании Bash):
# nft add chain filter input { type filter hook input priority 0 \; }
Правила обычно добавляются со следующим синтаксисом: nft add rule [family] table chain handle handle statement.
insert похож на команду add, но данное правило присоединяется к началу цепочки или перед правилом с данным хэндлом вместо конца или после этого правила. Например, следующая команда вставляет правило перед правилом с номером обработчика 8:
# nft insert rule filter output position 8 ip daddr 127.0.0.8 drop
Выполняемые команды nft не вносят постоянных изменений в конфигурацию, поэтому они теряются, если их не сохранить. Правила брандмауэра расположены в /etc/nftables.conf. Простой способ сохранить текущую конфигурацию брандмауэра на постоянной основе - выполнить nft list ruleset > /etc/nftables.conf как root.
nft позволяет гораздо больше операций, смотрите страницу его руководства nft(8) для получения дополнительной информации.

14.2.4. Установка правил при каждой загрузке

Чтобы включить брандмауэр по умолчанию в Debian, необходимо хранить правила в /etc/nftables.conf и выполнять systemctl enable nftables как root. Вы можете остановить брандмауэр, исполнив nft flush ruleset как root.
В других случаях рекомендуется регистрировать сценарий конфигурации в up директиве /etc/network/interfaces. В следующем примере сценарий хранится под /usr/local/etc/arrakis.fw.

Пример 14.1. interfaces файл вызова сценария брандмауэра

auto eth0
iface eth0 inet static
    address 192.168.0.1
    network 192.168.0.0
    netmask 255.255.255.0
    broadcast 192.168.0.255
    up /usr/local/etc/arrakis.fw
Это, очевидно, предполагает, что вы используете ifupdown для настройки сетевых интерфейсов. Если вы используете что-то другое (например NetworkManager или systemd-networkd), обратитесь к своей соответствующей документации, чтобы узнать, как выполнить сценарий после того, как интерфейс был поднят.