как легко понять iptables

много раз я пытался понять эту штуку, но в целом не понимал, пытался ролики смотреть, но все равно мало было понятно

можно только по конкретным образцам правил под себя подогнать

pre-up iptables -t nat -A POSTROUTING -s ‘192.168.180.0/24’ -o vmbr5 -j MASQUERADE

pre-up iptables -t nat -A PREROUTING -i vmbr5 -p udp --dport 51820 -j DNAT --to 192.168.25.2:51820

а вот как с нуля составить? или как уже готовые правила подправить?

вот возникла недавно потребность, когда я в виртуальной машине с zabbix добавил еще один интерфейс из другой сети, откуда доступ в вебморду нежелателен и вот как его закрыть?

кто не знает, если просто в консоли ввести какое-то правило, то оно не сохраняется в файле и до следущего перезагруза будет работать

zabbix виртуалка сделана на базе alpine linux 8, а в разных линупсах настройки фаерволла хранятся в разных местах и файлы по разному называются
в моем случае это /etc/sysconfig/iptables

вот его содержимое

*raw
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]

### DNS-notrack
-A PREROUTING -p udp --sport 53 -j NOTRACK
-A OUTPUT -p udp --dport 53 -j NOTRACK
COMMIT

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:ICMP - DROP [0:0]
:NTP - DROP [0:0]
:SSH - DROP [0:0]
:ZBX - DROP [0:0]
:WEB - DROP [0:0]
:SNMPTRAP - DROP [0:0]
:SQL - DROP [0:0]

### ICMP-chain
-A ICMP -p icmp -m limit --limit 5/s --limit-burst 5 -j ACCEPT

### NTP-chain

### SSH-chain
-A SSH -s 0/0 -p tcp --dport 22 -j ACCEPT

### ZBX-chain
-A ZBX -s 0/0 -p tcp --dport 10050 -j ACCEPT
-A ZBX -s 0/0 -p tcp --dport 10051 -j ACCEPT

### WEB-chain
-A WEB -s 0/0 -p tcp -m multiport --dports 80,443 -j ACCEPT

### WEB-chain
-A WEB -s 0/0 -p udp --dport 162 -j ACCEPT

### Lo
-A INPUT -i lo -j ACCEPT

### DNS notrack
-A INPUT -s 8.8.8.8 -p udp --sport 53 -j ACCEPT
-A INPUT -s 8.8.4.4 -p udp --sport 53 -j ACCEPT

### ICMP
-A INPUT -s 0/0 -p icmp -j ICMP

### NTP
-A INPUT -s 0/0 -p udp --dport 123 -j NTP

### SSH
-A INPUT -s 0/0 -p tcp --dport 20:22 -j SSH

### ZBX
-A INPUT -s 0/0 -p tcp -m multiport --dports 10050,10051 -j ZBX

### WEB/HTTP/HTTPS
-A INPUT -s 0/0 -p tcp -m multiport --dports 80,443 -j WEB

### SNMP Traps
-A INPUT -s 0/0 -p udp --dport 162 -j SNMPTRAP

### OUT-for-notrack
-A OUTPUT -p icmp --icmp-type echo-request -j ACCEPT
-A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT

### DROP
-A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -j DROP
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
COMMIT

Файл состоит из:

  1. объявления таблиц,

  2. описания цепочек,

  3. правил,

  4. завершающего COMMIT.

Пример минимального файла:

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]

-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p tcp --dport 22 -j ACCEPT
-A INPUT -j DROP

COMMIT

в чем цимес очередного унылого поста с буквами, которых и так полно?

весь цимес в картинках правильных :index_pointing_up:t6:

когда я пробовал осилить, то мне попадались ролики с неправильными картинками и одна из них вот

ну или вот

в курсах микротик примерно такая же диаграмма

можно еще вот такую подкинуть

как же правильно тогда?
одним из пунктов поиска ответов на вопрос у меня являются гуглокартинки, иногда там неожиданно можно найти подходящую картинку и посмотреть еще к ней статью или даже ролик и я такие картинки нашел

вернемся опять к тому, что выдал чатик гпт о фрмате файла

Пример минимального файла:

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]

-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p tcp --dport 22 -j ACCEPT
-A INPUT -j DROP

COMMIT

Подробно по элементам

1. Таблица

*filter

Объявляет таблицу. Возможные варианты:

  • *filter — фильтрация пакетов (самая частая)

  • *nat — NAT (DNAT, SNAT, MASQUERADE)

  • *mangle — изменение пакетов

  • *raw — до трекинга соединений

  • *security — SELinux/LSM (редко)


2. Цепочки (chains)

:INPUT ACCEPT [0:0]

Формат:

:<ИМЯ_ЦЕПОЧКИ> <POLICY> [<packets>:<bytes>]

  • INPUT, OUTPUT, FORWARD — встроенные цепочки

  • POLICY — политика по умолчанию (ACCEPT, DROP)

  • [0:0] — счётчики пакетов и байт (информационные)

Пользовательская цепочка:

:MYCHAIN - [0:0]

(у неё нет политики)


3. Правила

-A INPUT -p tcp --dport 22 -j ACCEPT

Формат:

-A <ЦЕПОЧКА> <условия> -j <ДЕЙСТВИЕ>

Часто используемые параметры:

  • -p tcp|udp|icmp — протокол

  • --dport / --sport — порт

  • -s / -d — источник / назначение

  • -i / -o — интерфейс

  • -m state --state ESTABLISHED,RELATED

  • -j ACCEPT | DROP | REJECT | LOG | DNAT | SNAT | MASQUERADE

Порядок правил критически важен — они обрабатываются сверху вниз.


4. COMMIT

COMMIT

Обязательная строка, означающая конец таблицы.
Без неё iptables-restore не применит правила.

вот из этой картинки видно, что такое таблицы, а что такое цепочки и какие цепочки к каким таблицам относятся
и теперь мы видим, что *filter это общая таблица и там всего 3 цепочки дефолтных, которые и определены ниже в файле
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]

а что такое цепочки и как они устроены?

оказывается можно из одной цепочки правил прыгнуть в другую цепочку правил как это делается в подпрограммах бейсика или асма

и прыгнуть я тут не просто так написал, потому что именно этим термином обозначен параметр -j и после него всегда идет обычно дефолтные названиця цепочек

например -A INPUT -p tcp --dport 22 -j ACCEPT

но если мы возьмем наш Zabbix, то там несколько по другому выглядит

WEB/HTTP/HTTPS

-A INPUT -s 0/0 -p tcp -m multiport --dports 80,443 -j WEB
это уже сбивает с толку, поэтому я тут и приложил картинку выше, которая показывает, что можно прыгнуть в другую цепочку правил -j WEB

перейдем в нее

WEB-chain

-A WEB -s 0/0 -p tcp -m multiport --dports 80,443 -j ACCEPT

WEB-chain

-A WEB -s 0/0 -p udp --dport 162 -j ACCEPT
тут правда 162 порт не в тему, но да ладно

как видим, цепочка веб ведет к разрешению пакетов только при совпадении портов 80 и 443,а вот дефолтные настройки всех цепочек

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:ICMP - DROP [0:0]
:NTP - DROP [0:0]
:SSH - DROP [0:0]
:ZBX - DROP [0:0]
:WEB - DROP [0:0]
:SNMPTRAP - DROP [0:0]
:SQL - DROP [0:0]

т.е. сначала все запрещаем, потом добавляем правила разрешения

теперь встает вопрос, как пакет вообще идет?
у него есть 2 варианта - с интерфейса на другой сетевой интерфейс и тогда компутер работает как роутер (в частности настройка маскарадинга или разрешение исходящих пакетов в инторнет)

либо же пакет предназначен локальному процессу
и вот тут очень хорошо нарисованы эти таблицы (были в платном пдф, но я таки вытащил их)

вариант 2 - роутер

вариант 3 - наш с одним интерфейсом и локальным процессом

вот эти 3 части картинок дают полную картину работы фаерволла

и вот уже после всего этого можно только понять первую картинку прохождения пакетов по всем этим таблицам с цепочками

теперь возвращаемся к вопросу о том, как на одном интерфейсе запретить приходить пакетам на вебморду заббикса, но не препятствовать приходить им на заббикс сервер

а еще там работает база данных и тоже к ней как бы доступ надо закрыть

и вот тут мы начинаем понимать, зачем сделано перенаправление на отдельные кастомные цепочки типа -j WEB

заходим туда
WEB-chain
-A WEB -s 0/0 -p tcp -m multiport --dports 80,443 -j ACCEPT

и просто дописываем входящий интерфейс -i eth0

т.е. на нулевой интерфейс только разрешаем приход пакетов, а второй eth1 уже не в списке и если на него будут приходить пакеты на 80 и 443 порты, то будут забанены

а что с базой данных? для нее нет никаких правил, только цепочка определена

:SQL - DROP [0:0]

причем порт 3306 так же нигде не определен, а значит по дефолту все забанено, что явно не разрешено правилами, но ведь все работает?
а оказывается есть такое правило
#Lo

-A INPUT -i lo -j ACCEPT

разрешить любой трафик на интерфейсе loopback, через который пхп соединяется с бд

ssh я просто остановлю как сервис, т.к. больше не нужен и при необходимости из прокса можно зайти

2 лайка

добавлю от себя пару слов…

для новичков - перед тем как браться за iptables, желательно хотя бы базово понимать:

  1. TCP/IP стек (желательно вообще OSI модель), протоколы для которых будем настраивать файрволл, базово - какие протоколы, какие порты используются
  2. как выполнять настройку сетевых интерфейсов в линукс

неплохая статья по iptables для “быстрого чтения” есть на вики arch ну и man iptables, man iptables-extensions как референс

это зависит от операционной системы. например в debian (да и скорее всего во всех systemd системах) - правила сохраняются без доп. телодвижений. в alpine (который на openrc) нужно по старинке вызывать iptables save

этот формат iptables довольно сложно читать и составлять, обычно iptables настраивают просто через команды (дамп текущего списка правил можно сделать через флаг iptables -S). единственное исключение - если вам нужно использовать функционал commit-ов, те подменять правила iptables без прерываний, если у вас супер ответственный продакшен/high-load итп

в качестве диаграммы packet flow я всегда использовал картинку с англоязычной вики:

(на этой диаграмме только ярко-зеленые прямоугольники это iptables, все остальное это сопутствующие системы)

таблицы (флагiptables -t table) - это элемент настройки iptables, содержащий внутри цепочки (chain), далее цепочки содержат правила (rule)

таблицы:

  • таблицы заранее предопределены и имеют определенное назначение: на разных этапах обработки сетевого трафика используются разные комбинации таблица+цепочка
  • набор таблиц зависит от модулей ядра линукс. некоторых таблиц может не быть и/или не быть пока не загрузим модуль
  • обычно используются таблицы:
    • filter - для “фильтрации” пакетов - пропуска нужных или ненужных пакетов. на этом строится функционал firewall
    • nat - Network Address Translation - таблица для трансляции адресов, для настройки функционала роутера, проброса портов, проксирования
    • mangle - для “искажения”, модификации пакетов, выставления меток для пакетов и для различных расширенных манипуляций над трафиком
  • какую таблицу+цепочку нужно использовать зависит от:
    • в какой момент обработки пакета мы хотим “вклинить” свою логику
    • какое действие (target) мы хотим использовать. об этом ниже

цепочки:

  • существует набор предопределенных цепочек. мы можем создавать свои цепочки: iptables -N chain
  • ядро линукс обрабатывает правила внутри цепочек поочередно, в том порядке в которым мы настроили правила (добавили в цепочку)
  • если ни одно правило не совпало с пакетом, то срабатывает действие “по умолчанию” указанное в текущей цепочке. действие “по умолчанию” указывается для каждой цепочки через флаг iptables -P chain target
  • с помощью флага -j/–jump (или -g/–goto, но есть отличия) мы можем настроить переход из одной цепочки в другую, например в свою кастомную. iptables запоминает стек переходов и с помощью target RETURN можно вернуться в предыдущую цепочку вызвавшую текущую

правила:

  • правило состоит из критериев (? не знаю как лучше назвать match) и действия (target). критериев большое многообразие, разные флаги. действие указывается через флаг -j, --jump target
  • действие можно не указать, тогда над пакетом никаких действий совершено не будет но iptables засчитает такое сработавшее правило в счетчиках. для каждого правила считается отдельный счетчик совпадения по правилу, котор. можно сбросить через iptables -Z
  • существуют target не прерывающие обработку (non-terminating) и прерывающие:
    • не прерывающие позволяют выполнить несколько правил/действий над пакетом (и только в конце применить финальное прерывающие правило/действие). пример не прерывающего target - MARK, LOG
    • после прерывающего действия пакет больше не обрабатывается на текущем этапе (таблице+цепочке). пример - ACCEPT, DROP, REJECT
1 лайк

Спасибо за материалы, сохранил в закладки

как составлять iptables правила, на примерах:

запретить доступ по SSH и админскому UI по HTTP на порту 8006 для всех, кроме доверенной сети

что важно знать для упрощения настройки: HTTP и SSH это tcp протоколы. стандартный порт SSH - 22

допустим доверенная сеть - 10.0.0.0/24

trusted_net='10.0.0.0/24'

# предварительно очищаем всю цепочку INPUT
iptables -t filter -F INPUT

# разрешаем трафик на loopback интерфейсе
iptables -t filter -A INPUT -i lo -j ACCEPT

# разрешаем прохождение трафика для ранее установленных соединений
iptables -t filter -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# conntrack - модуль и подсистема ядра линукс отвечающая за определение пакетов в рамках "постоянных" соединений
# ctstate - фильтр для уже установленных соединений и относящихся к уже установленным

# разрешаем SSH для доверенной сети
iptables -t filter -A INPUT -s $trusted_net -p tcp --dport 22 -j ACCEPT
# -p - указание протокола L4
# --dport - порт назначения


# разрешаем 8006 для доверенной сети
iptables -t filter -A INPUT -s $trusted_net -p tcp --dport 8006 -j ACCEPT

# запрещаем весь входящий трафик по умолчанию. [!] будет заблокировано все что не разрешено явно
iptables -t filter -P INPUT DROP


# смотрим что получилось
iptables -t filter -nvL

запретить ответы на ping запросы на интерфейсе eth0

iptables -t filter -I INPUT -i eth0 -p icmp --icmp-type echo-request -j DROP

перенаправить трафик с привилегированного порта 443 на пользовательский 8443

iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-ports 8443

включить маршрутизацию трафика (NAT)

например у нас есть роутер с интерфейсом к провайдеру - wan0, и интерфейсом в домашнюю сеть - eth0 с адресом 10.0.0.0/24


sysctl net.ipv4.ip_forward=1
echo net.ipv4.ip_forward=1 >> /etc/sysctl.conf

# маскируем пакеты, те подменяем source адрес на свой для того что бы ответы на наши запросы вернулись на наш внешний IP (выданный провайдером)
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o wan0 -j MASQUERADE

# разрешаем пересылку трафика для уже установленных соединений
iptables -A FORWARD -o wan0 -m state --state ESTABLISHED,RELATED -j ACCEPT
# разрешаем пересылку трафика eth0 -> wan0
iptables -A FORWARD -i eth0 -o wan0 -j ACCEPT

# запрещаем пересылку пакетов для неизвестных интерфейсов
iptables -P FORWARD DROP

перенаправить трафик на другой хост в сети за NAT

типовой сценарий port forwarding: допустим нужно перенаправить входящий трафик на с портов 6000..6200 на 10.0.0.120 хост в локальной сети

iptables -t nat -A PREROUTING -i wan0 -p tcp --dport 6000:6200 -j DNAT --to 10.0.0.120

так же можно поизучать примеры на разных wiki, вот пример gentoo (возможно полезно для пользователей alpine, тк тоже openrc система, готовые скрипты):

как составлять цепочки правил:

  • зависит от того как настроен policy на “родительской” цепочке, с которой начинается обработка трафика:

    • если policy DROP - соответственно все правила пишутся с расчета что по умолчанию DROP. в основном такой подход нужен когда нужна высокая надежность - забыли/ошиблись в каком то правиле это не приводит к появлению уязвимости в firewall. так же такой подход удобен когда нужно открывать минимум портов (что зачастую так)
    • если policy ACCEPT - тогда мы в конце нашей цепочки в конце добавляем явное правило с -j DROP
  • хорошим тоном считается когда iptables правил минимальное кол-во, тк правила ограничивают пропускную способность, тратят ресурсы итд. существуют технологии hw offload правил (вычислений правил на уровне железа, для примера такое используется в openwrt, в mikrotik оборудовании), но это скорее экзотика в условиях домашних пк/серверов и там много ограничений

  • переход (-j) в кастомную цепочку можно использовать из любого места. выгодно составлять свои цепочки с возможностью пере-использования из разных мест, пример:

# команда iptables вначале опущена для упрощения

-N LOG_AND_DROP
-A LOG_AND_DROP -j LOG --log-level alert --log-prefix "UNAUTHORIZED:"
-A LOG_AND_DROP -j DROP


-A INPUT -p tcp --dport ssh ! -s $trusted_net -j LOG_AND_DROP

-A FORWARD -i eth0 -o wan0 -j ACCEPT
-A FORWARD -j LOG_AND_DROP # переиспользуем тот же LOG_AND_DROP
  • определитесь по какому критерию вы хотите “нарезать”, сгруппировать правила, некоторые примеры:

в proxmox такая структура:

# proxmox firewall уровня хоста
# INPUT -> PVEFW-INPUT -> PVEFW-HOST-IN -> (DROP)
-A INPUT -j PVEFW-INPUT

-A PVEFW-INPUT -j PVEFW-HOST-IN

-A PVEFW-HOST-IN -i lo -j ACCEPT
-A PVEFW-HOST-IN ... # кастомные правила уровня хоста
-A PVEFW-HOST-IN -j DROP


# proxmox firewall уровня VM/CT
# FORWARD -> PVEFW-FORWARD -> PVEFW-FWBR-IN -> tapXXXXX-IN -> (DROP)
-A FORWARD -j PVEFW-FORWARD
-A PVEFW-FORWARD ... -j PVEFW-FWBR-IN
-A PVEFW-FWBR-IN ... -j tapXXXXX-IN

-A tapXXXXX-IN ... # кастомные правила уровня VM/CT
-A tapXXXXX-IN -j DROP

в openwrt правила поделены на зоны файрвола, там что-то вроде:

-A INPUT -i lo -j ACCEPT

-A INPUT -i internet -j input_ZONE_INTERNET
-A INPUT -i lan -j input_ZONE_LAN
# ... настроены цепочки под каждую зону ...

-A input_ZONE_INTERNET ... # кастомные правила
-A input_ZONE_INTERNET -j reject_from_INTERNET 

# и далее настроены reject_from / reject_to / accept_from / accept_to правила под каждую зону

-A reject_from_INTERNET ... # кастомные правила
-A reject_from_INTERNET -j handle_reject

# общая цепочка для reject
-A handle_reject -p tcp -j REJECT --reject-with tcp-reset
-A handle_reject -p udp -j REJECT --reject-with icmp-port-unreachable

например можно поделить правила по сервисам:

# на первом уровне определяем что фильтруем (сервис/протокол)
-A INPUT -p tcp --dport ssh   -j IN_SSH
-A INPUT -p tcp --dport mysql -j IN_MYSQL
# ...
-A INPUT -j DROP # блокируем все остальное не известное


# на втором уровне определяем кого фильтруем (клиенты/сети)
-A IN_SSH ! -s $trusted_net -j DROP
-A IN_SSH -j ACCEPT

-A IN_MYSQL -s $mysql_clients_net -j ACCEPT
-A IN_MYSQL -j DROP