Iptables: различия между версиями

Содержимое удалено Содержимое добавлено
м <source> -> <syntaxhighlight> (phab:T237267)
Строка 42:
=== Принцип работы ===
Все пакеты пропускаются через определенные для них последовательности цепочек (см. рис.). При прохождении пакетом цепочки, к нему последовательно применяются все правила этой цепочки в порядке их следования. Под применением правила понимается: во-первых, проверка пакета на соответствие критерию, и во-вторых, если пакет этому критерию соответствует, применение к нему указанного действия. Под действием может подразумеваться как элементарная операция (встроенное действие, например, ACCEPT, MARK), так и переход в одну из пользовательских цепочек. В свою очередь, действия могут быть как терминальными, то есть прекращающими обработку пакета в рамках данной базовой цепочки (например, ACCEPT, REJECT), так и нетерминальными, то есть не прерывающими процесса обработки пакета (MARK, TOS). Если пакет прошел через всю базовую цепочку и к нему так и не было применено ни одного терминального действия, к нему применяется действие по умолчанию для данной цепочки (обязательно терминальное). Например,
<sourcesyntaxhighlight lang="bash">
iptables -F # Очищаем все цепочки таблицы filter
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # Ко всем пакетам, которые относятся к уже установленным
Строка 48:
iptables -P INPUT DROP # В качестве действия по умолчанию для входящих пакетов устанавливаем DROP — блокирование пакета
iptables -P OUTPUT ACCEPT # Разрешаем все исходящие пакеты
</syntaxhighlight>
</source>
Теперь цепочка INPUT таблицы filter содержит единственное правило, которое пропускает все пакеты, относящиеся к уже установленным соединениям. Ко всем остальным входящим пакетам будет применено действие по умолчанию — DROP. Цепочка же OUTPUT вообще не содержит правил, поэтому ко всем исходящим пакетам будет применяться действие по умолчанию ACCEPT. Таким образом хост, настроенный согласно этому примеру и подключенный к {{w |Интернет}}у, будет недоступен извне (все попытки установить соединение снаружи блокируются), однако с самого хоста доступ к Интернету будет свободный (исходящие пакеты разрешены, а ответы на них уже относятся к установленным соединениям).
 
Строка 177:
 
Что касается улучшений в версиях 5 и 6 по сравнению с четвертой и более ранними, то, помимо уже упомянутых (поддержка адресов {{w |IPv6}}, поддержка [[Тайм-аут (Телекоммуникации)|тайм-аутов]] для всех типов данных, возможность сохранения идентификатора протокола транспортного уровня в типах hash: ip, port, hash: ip, port, ip и hash: ip, port, net), стоит также отметить переход на использование nfnetlink (см. выше) для связи ядра и userspace, а также более простой синтаксис. Например, те действия, которые в ipset 4 выглядели бы так:
<sourcesyntaxhighlight lang="bash">
ipset -N foo macipmap --network 192.168.0.0/16 # Создаем список
ipset -A foo 192.168.1.1,12:34:56:78:9A:BC # Добавляем запись
Строка 184:
ipset -F foo # Очищаем список
ipset -X foo # Удаляем список
</syntaxhighlight>
</source>
в современных версиях ipset будут выглядеть следующим образом:
<sourcesyntaxhighlight lang="text">
ipset create foo bitmap:ip,mac range 192.168.0.0/16
ipset add foo 192.168.1.1,12:34:56:78:9A:BC
Строка 193:
ipset flush foo
ipset destroy foo
</syntaxhighlight>
</source>
Кроме того, современные версии ipset поддерживают работу в режиме простой [[w:Командный интерпретатор|оболочки]], принимающей команды со [[stdin]]. При этом в них обеспечивается полная обратная совместимость синтаксиса с четвертой версией, то есть синтаксис, корректно работающий для четвертой версии ipset, будет точно так же работать в пятой и шестой.
 
Строка 241:
 
Например, одним простым правилом
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT
</syntaxhighlight>
</source>
вы можете обеспечить корректное пропускание всех входящих пакетов, принадлежащих установленным соединениям, и сконцентрироваться только на фильтрации новых соединений.
 
Строка 285:
* '''RETURN''' — обеспечивает возврат из текущей цепочки. В частности, если из цепочки A правилом номер 3 пакет был направлен в цепочку B, то применение к нему в цепочке B действия RETURN приведет к его переходу обратно в цепочку A, и он продолжит ее прохождение со следующего правила (номер 4). Для базовой цепочки к пакету сразу будет применено действие по умолчанию.
Например, предположим, что нам нужно обеспечить доступ к определенным [[w:Порт (компьютерные сети)|портам]] нашего сервера для всех хостов из подсети 10.134.0.64/26, ''кроме'' 10.134.0.67 и 10.134.0.100.
<sourcesyntaxhighlight lang="bash">
iptables -F # Очищаем все цепочки таблицы filter
iptables -N our_subnet # Создаем специальную цепочку для проверки пакетов из нашей подсети
Строка 300:
iptables -P INPUT DROP # Что не разрешено — то запрещено
iptables -P OUTPUT ACCEPT # На выход — можно все
</syntaxhighlight>
</source>
Теперь все новые входящие пакеты, отправленные из нашей подсети 10.134.0.64/26, отправляются на проверку в цепочку <tt>our_subnet</tt>. К пакетам с «запрещенных» хостов применяется операция RETURN, и они покидают эту цепочку и впоследствии блокируются действием по умолчанию цепочки INPUT. Пакеты с остальных хостов этой подсети пропускаются в том случае, если они адресованы на порты [[w:Прокси-сервер|прокси]] (8080/tcp), {{w |SSH}} (22/tcp), {{w |SMB}} (139,445/tcp, 137,138/udp), {{w |DNS}} (53/tcp, udp), {{w |NTP}} (123/udp). Также для этих хостов разрешены {{w |ICMP}}-эхо-запросы (пинги). Все остальные пакеты (включая пакеты не из нашей подсети, пакеты с запрещенных хостов и пакеты на неразрешенные порты) блокируются действием по умолчанию DROP.
 
Строка 306:
 
Впрочем, изложенный принцип теряет первоначальный смысл, если в разных случаях нужно обеспечить разные способы блокирования. Рассмотрим чуть более сложный пример: допустим, наш сервер подключен к локальной сети 10.0.0.0/8. В ней есть некий «недоверенный» сегмент, скажем, 10.122.0.0/16, для которого нужно полностью заблокировать доступ к нашему серверу. Но в этом сегменте есть несколько «хороших» хостов (скажем, 10.122.72.11 и 10.122.180.91), которые блокировать не нужно. Поставленную задачу можно решить следующим образом:
<sourcesyntaxhighlight lang="bash">
iptables -F # Очищаем все цепочки таблицы filter
iptables -N check_untrusted # Создаем специальную цепочку для проверки пакетов из нашей подсети
Строка 325:
iptables -P INPUT REJECT
iptables -P OUTPUT ACCEPT # На выход — можно все
</syntaxhighlight>
</source>
Заметим, что в данном примере хосты из недоверенного сегмента блокируются «молча» (DROP), в то время как при обращении «хороших» хостов на неправильные порты сервер вежливо сообщает им об отказе (REJECT). Этот принцип также является очень важным при построении фаерволов — чем меньше информации попадает к потенциальному злоумышленнику, тем лучше, поэтому на опасных направлениях целесообразно использовать «молчаливое» блокирование, в то время как явные сообщения для «своих» упрощают диагностику работы сети и поиск ошибок.
 
: ''Примечание: следующий пример планируется к переносу в еще не написанный раздел статьи (Прочие критерии → mac)''.
Например, предположим, что у нас есть объединенная через один [[w:Сетевой коммутатор|свитч]] подсеть 10.134.0.64/26, к которой наш компьютер подключен через интерфейс eth1. Тогда защиту от {{w |спуфинг}}а (подделки адресов отправителя) через проверку {{w |MAC-адрес}}а можно обеспечить следующим образом:
<sourcesyntaxhighlight lang="bash">
sysctl net.ipv4.ip_forward=1 # Разрешаем шлюзу передавать транзитный трафик
iptables -F # Очищаем все цепочки таблицы filter
Строка 354:
iptables -P FORWARD DROP # Остальных блокируем
iptables -P OUTPUT ACCEPT # Исходящий трафик разрешаем
</syntaxhighlight>
</source>
<!--В демонстрационных целях этот пример был слегка упрощён (вместо повтора правил с критерием <tt>-s 10.134.0.64/26</tt> эти правила логичнее было бы вынести в отдельные цепочки) но, тем не менее, наглядно показывает принципы работы действия RETURN.-->
Все новые входящие пакеты с обратным адресом из подсети 10.134.0.64/26 проходят двойную проверку в цепочке <tt>check_ours</tt>. Первый этап проверки — соответствие интерфейса. Ведь к нашей подсети, по условию задачи, мы подключены только интерфейсом eth1. Если пакет якобы из этой подсети придет с любого другого интерфейса, он будет заблокирован. Второй этап проверки заключается в последовательном чтении списка соответствующих [[w:IP-адрес|IP]]- и {{w |MAC-адрес}}ов (цепочка <tt>check_ours_sp00f</tt>). Каждое правило этой цепочки, кроме последнего, выделяет пакеты от одного конкретного хоста нашей подсети. К пакетам, прошедшим проверку, применяется операция RETURN, выводящая пакет из этой цепочки. Если же пакет не подошел ни по одному из этих правил, он считается не прошедшим проверки и блокируется последним правилом <tt>-j DROP</tt>. Для прошедших же проверку пакетов дальнейшая судьба зависит от того, кому они адресованы. Если они идут через наш хост в другие подсети, то они пропускаются. Если же они адресованы непосредственно нашему хосту, то пропускаются только соединения на некоторые {{w |TCP}}-[[w:Порт (компьютерные сети)|порты]] ([[w:Прокси-сервер|прокси]], {{w |SSH}}, {{w |SMB}}).
* '''LOG''' — позволяет записывать информацию о пакетах в журнал ядра (см. {{w |syslog}}).
Например, если в предыдущем примере перед строкой
<sourcesyntaxhighlight lang="bash">
iptables -A INPUT -p tcp -m multiport --dports 22,53,8080,139,445 -j ACCEPT
</syntaxhighlight>
</source>
мы добавим строку
<sourcesyntaxhighlight lang="bash">
iptables -A INPUT -p tcp -m multiport --dports 22,53,8080,139,445 -j LOG --log-level INFO --log-prefix "New connection from ours: "
</syntaxhighlight>
</source>
то для каждого нового соединения к нашему хосту из нашей подсети в системном журнале будет появляться запись примерно такого вида:
Jul 16 20:10:40 interdictor kernel: New connection from ours: IN=eth0 OUT= MAC=00:15:17:4c:89:35:00:1d:60:2e:ed:a5:08:00 SRC=10.134.0.67
Строка 386:
* '''NFQUEUE''' — во многом похоже на ULOG, но передает специальному демону не информацию о пакете, а сам пакет целиком. Применяется, в частности, для организации работы {{w |l7-filter}}-userspace.
Например, если демон l7-filter был запущен командой
<sourcesyntaxhighlight lang="bash">
l7-filter -f /etc/l7-filter.conf -q 2
</syntaxhighlight>
</source>
то передать ему на обработку все входящие и локально сгенерированные пакеты можно командами
<sourcesyntaxhighlight lang="bash">
iptables -t mangle -F # На всякий случай очищаем таблицу mangle
iptables -t mangle -A PREROUTING -j NFQUEUE --queue-num 2
iptables -t mangle -A OUTPUT -j NFQUEUE --queue-num 2
</syntaxhighlight>
</source>
Опция <tt>--queue-num</tt> позволяет указать номер очереди пакетов. Он должен быть совпадать с номером, указанным при запуске демона l7-filter (параметр <tt>-q</tt>).
* '''QUEUE''' — устаревшая версия NFQUEUE. Не имеет параметров, так как работает только с очередью номер 0.
Строка 407:
* ''Терминальными'' называются действия, которые прерывают прохождение пакета через текущую базовую цепочку. То есть если к пакету в рамках некоторого правила было применено терминальное действие, он уже не проверяется на соответствие всем следующим правилам в этой цепочке (и в тех цепочках, из которых она была вызвана, если это пользовательская цепочка). Терминальными являются все действия, специфичные для таблиц filter и nat. Из приведенных выше в этом разделе — ACCEPT, DROP, REJECT, NFQUEUE, QUEUE.
Например,
<sourcesyntaxhighlight lang="bash">
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j LOG --log-prefix "IN HTTP: "
</syntaxhighlight>
</source>
Второе из приведенных правил (логгирование) никогда не будет срабатывать, так как все пакеты, удовлетворяющие этому критерию (входящие TCP-пакеты на порт 80), будут пропущены по первому из этих правил, и до второго просто не дойдут.
 
* ''Нетерминальными'', соответственно, являются действия, ''не'' прерывающие процесс прохождения пакета через цепочки. Нетерминальными являются действия, специфичные для таблицы mangle, а из перечисленных выше — LOG, ULOG и NFLOG.
Например,
<sourcesyntaxhighlight lang="bash">
iptables -A INPUT -p tcp --dport 80 -j LOG --log-prefix "IN HTTP: "
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
</syntaxhighlight>
</source>
Теперь все входящие на TCP-порт 80 пакеты будут сначала заноситься в лог, и только потом пропускаться.
 
Строка 455:
 
В качестве полезного примера использования этого действия можно привести команду
<sourcesyntaxhighlight lang="bash">
iptables -t mangle -I PREROUTING -j TTL --ttl-inc 1
</syntaxhighlight>
</source>
делающую наш шлюз невидимым для большинства [[w:Traceroute|трассировщиков]].
 
Строка 463:
 
Другой полезный пример — выравнивание TTL на выходе в Интернет (интерфейс eth0)
<sourcesyntaxhighlight lang="bash">
iptables -t mangle -I POSTROUTING -o eth0 -j TTL --ttl-set 64
</syntaxhighlight>
</source>
Некоторые домашние провайдеры, пытаясь бороться с нелегальной перепродажей канала их клиентами, блокируют доступ тем клиентам, на выходе которых замечены пакеты с разными значениями TTL, потому что этот факт косвенно указывает на наличие у клиента внутренней подсети. Предложенное правило, будучи применено на шлюзе такой подсети, делает ее детекцию по TTL крайне затруднительной.
 
Строка 477:
 
С точки зрения клиента, эта проблема выглядит так: {{w |пинг}}и проходят нормально, но при попытке открыть какую-либо {{w |веб}}-страницу, {{w |браузер}} «подвисает». При этом с самого шлюза все работает нормально. В этом случае достаточно применить на шлюзе следующую команду
<sourcesyntaxhighlight lang="bash">
iptables -t mangle -I FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
</syntaxhighlight>
</source>
которая обеспечит автоматическую установку размера сегмента в TCP-заголовках SYN- и RST,SYN-пакетов в соответствии с минимальным из известных нашему шлюзу значений {{w |MTU}} на пути следования пакета. Например, если пакет пришел с интерфейса eth0 (MTU 1500) и уходит через интерфейс ppp0 (MTU 1492), а суммарный размер заголовков [[w:Протоколы сетевого уровня|сетевого]] и [[w:Транспортный уровень|транспортного]] уровней составляет 40 {{w |байт}} (20 байт {{w |TCP}} и 20 байт {{w |IP}}), то целесообразно установить MSS равным 1452 байтам.
 
Строка 487:
Кроме того, данное действие поддерживает три потенциально опасные опции, не отраженные в документации: <tt>--ecn-tcp-cwr</tt> (установка значения бита CWR в TCP-заголовке, 0 или 1), <tt>--ecn-tcp-ece</tt> (установка значения бита ECE в TCP-заголовке, 0 или 1) и <tt>--ecn-ip-ect</tt> (установка значения ECT codepoint в IPv4-заголовке, от 0 до 3). Использование этих опций настолько опасно, что они не отражены даже во встроенной справке iptables (<tt>iptables -j ECN -h</tt>) — их можно увидеть, только изучив [https://git.netfilter.org/iptables/tree/extensions/libipt_ECN.c исходный код]. Без крайней необходимости применять их не рекомендуется.
* '''TCPOPTSTRIP''' — выполняет удаление заданных TCP-опций из заголовка TCP-пакета (единственный параметр <tt>--strip-options ''значение''[,''значение''[,...]]</tt>). Удаляемые опции могут быть указаны через их номера (согласно [http://www.iana.org/assignments/tcp-parameters/tcp-parameters.xhtml списку на сайте IANA]) или в виде символьных обозначений (список обозначений, поддерживаемых в вашей версии iptables можно посмотреть, выполнив команду <tt>iptables -j TCPOPTSTRIP -h</tt>). Разумеется, данное действие допустимо только для протокола TCP (<tt>-p tcp</tt>). Например,
<sourcesyntaxhighlight lang="bash">
iptables -t mangle -A POSTROUTING -p tcp -j TCPOPTSTRIP --strip-options timestamp
</syntaxhighlight>
</source>
обеспечит удаление штампов времени (RFC 1323). С одной стороны, такое правило будет препятствовать удаленному злоумышленнику определить {{w |аптайм}} нашего хоста, а также тех хостов, маршрут от которых до злоумышленника проходит через наш хост. С другой стороны, блокирование штампов времени может негативно сказаться на быстродействии сети, особенно если вы используете подключения на скорости [[w:Fast Ethernet|100 МБит]] и выше. Заметим также, что если вам нужно управлять использованием именно штампов времени для TCP IPv4-пакетов, исходящих от вашего хоста, вы можете воспользоваться {{w |sysctl}}-параметром net.ipv4.tcp_timestamps (1 — штампы включены, 0 — выключены).
* '''TPROXY''' — реализует механизм полностью прозрачного [[w:Прокси-сервер|проксирования]]. Такой подход отличается от традиционно используемого «прозрачного» проксирования (действие REDIRECT [[#Таблица nat|таблицы nat]], см. ниже) тем, что заголовок пакета никак не модифицируется, в том числе не заменяется {{w |IP-адрес}} назначения (при традиционном прозрачном проксировании он заменяется на адрес проксирующего хоста). Кроме того, полностью прозрачное проксирование является прозрачным с точки зрения обеих общающихся сторон. Например, при проксировании обращений некоторой подсети клиентов к серверам из другой подсети, можно сделать так, чтобы не только клиенты считали, что обращаются напрямую к серверам, но и сервера «видели» настоящие исходные адреса клиентов и могли бы устанавливать с ними обратные соединения (например, в случае активного режима {{w |FTP}}). При традиционном же «прозрачном» проксировании, сервера могут видеть только адрес прокси-сервера.
Строка 496:
 
В качестве примера можно привести простейший случай конфигурации iptables и iproute2 для обеспечения полностью прозрачного проксирования {{w |IPv4}} {{w |HTTP}}.
<sourcesyntaxhighlight lang="bash">
# Добавляем принудительную локальную маршрутизацию для всех адресов в таблицу 120
ip route add local 0.0.0.0/0 dev lo table 120
Строка 509:
# Такие пакеты можно выделять с помощью критерия socket
iptables -t mangle -I PREROUTING -m socket -j proxypackets
</syntaxhighlight>
</source>
Таким образом, пакеты, перехваченные TPROXY-правилом, маркируются и направляются на локальный сокет (порт 3129), который должен прослушиваться специально сконфигурированным прокси-сервером (в случае Squid, в файл конфигурации <tt>squid.conf</tt> нужно добавить строку <tt>http_port 3129 tproxy</tt>). Все пакеты, принадлежащие уже проксируемым соединениям, перехватываются правилом с критерием socket (этот критерий позволяет определить, существует ли на нашем хосте сокет, связанный с данным пакетом), маркируются и немедленно пропускаются. Отметим, что последнее правило будет обрабатываться раньше, чем правило с TPROXY, так как параметр <tt>-I</tt> обеспечивает вставку правил ''в начало'' цепочки, и при последовательном добавлении нескольких правил с этим параметром, порядок добавленных правил будет обратным порядку их ввода.
 
Строка 526:
 
Допустим, у нас есть локальная сеть 192.168.1.0/255.255.255.0 с несколькими компьютерами, имеющими адреса 192.168.1.2, 192.168.1.3 и т. д. Адрес 192.168.1.1 имеет внутренний (подключенный к локальной сети) сетевой интерфейс шлюза, назовем этот интерфейс eth1. Другой его интерфейс, назовем его eth0, подключен к сети Интернет и имеет адрес, допустим, 208.77.188.166. Тогда, чтобы обеспечить выход хостов из этой локальной сети в Интернет, на шлюзе достаточно выполнить следующие команды
<sourcesyntaxhighlight lang="bash">
sysctl net.ipv4.ip_forward=1 # Разрешаем шлюзу передавать транзитный трафик
iptables -F FORWARD # На всякий случай очистим цепочку FORWARD
Строка 537:
# Маскарадим весь трафик, идущий через eth0
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
</syntaxhighlight>
</source>
Теперь, если один из хостов локальной сети, например, 192.168.1.2, попытается связаться с одним из интернет-хостов, например, 199.204.44.194 (kernel.org), при проходе его пакетов через шлюз, их исходный адрес будет подменяться на внешний адрес шлюза, то есть 208.77.188.166. С точки зрения удаленного хоста (kernel.org), это будет выглядеть, как будто с ним связывается непосредственно сам шлюз. Когда же удаленный хост начнет ответную передачу данных, он будет адресовать их именно шлюзу, то есть 208.77.188.166. Однако, на шлюзе адрес назначения этих пакетов будет подменяться на 192.168.1.2, после чего пакеты будут передаваться настоящему получателю. Для такого обратного преобразования никаких дополнительных правил указывать не нужно — это будет делать все та же операция MASQUERADE. Простота трансляции сетевых адресов является одним из важнейших достоинств stateful-фильтрации.
 
Строка 543:
 
* '''SNAT''' (Source Network Address Translation) — работает аналогично MASQUERADE, однако позволяет указать адрес «внешнего» интерфейса (опция <tt>--to-source</tt>). Такой подход позволяет экономить процессорное время шлюза, так как в случае с MASQUERADE для каждого пакета адрес внешнего интерфейса определяется заново. Таким образом, если в предыдущем примере внешний адрес шлюза 208.77.188.166 является статическим (то есть никогда не меняется), команду
<sourcesyntaxhighlight lang="bash">
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
</syntaxhighlight>
</source>
целесообразно заменить на
<sourcesyntaxhighlight lang="bash">
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 208.77.188.166
</syntaxhighlight>
</source>
Однако, в случае, если внешний адрес шлюза динамический, то есть меняется в начале каждой новой сессии, правильнее будет использовать именно MASQUERADE.
 
Дополнительно, если на внешнем интерфейсе шлюза «висит» несколько статических адресов, например, 208.77.188.166, 208.77.188.167 и 208.77.188.168, можно использовать балансировку между этими адресами:
<sourcesyntaxhighlight lang="bash">
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 208.77.188.166-208.77.188.168
</syntaxhighlight>
</source>
Теперь для каждого нового соединения адрес для подмены будет выбираться случайным образом.
 
Строка 561:
 
Возвращаясь к предыдущему примеру, предположим, что нам нужно «пробросить» внешний адрес шлюза 208.77.188.166 на хост 192.168.1.2, то есть все, кто будет обращаться по внешнему адресу шлюза 208.77.188.166, должны попадать на хост 192.168.1.2. Это достигается следующими командами:
<sourcesyntaxhighlight lang="bash">
# Разрешаем шлюзу передавать транзитный трафик
sysctl net.ipv4.ip_forward=1
Строка 573:
iptables -A FORWARD -m conntrack --ctstate NEW -d 192.168.1.2 -j ACCEPT
iptables -P FORWARD DROP # Весь остальной транзитный трафик — запрещаем.
</syntaxhighlight>
</source>
В лучших традициях stateful-фаерволинга, подменяются не только адреса назначения для входящих пакетов (208.77.188.166 -> 192.168.1.2), но и адреса источника для исходящих пакетов (192.168.1.2 -> 208.77.188.166), поэтому для интернет-хостов создается полное впечатление, что они общаются именно с 208.77.188.166.
 
Однако, при всей своей простоте и наглядности, этот пример имеет два недостатка. Во-первых, из нашей локальной сети 192.168.1.0/24 адрес 208.77.188.166 может быть недоступен, так как 192.168.1.2, увидев, что запрос исходит из его сети, может отправить ответ непосредственно отправителю (например, 192.168.1.3), минуя сам шлюз. Отправитель же будет ждать ответа от 208.77.188.166, и просто проигнорирует ответ от 192.168.1.2. Впрочем, эта проблема легко исправляется добавлением SNAT’а для пробрасываемых пакетов:
<sourcesyntaxhighlight lang="bash">
iptables -t nat -A POSTROUTING -d 192.168.1.2 -s 192.168.1.0/24 -j SNAT --to-source 192.168.1.1
</syntaxhighlight>
</source>
Теперь, с точки зрения 192.168.1.2, к нему будет обращаться только сам шлюз (192.168.1.1), поэтому ответ также пойдет через шлюз, где будет корректно оттранслирован. Такое решение тоже имеет недостаток — подмена исходного адреса пакета приводит к тому, что компьютер, на который мы пробрасываем соединение, не может определить реального инициатора соединения. Чтобы уменьшить «вредность» этого правила, в него добавлена опция «-s 192.168.1.0/24», которая ограничивает применение этого правила только пакетами из локальной сети. Таким образом, соединения из локальной сети будут выглядеть как соединения, инициированные шлюзом, а соединения из внешней сети будут иметь свои оригинальные адреса.
 
Вторым недостатком предложенного примера является некорректная обработка запросов с самого шлюза. Ведь пакеты, генерируемые локальными процессами, не проходят цепочку PREROUTING. Поэтому правило DNAT нужно также добавить и в цепочку OUTPUT таблицы nat:
<sourcesyntaxhighlight lang="bash">
iptables -t nat -A OUTPUT -d 208.77.188.166 -j DNAT --to-destination 192.168.1.2
</syntaxhighlight>
</source>
Разумеется, исходящие соединения к 192.168.1.2 и ответы на них должны быть разрешены соответственно в цепочках OUTPUT и INPUT таблицы filter. Самый простой способ сделать это —
<sourcesyntaxhighlight lang="bash">
iptables -F INPUT # Очищаем цепочку INPUT
# Разрешаем входящие пакеты по уже установленным соединениям
Строка 594:
iptables -F OUTPUT # Очищаем цепочку OUTPUT
iptables -P OUTPUT ACCEPT # Разрешаем исходящие пакеты безо всяких ограничений
</syntaxhighlight>
</source>
 
Кроме того, при помощи действия DNAT можно пробрасывать не только сразу весь IP-адрес, но и отдельные {{w |TCP}}- или {{w |UDP}}-[[w:Порт (компьютерные сети)|порты]]. Например, если в предыдущем примере мы в каждое из правил
<sourcesyntaxhighlight lang="bash">
iptables -t nat -A PREROUTING -d 208.77.188.166 -j DNAT --to-destination 192.168.1.2
iptables -t nat -A POSTROUTING -d 192.168.1.2 -s 192.168.1.0/24 -j SNAT --to-source 192.168.1.1
iptables -t nat -A OUTPUT -d 208.77.188.166 -j DNAT --to-destination 192.168.1.2
</syntaxhighlight>
</source>
добавим
<sourcesyntaxhighlight lang="bash">
-p tcp --dport 80
</syntaxhighlight>
</source>
то мы «пробросим» не сам адрес 208.77.188.166, а только его {{w |TCP}}-[[w:Порт (компьютерные сети)|порт]] 80 ({{w |HTTP}}). Обращения на все остальные {{w |TCP}}- и {{w |UDP}}-порты, а также {{w |ICMP}}-пакеты, поступившие на адрес 208.77.188.166, будут обрабатываться шлюзом, и лишь входящие TCP-соединения на порт 80 будут передаваться на 192.168.1.2.
 
Строка 611:
 
* '''REDIRECT''' — подменяет номер порта в TCP- или UDP-пакете, а также подменяет адрес назначения на свой собственный. Например,
<sourcesyntaxhighlight lang="bash">
iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 80 -j REDIRECT --to-port 8080
</syntaxhighlight>
</source>
позволяет «завернуть» все исходящие из локальной сети TCP-соединения на порт 80, на TCP-порт 8080 шлюза. Если этот порт обслуживается специально настроенным {{w |прокси-сервер}}ом, то можно организовать «прозрачное проксирование» — клиенты из локальной сети даже не будут подозревать, что их запросы идут через прокси-сервер. Разумеется, входящие соединения из локалки на TCP-порт 8080 должны быть разрешены в таблице filter.
 
Другой полезный пример:
<sourcesyntaxhighlight lang="bash">
iptables -t nat -A PREROUTING -i eth0 -s 213.180.0.0/16 \
-p tcp --dport 80 -j REDIRECT --to-port 8000
</syntaxhighlight>
</source>
«заворачивает» все входящие из Интернета, а точнее подсети 213.180.0.0/255.255.0.0 TCP-соединения на порт 80, переадресуя их на порт 8000 того же сервера (где, например, может работать специальный веб-сервер). Как обычно, входящие соединения на TCP-порт 8000 должны быть разрешены в таблице filter.
 
* '''SAME''' — в зависимости от цепочки (PREROUTING или POSTROUTING) может работать как DNAT или SNAT. Однако, при указании (в параметре <tt>--to-ip</tt>) одного или нескольких диапазонов IP-адресов, определяет для каждого нового соединения подставляемый адрес не случайно, а базируясь на IP-адресе клиента. Таким образом, адрес для подмены остается постоянным для одного и того же клиента при повторных соединениях (что ''не'' выполняется для обычных DNAT/SNAT). В некоторых случаях это бывает важным. Примечание: в соответствии с iptables-extensions’s man page действие SAME убрано и заменено на опцию --persistent для действий '''DNAT'''/'''SNAT'''.
* '''NETMAP''' — позволяет «пробросить» целую сеть. Например, для шлюза, стоящего между сетями 192.168.1.0/24 и 192.168.2.0/24 можно организовать следующий проброс:
<sourcesyntaxhighlight lang="bash">
iptables -t nat -A PREROUTING -s 192.168.1.0/24 -d 192.168.3.0/24 -j NETMAP --to 192.168.2.2/24
</syntaxhighlight>
</source>
Теперь, при обращении, например, хоста 192.168.1.3 на IP-адрес 192.168.3.2 он будет попадать на 192.168.2.2 (при условии, что такой транзитный трафик разрешен на шлюзе в цепочке FORWARD таблицы filter, а также на хостах сети 192.168.1.0/24 наш шлюз [[w:Маршрутизация|прописан]] в качестве шлюза для подсети 192.168.3.0/24).
 
Строка 652:
* '''TARPIT''' — «подвесить» {{w |TCP}}-соединение. Используется лишь в самых крайних случаях, например, при борьбе с {{w |DoS-атака}}ми. Отвечает на входящее соединение, после чего уменьшает размер фрейма до нуля, блокируя возможность передачи данных. Соединение будет «висеть» в таком состоянии пока не истечет тайм-аут на атакующей стороне (обычно 20—30 минут). При этом на такое соединение расходуются системные ресурсы атакующей стороны (процессорное время и оперативная память), что может быть весьма ощутимо при значительном количестве соединений. В случае правильного использования действия TARPIT ресурсы атакуемой стороны практически не расходуются.
Под правильным применением понимается предотвращение обработки таких соединений подсистемой [[conntrack]], так как в противном случае будут расходоваться системные ресурсы самого атакуемого хоста. Например, перед добавлением правила блокирования порта
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -p tcp --dport 25 -j TARPIT
</syntaxhighlight>
</source>
обязательно добавляйте в таблицу raw соответствующее правило
<sourcesyntaxhighlight lang="bash">
iptables -t raw -I PREROUTING -p tcp --dport 25 -j NOTRACK
</syntaxhighlight>
</source>
предотвращающее обработку блокируемых соединений подсистемой conntrack.
* '''DELUDE''' — создать видимость открытого TCP-порта. На SYN-пакеты отвечает пакетами SYN/ACK, на все прочие пакеты отвечает RST. Очень полезно для введения в заблуждение злоумышленника, сканирующего порты вашего хоста.
Строка 697:
:* Отключить отслеживание соединения (опция <tt>--notrack</tt>). Таким образом, функциональность действия CT включает и функциональность NOTRACK, так что, возможно, действие NOTRACK будет объявлено устаревшим.
:* Задать вспомогательный модуль (conntrack helper) для данного соединения. Если раньше указать нестандартные порты для такого модуля можно было только в параметрах его загрузки (<tt>/etc/modprobe.conf</tt> или <tt>/etc/modprobe.d/netfilter.conf</tt>, например, <tt>options nf_conntrack_ftp ports=2121</tt>), то теперь это можно сделать и через правила netfilter:
<sourcesyntaxhighlight lang="bash">
iptables -t raw -I PREROUTING -p tcp --dport 2121 -j CT --helper ftp
</syntaxhighlight>
</source>
:* Ограничить список событий conntrack, которые будут генерироваться для данного соединения. Например, правило
<sourcesyntaxhighlight lang="bash">
iptables -t raw -I PREROUTING -p tcp --dport 8000 -j CT --ctevents new,destroy
</syntaxhighlight>
</source>
: будет предписывать для всех соединений на {{w |TCP}}-[[w:Порт (компьютерные сети)|порт]] 8000 генерировать только события «открытие соединения» (NEW) и «завершение соединения» (DESTROY), игнорируя все прочие события. Полный список возможных событий: new, related, destroy, reply, assured, protoinfo, helper, mark, natseqinfo, secmark.
:* Задать ''зону conntrack'' для данного пакета (параметр <tt>--zone</tt>). Механизм зон conntrack позволяет корректно отслеживать, фильтровать и {{w |NAT}}'ить соединения даже в том случае, если хост подключен к нескольким сетям, использующим одинаковые пространства имен, через различные интерфейсы (например, интерфейсы eth0 и eth1 подключены к двум разным сетям, но обе эти сети используют пространство 192.168.0.0/24). Традиционно, для идентификации соединений conntrack использует кортежи (tuples) — набор значений в который входят адреса и порты (в случае ICMP — типы и коды ICMP) источника и назначения при передаче данных в прямом и обратном направлении. Очевидно, что при наличии нескольких подсетей с одинаковыми адресными пространствами, возможно возникновение путаницы, когда сразу нескольким соединениям ставится в соответствие одна и та же запись в таблице соединений. Чтобы избежать такой ситуации, в кортеж был добавлен идентификатор зоны conntrack — целое число, которое можно устанавливать через специальное правило в таблице raw в зависимости от входящего/исходящего интерфейса (как уже говорилось выше, цепочки таблицы raw пакеты проходят еще до обработки их conntrack’ом). Например,
<sourcesyntaxhighlight lang="bash">
iptables -t raw -I PREROUTING -i eth1 -j CT --zone 1
iptables -t raw -I OUTPUT -o eth1 -j CT --zone 1
</syntaxhighlight>
</source>
: Таким образом, пакеты, входящие или исходящие через интерфейс eth1, будут получать идентификатор зоны 1, в то время как пакеты интерфейса eth0 будут по-прежнему использовать зону по умолчанию (идентификатор 0), и путаницы не произойдет.
: Кроме того, идентификатор зоны можно задать для каждого интерфейса через {{w |sysfs}}, минуя iptables (псевдофайл <tt>/sys/class/net/имя_интерфейса/nf_ct_zone</tt>):
<sourcesyntaxhighlight lang="bash">
echo 1 > /sys/class/net/eth1/nf_ct_zone
</syntaxhighlight>
</source>
: Однако, стоит заметить, что iptables/netfilter позволяют реализовать более гибкую логику управления идентификатором зоны, опирающуюся на различные параметры пакетов, и поэтом данный параметр действия CT может быть полезен при решении различных сложных задач управления трафиком при использовании зон conntrack.
* '''RAWDNAT''' — позволяет выполнять [[w:NAT|«проброс»]] адресов и портов «сырым» методом — без использования системы conntrack, то есть без учета состояний соединений. Это действие реализовано в рамках проекта xtables-addons. Применять его можно только в таблице raw. Имеет единственную опцию <tt>--to-destination ''адрес''[/''маска'']</tt>, по смыслу аналогичную опции <tt>--to</tt> действия NETMAP, то есть при указании маски заменяются только те биты в адресе, которые соответствуют единичным битам маски. Биты адреса, соответствующие нулевым битам маски, остаются неизменными. При отсутствии маски изменяется весь адрес.
 
: Отметим, что в отличие от действий [[#Таблица nat|таблицы nat]], которые работают только с соединениями в целом, операции «сырого» преобразования адресов работают только с отдельными пакетами, никак не учитывая контекст их передачи. Вышесказанное можно проиллюстрировать, скажем, таким простым примером: для элементарной операции «проброса» внешнего адреса на другой адрес при использовании обычных операций NAT достаточно одного правила<ref>На самом деле, для «чистого» проброса адреса, даже при использовании stateful NAT, необходимо обычно два или три правила, в частности, для проброса соединений с самого сервера в цепочке OUTPUT. Более подробно это описано при рассмотрении операции DNAT [[#Таблица nat|таблицы nat]], и здесь, с целью упрощения понимания примера, эти аспекты опущены</ref>
<sourcesyntaxhighlight lang="bash">
iptables -t nat -A PREROUTING -i eth0 -d 212.201.100.135 -j DNAT --to-destination 199.181.132.250
</syntaxhighlight>
</source>
: в то время как для той же операции при использовании «сырых» преобразований необходимо минимум два правила
<sourcesyntaxhighlight lang="bash">
iptables -t raw -A PREROUTING -i eth0 -d 212.201.100.135 -j RAWDNAT --to-destination 199.181.132.250
iptables -t rawpost -A POSTROUTING -o eth0 -s 199.181.132.250 -j RAWSNAT --to-source 212.201.100.135
</syntaxhighlight>
</source>
: Как уже говорилось выше, при использовании обычной операции DNAT в ответных пакетах, приходящих с пробрасываемого адреса (199.181.132.250), производится автоматическая (без необходимости писать для этого отдельные правила) подмена исходного адреса на изначальный (212.201.100.135), и обращающиеся по адресу 212.201.100.135 хосты даже не подозревают, что общаются с кем-то другим. В случае же «сырого» преобразования такая автоматическая обработка невозможна, так как она требует возможности проверять пакеты на принадлежность соединению. С другой стороны, операции «сырого» преобразования являются более гибким инструментом, а также могут работать даже с UNTRACKED и INVALID-пакетами.
 
Строка 741:
==== Действия ====
В таблице rawpost можно использовать действие '''RAWSNAT''', выполняющее операцию «сырой» подмены исходного адреса. Имеет единственную опцию <tt>--to-source ''адрес''[/''маска'']</tt>, позволяющую задать новый исходный адрес для обрабатываемых пакетов. Как обычно, при наличии маски, в обрабатываемых адресах изменяются только те биты, которые в маске установлены в единицу, при отсутствии маски адрес изменяется целиком. Например, если по правилу
<sourcesyntaxhighlight lang="bash">
iptables -t rawpost -A POSTROUTING -o eth0 -s 10.125.0.0/16 -d 172.18.1.100 -j RAWSNAT --to-source 192.168.0.20/16
</syntaxhighlight>
</source>
будет обработан пакет с исходным адресом 10.125.32.28, то этот адрес будет заменен на 192.168.32.28.
 
Если же при задании правила маску /16 в параметре <tt>--to-source</tt> опустить, адрес будет заменен на 192.168.0.20. Но так лучше не делать, потому что мы выполняем трансляцию для всех пакетов из подсети 10.125.0.0/16, и без отслеживания соединений не сможем отличить, какие пакеты следует вернуть 10.125.32.28, а какие — другим хостам из этой подсети. Сама же операция «возвращения» пакетов требует отдельного RAWDNAT-правила:
<sourcesyntaxhighlight lang="bash">
iptables -t raw -A PREROUTING -i eth0 -s 172.18.1.100 -d 192.168.0.0/16 -j RAWDNAT --to-destination 10.125.0.0/16
</syntaxhighlight>
</source>
которое выполняет обратную подмену, заменяя адреса назначения в пакетах-ответах. Таким образом, хост 172.18.1.100 будет считать, что к нему обращаются хосты из подести 10.125.0.0/16, а вовсе не из 192.168.0.0/16.
 
Для сравнения, с использованием stateful-преобразования достаточно одного правила
<sourcesyntaxhighlight lang="bash">
iptables -t nat -A POSTROUTING -o eth0 -s 10.125.0.0/16 -d 172.18.1.100 -j NETMAP --to 192.168.0.0/16
</syntaxhighlight>
</source>
которое будет обеспечивать все необходимые преобразования адресов.
 
Строка 782:
 
Определяет адрес отправителя. В качестве адреса может выступать {{w |IP-адрес}} (возможно с [[w:Маска подсети|маской]]), имя хоста из /etc/hosts, или {{w |доменное имя}} (в последних двух случаях перед добавлением правила в цепочку имя [[w:DNS|резольвится]] в IP-адрес). Маска подсети может быть указана в классическом формате (например, 255.255.0.0) либо в формате {{w |CIDR}} (например, 16)<ref>Заметим, что для расчета масок подсетей и диапазонов адресов существует очень удобная утилита [[ipcalc]].</ref>. Например,
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -i eth0 -s 192.168.0.0/16 -j DROP
</syntaxhighlight>
</source>
(где eth0 — интерфейс, подключенный к интернету), позволяет заблокировать простейший вид {{w |спуфинг}}а — из интернета ''не могут'' приходить пакеты с обратным адресом, принадлежащим к диапазону, зарезервированному для локальных сетей.
 
Начиная с версии iptables 1.4.6, в одном параметре <tt>-s</tt> можно указывать более одного адреса, разделяя адреса запятой. При этом для каждого адреса будет добавлено ''отдельное'' правило. Например, запись
<sourcesyntaxhighlight lang="bash">
iptables -A INPUT -i eth0 -s 192.168.0.0/16,172.16.0.0/12,10.0.0.0/8 -j DROP
</syntaxhighlight>
</source>
эквивалентна записи
<sourcesyntaxhighlight lang="bash">
iptables -A INPUT -i eth0 -s 192.168.0.0/16 -j DROP
iptables -A INPUT -i eth0 -s 172.16.0.0/12 -j DROP
iptables -A INPUT -i eth0 -s 10.0.0.0/8 -j DROP
</syntaxhighlight>
</source>
При добавлении через команду <tt>-A</tt> (вставка в конец цепочки) порядок добавляемых правил будет соответствовать порядку перечисления адресов в исходной команде, при использовании команды <tt>-I</tt> (вставка в начало цепочки либо после заданного правила) порядок будет обратным исходному.
 
Строка 806:
 
Заметим, что, если использовать возможность указания нескольких адресов (перечислив их явно или указав доменное имя, резольвящееся на несколько адресов) ''одновременно'' в параметрах <tt>-s</tt> и <tt>-d</tt>, то на каждое возможное сочетание адресов будет добавлено свое правило. Например, запись
<sourcesyntaxhighlight lang="bash">
iptables -A FORWARD -s 192.168.1.1,192.168.1.2 -d 192.168.1.3,192.168.1.4 -j REJECT
</syntaxhighlight>
</source>
эквивалентна
<sourcesyntaxhighlight lang="bash">
iptables -A FORWARD -s 192.168.1.1 -d 192.168.1.3 -j REJECT
iptables -A FORWARD -s 192.168.1.1 -d 192.168.1.4 -j REJECT
iptables -A FORWARD -s 192.168.1.2 -d 192.168.1.3 -j REJECT
iptables -A FORWARD -s 192.168.1.2 -d 192.168.1.4 -j REJECT
</syntaxhighlight>
</source>
 
* <tt>[!] '''-i'''</tt>, <tt>--in-interface ''имя_интерфейса''</tt>
 
Определяет входящий сетевой интерфейс. Если указанное имя интерфейса заканчивается знаком «+» (например, <tt>tun+</tt>), то критерию соответствуют все интерфейсы, чьи названия начинаются на указанное имя (для нашего примера tun0, tun1, …). Данный критерий можно использовать в цепочках PREROUTING, INPUT и FORWARD. Например,
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -i lo -j ACCEPT
</syntaxhighlight>
</source>
позволит принимать весь трафик, входящий через интерфейс [[w:Loopback|обратной петли]].
 
Строка 828:
 
Определяет исходящий сетевой интерфейс. Синтаксис аналогичен <tt>-i</tt>. Критерий можно использовать в цепочках FORWARD, OUTPUT и POSTROUTING. Вспоминая один из наших примеров,
<sourcesyntaxhighlight lang="bash">
iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE
</syntaxhighlight>
</source>
будет [[w:Маскарадинг|маскарадить]] весь трафик, исходящий через интерфейс eth0.
 
Строка 841:
 
Позволяет указать исходящий порт (или их диапазон). Например,
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -m conntrack --ctstate NEW -p tcp --sport 0:1023 -j DROP
</syntaxhighlight>
</source>
заблокирует все входящие соединения с [[w:Список портов TCP и UDP|привилегированных]] портов (''привилегированными'' в TCP/UDP считаются порты с 0 по 1023 включительно, так как для их использования нужны привилегии [[w:Root|суперпользователя]], и обычно такие порты используются [[w:Демон (программа)|демонами]] только в режиме прослушивания).
 
Строка 849:
 
Позволяет указать порт назначения (или их диапазон). Синтаксис аналогичен <tt>--sport</tt>. Например,
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -p tcp --dport 80 -j ACCEPT
</syntaxhighlight>
</source>
разрешит все входящие пакеты на 80 порт ({{w |HTTP}}).
 
Строка 859:
 
Пример:
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -p tcp --tcp-flags SYN,RST,ACK,FIN SYN -j LOG --log-level DEBUG --log-prefix "TCP SYN: "
</syntaxhighlight>
</source>
будет заносить в лог все входящие TCP SYN-пакеты. У обычного SYN-пакета всегда установлен флаг SYN и сняты флаги RST, ACK и FIN.
 
Другой пример:
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -m conntrack --ctstate NEW,INVALID -p tcp --tcp-flags SYN,ACK SYN,ACK -j REJECT --reject-with tcp-reset
</syntaxhighlight>
</source>
будет препятствовать {{w |спуфинг}}у от нашего имени. Ведь если мы получаем пакет с установленными флагами SYN и ACK (такой комбинацией флагов обладает только ответ на SYN-пакет) по еще не открытому соединению, это означает, что кто-то послал другому хосту SYN-пакет от нашего имени, и ответ пришел к нам. Однако добавление такого правила в конфигурацию фаервола ''не рекомендуется'', поскольку стандартом также предусмотрено использование случайного начального номера последовательности для каждого нового TCP-соединения, как раз для предотвращения возможности спуфинг-атаки. Шансов угадать начальный номер у злоумышленника практически нет, тогда как наш ответ RST-пакетом на такой пакет может серьезно нарушить работоспособность некоторых систем защиты от DoS-атак, полагающихся на метод, называемый out-of-sequence ACK. В случае добавления этого правила в конфигурацию фаервола, системы защиты будут считать, что, раз уж мы отвечаем RST на out-of-sequence ACK, вы действительно пытались установить соединение с ресурсом, и пропустят все последующие поддельные пакеты злоумышленника, отправленные от вашего имени. В результате вы заметно облегчите злоумышленнику осуществление DoS-атаки от вашего имени, и при расследовании этого эпизода следы приведут к вам.
 
Строка 873:
 
Позволяет отлавливать TCP SYN-пакеты (сокращение для <tt>--tcp-flags SYN,RST,ACK,FIN SYN</tt>). Так что приведенный выше пример про логгирование SYN-пакетов можно записать в виде
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -p tcp --syn -j LOG --log-level DEBUG --log-prefix "TCP SYN: "
</syntaxhighlight>
</source>
 
Более интересный пример:
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -m conntrack --ctstate NEW -p tcp ! --syn -j DROP
</syntaxhighlight>
</source>
будет блокировать все попытки открыть входящее TCP-соединение ''не'' SYN-пакетом. Попытка установить соединение таким образом может быть либо ошибкой, либо атакой.
 
Строка 908:
 
Позволяет анализировать набор [[:en:SCTP packet structure|секций (chunks)]], входящих в состав SCTP-пакета. Возможные типы секций: DATA INIT INIT_ACK SACK HEARTBEAT HEARTBEAT_ACK ABORT SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECN_ECNE ECN_CWR SHUTDOWN_COMPLETE ASCONF ASCONF_ACK. Также, для секций, имеющих флаги (DATA, ABORT и SHUTDOWN_COMPLETE) можно проверить состояние флагов, указав соответствующие буквы после названия секции (для DATA — U, B и E, для ABORT и SHUTDOWN_COMPLETE — T). Литера в верхнем регистре предполагает установленный флаг, в нижнем — снятый. Ключевое слово в начале позволяет определить логику работы: в случае <tt>all</tt> должны присутствовать ''все'' перечисленные секции, для <tt>any</tt> — хотя бы одна из них, для <tt>only</tt> — пакет должен состоять только из перечисленных секций. Примеры:
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -p sctp --chunk-types only DATA,INIT -j DROP
</syntaxhighlight>
</source>
заблокирует все входящие SCTP-пакеты, состоящие только из секций DATA и INIT, а
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -p sctp --chunk-types any DATA:Be -j ACCEPT
</syntaxhighlight>
</source>
разрешит входящие SCTP-пакеты, содержащие секцию DATA с установленным флагом B и снятым флагом E, то есть первый фрагмент фрагментированной DATA-секции.
 
Строка 929:
 
Позволяет указать тип DCCP-пакета. В ''маске'' через запятую перечисляются DCCP-типы (допустимые названия: REQUEST RESPONSE DATA ACK DATAACK CLOSEREQ CLOSE RESET SYNC SYNCACK INVALID). Пакет считается удовлетворяющим критерию, если имеет один из типов, перечисленных в маске. Например,
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -p dccp --dccp-types RESET,INVALID -j LOG --log-level DEBUG --log-prefix "DCCP RESET or INVALID: "
</syntaxhighlight>
</source>
занесет в лог все входящие RESET и INVALID DCCP-пакеты.
 
Строка 945:
 
Проверка фрагментации: критерию соответствуют только фрагменты пакета, начиная со второго фрагмента. Пример:
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -p icmp -f -j DROP
</syntaxhighlight>
</source>
блокирует фрагменты {{w |ICMP}}-пакетов. Так как, в силу функционального назначения протокола, ICMP-пакеты должны быть очень небольшими и нормально укладываться в {{w |MTU}}, наличие их фрагментов обычно свидетельствует об ошибке или попытке атаки.
 
Строка 965:
 
В качестве практического примера использования данного критерия можно привести простейшую защиту от {{w |спуфинг}}а:
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -m addrtype --src-type LOCAL ! -i lo -j DROP
</syntaxhighlight>
</source>
Это правило заблокирует пакеты, которые пришли с внешних интерфейсов, но при этом в качестве обратного адреса у них указан один из адресов, принадлежащих нашему хосту (например, {{w |127.0.0.1}}).
 
Строка 991:
<tt>[!] '''--icmp-type''' ''тип''</tt> — обеспечивает проверку [[w:ICMP#Типы ICMP пакетов (полный список)|типа]] ICMP-пакета. Список возможных типов выводится по команде <tt>iptables -p icmp -h</tt>. Также можно указать стандартные числовые тип и, при
необходимости, код. Например,
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -p icmp --icmp-type echo-request -j ACCEPT
</syntaxhighlight>
</source>
как и аналогичное
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -p icmp --icmp-type 8 -j ACCEPT
</syntaxhighlight>
</source>
пропустят все входящие ICMP-эхо-запросы (пинги).
 
Строка 1004:
 
Особо отметим, что критерии для некоторых подзаголовков IPv6 (dst, frag, hbh, rt) корректно вызываются ''только'' с использованием синтаксиса для расширенных критериев (ключ <tt>-m</tt>), хотя имеют номера протоколов в <tt>/etc/protocols</tt> и, формально, могут быть вызваны с использованием синтаксиса для протоколов (ключ <tt>-p</tt>) что, однако, является неверным. В таких случаях ip6tables выдает предупреждение вида
<sourcesyntaxhighlight lang="text">
Warning: never matched protocol: ipv6-frag. use extension match instead.
</syntaxhighlight>
</source>
гласящее, что в такое правило никогда не будет срабатывать. Если вы видите подобное предупреждение, удалите правило, которое его вызвало, и перепишите это правило с использованием корректного синтаксиса (<tt>-m</tt>).
 
Строка 1062:
 
по смыслу и синтаксису аналогичный параметру <tt>--icmp-type</tt> критерия icmp (см. предыдущий раздел). Например,
<sourcesyntaxhighlight lang="bash">
ip6tables -I INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT
</syntaxhighlight>
</source>
как и аналогичное
<sourcesyntaxhighlight lang="bash">
ip6tables -I INPUT -p icmpv6 --icmpv6-type 128 -j ACCEPT
</syntaxhighlight>
</source>
пропустит входящие ICMPv6-эхо-запросы (пинги).
 
Как и в случае icmp, вы можете получить список допустимых типов сообщений, введя
<sourcesyntaxhighlight lang="bash">
ip6tables -p icmpv6 -h
</syntaxhighlight>
</source>
 
===== [[MH]] =====
Строка 1082:
 
позволяющую указать точное значение типа или диапазон допустимых значений. Перечень поддерживаемых типов MH можно получить, введя команду
<sourcesyntaxhighlight lang="bash">
ip6tables -p mh -h
</syntaxhighlight>
</source>
 
Также к данному критерию допустимо обращение по названию ipv6-mh (<tt>-p ipv6-mh</tt>).
Строка 1100:
 
В большинстве конфигураций IPsec-пакеты просто пропускаются фаерволом (пусть с ними разбирается IPsec-подсистема):
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -p ah -j ACCEPT
iptables -I INPUT -p esp -j ACCEPT
</syntaxhighlight>
</source>
 
=== Критерии состояния соединения ===
Строка 1122:
:* '''RELATED''' — пакет открывает новое соединение, логически связанное с уже установленными, например, открытие канала данных в пассивном режиме {{w |FTP}}.
Например:
<sourcesyntaxhighlight lang="bash">
modprobe nf_conntrack_ftp # Подгружаем модуль для распознавания связанных FTP-соединений
# В старых системах этот модуль может называться ip_conntrack_ftp
Строка 1131:
iptables -P INPUT DROP # В качестве действия по умолчанию устанавливаем DROP — блокирование пакета
iptables -P OUTPUT ACCEPT # Разрешаем все исходящие пакеты
</syntaxhighlight>
</source>
Этот набор команд обеспечит функционирование на хосте FTP-сервера с поддержкой как активного, так и пассивного режимов. В пассивном режиме FTP клиент сначала устанавливает управляющее соединение на TCP-порт 21 сервера. Когда возникнет необходимость передачи данных, клиент даст серверу команду PASV. Сервер выберет высокий порт и подключится к нему в режиме прослушивания, отправив номер порта клиенту по управляющему соединению. Система [[conntrack]], при наличии подгруженного модуля ядра nf_conntrack_ftp, зафиксирует это сообщение и выделит номер порта. Когда клиент откроет соединение данных на этот высокий порт, система conntrack присвоит первому пакету статус RELATED, в результате чего пакет пройдет по нашему правилу и будет принят. Остальные пакеты в этом соединении будут иметь уже статус ESTABLISHED и тоже будут приняты.
 
Строка 1139:
 
:* '''INVALID''' — пакет по смыслу должен принадлежать уже установленному соединению (например, ICMP-сообщение port-unreachable), однако такое соединение в системе не зарегистрировано. Обычно к таким пакетам применяют действие DROP:
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -m conntrack --ctstate INVALID -j DROP
</syntaxhighlight>
</source>
 
:* '''UNTRACKED''' — отслеживание состояния соединения для данного пакета было отключено. Обычно оно отключается с помощью действия NOTRACK в таблице raw.
Строка 1181:
 
Однако для соединений, к которым применена трансляция адресов или портов, это может не выполняться. Например, рассмотрим ситуацию, когда клиент из нашей подсети (192.168.1.2) обращается к некоторому серверу в интернете (204.152.191.37) через наш сервер (внешний адрес нашего сервера 208.77.188.166, внутренний 192.168.1.1). Для корректной работы такой схемы нужно обеспечить на нашем сервере подмену исходного адреса (SNAT), например,
<sourcesyntaxhighlight lang="bash">
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 208.77.188.166
</syntaxhighlight>
</source>
Тогда, с точки зрения системы conntrack нашего сервера,
: → В прямом направлении (от инициатора к отвечающему):
Строка 1195:
 
Аналогично, если один из внешних адресов нашего сервера «проброшен» на внутренний сервер, например, так
<sourcesyntaxhighlight lang="bash">
iptables -t nat -A PREROUTING -d 208.77.188.166 -j DNAT --to-destination 192.168.1.2
</syntaxhighlight>
</source>
то при обращении на этот адрес клиента извне (допустим, все тот же 204.152.191.37), получим
: → В прямом направлении (от инициатора к отвечающему):
Строка 1235:
 
Справочную информацию по любому из дополнительных критериев можно получить, используя команду
<sourcesyntaxhighlight lang="bash">
iptables -m название_критерия -h
</syntaxhighlight>
</source>
 
==== Вспомогательные критерии ====
Строка 1247:
 
<tt>[!] --dports</tt>, <tt>--destination-ports ''порт''[:''порт''][,''порт''[:''порт''][,...]]</tt> — улучшенная версия критерия <tt>--dport</tt>, описанного выше. Например,
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -p tcp -m multiport --dports 80,8000:8008 -j ACCEPT
</syntaxhighlight>
</source>
позволит принимать TCP-пакеты, приходящие на порты 80 и с 8000 по 8008.
 
Строка 1257:
 
<tt>[!] --src-range ''адрес''[-''адрес'']</tt> — позволяет указать диапазон исходных адресов. Например,
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -m iprange --src-range 192.168.0.8-192.168.0.25 -j DROP
</syntaxhighlight>
</source>
заблокирует все пакеты, исходный адрес которых лежит в диапазоне с 192.168.0.8 по 192.168.0.25 включительно.
 
Строка 1268:
 
<tt>[!] --mark ''значение''[/''маска'']</tt> — указывает значение маркировки. Простейший пример:
<sourcesyntaxhighlight lang="bash">
-m mark --mark 15
</syntaxhighlight>
</source>
будет выделять пакеты с маркировкой 15.
 
Если указана маска, то перед сравнением с заданным значением маркировка каждого пакета комбинируется с этой маской посредством логической операции [[w:Конъюнкция|AND]], то есть проверяется условие <tt>x & ''маска'' == ''значение''</tt> (где x — маркировка текущего пакета). Такой подход позволит сравнивать значения отдельных {{w |бит}}. Например, критерию
<sourcesyntaxhighlight lang="bash">
-m mark --mark 64/64
</syntaxhighlight>
</source>
будет отлавливать пакеты, в маркировке которых установлен 7-й бит (<math>2^6=64</math>, при этом первый бит соответствует <math>2^0</math>). В частности, 64…127, 192…255, 320…383 и т. д.
 
Еще один пример —
<sourcesyntaxhighlight lang="bash">
-m mark --mark 2/3
</syntaxhighlight>
</source>
будет определять пакеты, в маркировке которых установлен второй бит, но снят первый. Такие числа будут нацело делиться на два, но не делиться на четыре — 2, 6, 10, 14, …
 
Строка 1302:
 
Для начала, запустим демон l7-filter-userspace. Небольшое замечание: в его конфигурационном файле (назовем его, например, <tt>l7-filter.conf</tt>) будем помечать протоколы метками в диапазоне от 16 до 31 включительно (почему — станет понятно из дальнейших пояснений).
<sourcesyntaxhighlight lang="bash">
l7-filter -f /etc/l7-filter.conf -q 2 -m 0x1f
</syntaxhighlight>
</source>
Параметр <tt>-f</tt> указывает путь к конфигурационному файлу, <tt>-q</tt> — номер очереди, <tt>-m</tt> — задает биты маркировки, модифицируемые демоном l7-filter (в нашем случае — с первого по пятый, что соответствует диапазону значений маркировки от 0 до 31).
 
Далее, добавим правила, направляющие весь входящий и исходящий трафик (кроме локального) на анализ демону l7-filter:
<sourcesyntaxhighlight lang="bash">
iptables -t mangle -F # На всякий случай очищаем таблицу mangle
# Направляем на анализ входящий трафик, включая транзитный
Строка 1314:
# Направляем на анализ исходящий трафик, кроме транзитного
iptables -t mangle -A OUTPUT ! -o lo -j NFQUEUE --queue-num 2
</syntaxhighlight>
</source>
Добавлять второе из этих правил в цепочку POSTROUTING не стоило — ведь в нее попадает как трафик, исходящий от самого хоста, так и транзитный трафик, который уже был обработан ранее, в цепочке PREROUTING. Посмотрев на диаграмму выше, вы можете убедиться, что приведенные правила обрабатывают весь трафик, как принадлежащий самому хосту, так и транзитный, за исключением локального. Локальный трафик (идущий через интерфейс [[w:Loopback|lo]]), бессмысленно шейпить, а значит, не стоит и классифицировать.
 
Теперь добавим правила, обеспечивающие копирование маркировки пакетов в маркировку соединений и обратно:
<sourcesyntaxhighlight lang="bash">
# Для входящего трафика (кроме транзитного)
# Копируем маркировку пакетов в маркировку соединений
Строка 1327:
iptables -t mangle -A POSTROUTING -m mark --mark 0x10/0xfffffff0 -j CONNMARK --save-mark
iptables -t mangle -A POSTROUTING -m connmark --mark 0x10/0xfffffff0 -j CONNMARK --restore-mark
</syntaxhighlight>
</source>
Поясним два момента. Во-первых, добавление этих правил в цепочки PREROUTING и OUTPUT, сразу после правил, передающих трафик демону l7-filter, не имеет смысла — после обработки пакетов демон [http://l7-filter.sourceforge.net/HOWTO-userspace#Does применяет] к ним действие ACCEPT, прекращающее обработку пакета в рамках исходных цепочек. Поэтому мы добавляем эти правила в цепочки, идущие «ниже по течению». Как вы можете заметить по диаграмме выше, такая комбинация правил также обеспечивает обработку всего трафика.
 
Строка 1342:
 
Для начала, создадим для каждого провайдера свою таблицу маршрутизации
<sourcesyntaxhighlight lang="bash">
echo -e "\n110\tstatic\n111\tprov1\n112\tprov2\n113\tprov3" >> /etc/iproute2/rt_tables
</syntaxhighlight>
</source>
 
Этот код добавит в конец файла <tt>/etc/iproute2/rt_tables</tt> строки
Строка 1356:
 
Далее, сделаем каждого провайдера шлюзом по умолчанию в «своей» таблице:
<sourcesyntaxhighlight lang="bash">
ip route add default via 208.77.188.1 dev eth0 table prov1
ip route add default via 208.77.189.1 dev eth1 table prov2
ip route add default via 208.77.190.1 dev eth2 table prov3
</syntaxhighlight>
</source>
 
Добавим для каждой таблицы правило, отправляющее в нее пакеты с соответствующей маркировкой:
<sourcesyntaxhighlight lang="bash">
ip rule add fwmark 1 table prov1
ip rule add fwmark 2 table prov2
Строка 1369:
# Но прежде всего пакеты должны пройти таблицу static
ip rule add table static prio 1
</syntaxhighlight>
</source>
 
Таблица static предназначена для обслуживания статических маршрутов. В частности, в нее мы занесем подсети провайдеров (предположим, что все они [[w:Бесклассовая адресация|класса 1C]]), а также наши внутренние локальные сети (если таковые есть):
<sourcesyntaxhighlight lang="bash">
# Провайдеры
ip route add 208.77.188.0/24 dev eth0 table static
Строка 1382:
# Сбрасываем кеш маршрутов
ip route flush cache
</syntaxhighlight>
</source>
Таким образом, если нашему хосту нужно будет обратиться в подсеть провайдера prov2 (208.77.189.0/24), то маршрут пойдет сразу через интерфейс eth1. Также в этой таблице присутствуют маршруты для наших внутренних локальных сетей — с ними тоже все просто.
 
Строка 1390:
 
Далее, отключим статическую антиспуфинговую фильтрацию:
<sourcesyntaxhighlight lang="bash">
sysctl net.ipv4.conf.all.rp_filter=0
</syntaxhighlight>
</source>
Reverse path filtering — штука, конечно, удобная и полезная но, к сожалению, совершенно не совместимая с динамической маршрутизацией.
 
Если вы планируете использовать этот компьютер не только как [[w:Сетевой шлюз|шлюз]], и но и как интернет-{{w |сервер}} (то есть предоставлять доступ к нему извне), необходимо выполнить привязку входящих соединений к их интерфейсам — в противном случае могут возникнуть проблемы, если обращение извне придет через одного провайдера, а сервер ответит через другого.
<sourcesyntaxhighlight lang="bash">
iptables -t mangle -N bind_connect # Создаем отдельную цепочку (для простоты управления)
# Следите за правильным соответствием значений меток и интерфейсов!
Строка 1404:
# Пропускаем через эту процедуру все новые соединения к нашему серверу
iptables -t mangle -I INPUT -m conntrack --ctstate NEW -j bind_connect
</syntaxhighlight>
</source>
Теперь, в сочетании с операцией <tt>-j CONNMARK --restore-mark</tt>, которой мы подвергнем исходящий с нашего сервера трафик (см. ниже), эта процедура обеспечит корректную обработку входящих соединений. (Отметим, что, если бы нам не нужно было бы балансировать исходящие соединения, мы могли бы обойтись вообще без помощи iptables/netfilter, выполнив привязку входящих соединений через правила вида <tt>ip rule add from 208.77.188.100 table prov1</tt> и т. п. — ответные пакеты всегда уходят с того же адреса, на который пришел запрос, так что в качестве критерия для выбора шлюза можно использовать исходный адрес.)
 
Ввиду того, что выбор исходящего адреса для каждого нового соединения осуществляется на основании правил статической маршрутизации (таблица main), могут возникнуть ошибки. Например, если в маршруте по умолчанию (default) в таблице main указан интерфейс eth0, то все исходящие от нас во внешнюю сеть (интернет) соединения будут иметь в качестве исходного адреса первый адрес интерфейса eth0, и ответные пакеты пойдут именно на этот интерфейс. Чтобы избежать возникновения таких ситуаций, добавим {{w |маскарадинг}} для всех исходящих соединений (предполагается, что у всех провайдеров наши внешние адреса имеют вид 208.77.x.100):
<sourcesyntaxhighlight lang="bash">
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 208.77.188.100
iptables -t nat -A POSTROUTING -o eth1 -j SNAT --to-source 208.77.189.100
iptables -t nat -A POSTROUTING -o eth2 -j SNAT --to-source 208.77.190.100
</syntaxhighlight>
</source>
 
А теперь — самое интересное. Используя описанный ниже критерий statistic, мы будем случайно распределять метки между пакетами.
<sourcesyntaxhighlight lang="bash">
iptables -t mangle -N select_prov # Создаем для этого специальную цепочку
iptables -t mangle -A select_prov -j CONNMARK --set-mark 1 # Ставим всем соединениям маркировку 1
Строка 1422:
iptables -t mangle -A select_prov -m statistic --mode random --probability 0.5 -j RETURN # С вероятностью 50% выходим из этой цепочки
iptables -t mangle -A select_prov -j CONNMARK --set-mark 3 # Всем, кто дошел досюда, ставим маркировку 3
</syntaxhighlight>
</source>
Первое правило в этой цепочке пройдут все пакеты, вошедшие в нее. После этого, 34 % (примерно треть из них) покинет цепочку согласно второму правилу. Далее, оставшиеся пакеты (66 % от первоначального количества) получат маркировку 2. После этого половина из них (то есть 33 % от начального) покинут цепочку с этой маркировкой. Оставшаяся половина (тоже 33 % от начального количества) получат маркировку 3.
 
После этого, создадим вспомогательную цепочку, осуществляющую маркировку соединений и пакетов:
<sourcesyntaxhighlight lang="bash">
iptables -t mangle -N sort_connect
iptables -t mangle -A sort_connect -o lo -j RETURN # Локальным соединениям балансировка не нужна
Строка 1434:
iptables -t mangle -A sort_connect -m conntrack --ctstate NEW -j select_prov # Все новые пакеты прогоняем через процедуру случайного выбора
iptables -t mangle -A sort_connect -j CONNMARK --restore-mark # Копируем маркировку соединений на пакеты
</syntaxhighlight>
</source>
Через цепочку <tt>select_prov</tt> мы прогоняем только новые пакеты, то есть первые пакеты каждого соединения. После этой процедуры соединение уже имеет маркировку. К сожалению, на данный момент роутинговая подсистема ядра [[w:Ядро Linux|Linux]] не умеет маршрутизировать пакеты на основании маркировки соединения — только на основании маркировки пакетов. Поэтому действием <tt>CONNMARK --restore-mark</tt> мы копируем маркировку соединений в маркировку пакетов.
 
Осталось только добавить вызов этой цепочки в таблицу mangle:
<sourcesyntaxhighlight lang="bash">
iptables -t mangle -I OUTPUT -j sort_connect
</syntaxhighlight>
</source>
Теперь все ваши исходящие соединения будут балансироваться согласно описанным правилам.
 
Аналогичную функциональность можно реализовать и для транзитных соединений. Для этого достаточно добавить вызов <tt>sort_connect</tt> в цепочку FORWARD таблицы mangle:
<sourcesyntaxhighlight lang="bash">
iptables -t mangle -I FORWARD -j sort_connect
</syntaxhighlight>
</source>
 
Разумеется, при этом должна быть разрешена передача транзитного трафика, как в таблице filter, так и на уровне sysctl. Как это делается — см. выше.
 
Заметим, что кроме алгоритма случайной балансировки, критерий statistic позволяет реализовать балансировку в режим [[w:Round-robin (алгоритм)|round robin]]. Для этого поменяем цепочку <tt>select_prov</tt> следующим образом:
<sourcesyntaxhighlight lang="bash">
iptables -t mangle -F select_prov # Очищаем ее
iptables -t mangle -A select_prov -j CONNMARK --set-mark 1 # Ставим всем соединениям маркировку 1
Строка 1458:
iptables -t mangle -A select_prov -m statistic --mode nth --every 2 -j RETURN # Один из оставшихся двух - выходим
iptables -t mangle -A select_prov -j CONNMARK --set-mark 3 # Последний
</syntaxhighlight>
</source>
 
Подробнее о принципах работы критерия statistic см. ниже.
 
В завершение нашего обсуждения стоит заметить, что в некоторых случаях для балансировки соединений достаточно единственной команды
<sourcesyntaxhighlight lang="bash">
ip route add default scope global nexthop via 208.77.188.1 dev eth0 weight 1 nexthop via 208.77.189.1 dev eth1 weight 1 # И т.д.
</syntaxhighlight>
</source>
Стоит обратить особое внимание на тот факт, что данный метод балансирует ''пакеты'', а не соединения. Например, в том случае, если вы хотите организовать выход в интернет из локальной сети через нескольких провайдеров, не имея единого внешнего адреса, этот метод может работать некорректно — часть пакетов пройдет через одного провайдера и после операции {{w |NAT}} получит один исходный адрес, часть пойдет через другого и соответственно получит другой адрес, и в результате удаленные хосты не смогут правильно обрабатывать соединения, исходящие из вашей сети. Однако, в большинстве случаев этот негативный эффект нивелируется другим фактором — кэшированием маршрутов. При прохождении через такое правило серии пакетов, адресованных некоторому хосту, действительно случайным выбор будет только для первого из них, после чего выбранный маршрут будет закэширован, и остальные пакеты к этому хосту будут маршрутизироваться через тот же шлюз. С одной стороны, подобный эффект позволяет соединениям корректно функционировать, с другой стороны — балансировка оказывается не такой уж и случайной. К тому же, после очистки кэша маршрутов (например, посредством ввода команды <tt>ip route flush cached</tt>), работа существующих на этот момент соединений может быть нарушена. В качестве наиболее безопасного и целесообразного применения описанного метода можно привести задачу балансировки транзитного трафика в сетях без NAT (условия «прямой видимости» между балансирующим маршрутизатором и точкой схождения потоков трафика). В том случае, если доступ к шлюзам осуществляется через один сетевой интерфейс, этим методом можно балансировать и соединения, исходящие от самого хоста.
 
Строка 1481:
Критерий -limit использует модель «дырявого ведра», и --limit-burst задает «объем ведра», а --limit — «скорость вытекания». Каждому такому критерию соответствует своя очередь, длина которой задается параметром --limit-burst. Если в очереди есть пакеты, то со скоростью, заданной в --limit, они покидают очередь и считаются удовлетворяющими критерию. Если же вся очередь занята, то новые пакеты в ней не регистрируются и считаются ''не'' удовлетворяющими критерию.
Например,
<sourcesyntaxhighlight lang="bash">
iptables -I INPUT -m limit --limit 3/min --limit-burst 5 -j LOG --log-level debug --log-prefix "INPUT packet: "
</syntaxhighlight>
</source>
предполагает очередь на пять пакетов, которая «продвигается» со скоростью 3 пакета в минуту. При непрерывном поступлении входящих пакетов, очередь всегда будет заполнена, и в лог будут заноситься в среднем по три пакета в минуту. Однако, если входящих пакетов долго не будет, то очередь успеет очиститься, и при поступлении пяти и менее новых пакетов, они пойдут в лог подряд. В любом случае, скорость попадания пакетов в лог остается неизменной.
 
Типичная ошибка новичков — использовать limit для ограничения TCP-трафика, например, так:
<sourcesyntaxhighlight lang="bash">
iptables -A INPUT -p tcp --dport 80 -m limit --limit 10000/sec --limit-burst 10000 -j ACCEPT
iptables -P INPUT DROP
</syntaxhighlight>
</source>
Это пример попытки защитить {{w |web-сервер}} от [[w:DoS-атака|DDoS-атаки]], ограничив количество пакетов в единицу времени. Однако, это правило не помешает без особого труда завалить сервер запросами (считая, что на один запрос требуется два входящих пакета — SYN-пакет и пакеты данных, содержащий, например, только GET /, согласно спецификации HTTP 0.9). При этом могут возникнуть помехи для легальных пользователей, например, загружающих на сервер большой файл методом POST. Более корректным решением будет ограничивать не скорость входящего потока данных, а скорость открытия новых соединений:
<sourcesyntaxhighlight lang="bash">
iptables -A INPUT -p tcp --dport 80 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW -m limit --limit 32/sec --limit-burst 32 -j ACCEPT
iptables -P INPUT DROP
</syntaxhighlight>
</source>
Теперь мы ограничиваем количество не всех пакетов, а только новых, то есть мы разрешаем открывать не более 32 новых соединений в секунду. Впрочем, число 32 приведено здесь только для примера. Конкретное значение скорости для вашей задачи рекомендуем определять самостоятельно.
 
Строка 1502:
 
«1000 пакетов в секунду с каждого хоста из подсети 192.168.0.0/16»
<sourcesyntaxhighlight lang="bash">
-s 192.168.0.0/16 -m hashlimit --hashlimit-upto 1000/sec --hashlimit-mode srcip
</syntaxhighlight>
</source>
 
«100 пакетов в секунду с каждого TCP-порта хоста 192.168.1.1»
<sourcesyntaxhighlight lang="bash">
-s 192.168.1.1 -m hashlimit --hashlimit-upto 100/sec --hashlimit-mode srcport
</syntaxhighlight>
</source>
 
«10000 пакетов в минуту с каждой подсети префикса 28 (маска 255.255.255.240) из диапазона 10.0.0.0/8»
<sourcesyntaxhighlight lang="bash">
-s 10.0.0.0/8 -m hashlimit --hashlimit-upto 10000/min --hashlimit-mode srcip --hashlimit-srcmask 28
</syntaxhighlight>
</source>
 
Обратите внимание, что в ''каждом'' из этих правил нужно обязательно указать имя таблицы очередей (haslimit-name, см. ниже), однако, в целях простоты изложения, в некоторых примерах этот параметр опущен.
Строка 1521:
 
<tt>--hashlimit-mode {srcip|srcport|dstip|dstport}[,...]</tt> — задает список контролируемых параметров: адреса (ip) и порты (port) источника (src) и назначения (dst). Например, если указать параметр
<sourcesyntaxhighlight lang="bash">
--hashlimit-mode srcip,dstip
</syntaxhighlight>
</source>
то будет создаваться отдельная очередь для каждой пары «адрес источника — адрес назначения», то есть ограничение будет вводиться на количество пакетов, передаваемых с каждого хоста на другой хост (разумеется, если эти пакеты идут через наш сервер). Или, например,
<sourcesyntaxhighlight lang="bash">
--hashlimit-mode srcip,srcport
</syntaxhighlight>
</source>
будет создавать отдельную очередь для каждого исходного порта каждого хоста.
 
Строка 1537:
 
<tt>--hashlimit-srcmask ''префикс''</tt> — задает размер подсети исходных адресов, для которой вводится своя очередь. Имеет смысл, только если в haslimit-mode указан режим srcip. Как уже говорилось выше,
<sourcesyntaxhighlight lang="bash">
-m hashlimit --hashlimit-mode srcip --hashlimit-srcmask 28
</syntaxhighlight>
</source>
будет заводить отдельную очередь для каждой подсети с маской 255.255.255.240 (подробнее про префиксы и маски см. {{w |CIDR}}).
По умолчанию 32 (своя очередь для каждого отдельного хоста). Если указать 0, то контроль по параметру srcip теряет смысл.
Строка 1546:
 
<tt>--hashlimit-name ''имя''</tt> — позволяет использовать несколько независимых таблиц очередей (hashes), распознаваемых по имени. Обязательный параметр. Например,
<sourcesyntaxhighlight lang="bash">
iptables -F INPUT # Очищаем цепочку INPUT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # Пропускаем все, что идет по уже установленным соединениям
Строка 1554:
iptables -A INPUT -m conntrack --ctstate NEW -p tcp --dport 873 -m hashlimit --hashlimit-upto 5/min --hashlimit-mode srcip --hashlimit-name rsynchash -j ACCEPT
iptables -P INPUT DROP # Всех остальных не пускаем
</syntaxhighlight>
</source>
Теперь очереди ограничений для подключения к службам {{w |FTP}} и {{w |rsync}} для нашего сервера будут вводиться независимо, то есть если какой-либо хост превысит предел подключений по FTP, то это никак не повлияет на подключения по rsync. Заметим, что в данном конкретном случае аналогичную функциональность можно реализовать, даже не используя разные хеши:
<sourcesyntaxhighlight lang="bash">
iptables -F INPUT # Очищаем цепочку INPUT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # Пропускаем все, что идет по уже установленным соединениям
Строка 1562:
iptables -A INPUT -m conntrack --ctstate NEW -p tcp -m multiport --dports 21,873 -m hashlimit --hashlimit-upto 5/min --hashlimit-mode srcip,dstport --hashlimit-name ftprsynchash -j ACCEPT
iptables -P INPUT DROP # Всех остальных не пускаем
</syntaxhighlight>
</source>
Впрочем, выбирая между этими двумя реализациями, стоит учитывать, что размер каждого хеша ограничен.
 
Строка 1574:
 
<tt>[!] --connlimit-above количество</tt> — минимальное количество соединений. Все пакеты, проходящие по соединениям, установленным сверх заданного количества, считаются удовлетворяющими критерию. Например,
<sourcesyntaxhighlight lang="bash">
iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 3 -j DROP
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
</syntaxhighlight>
</source>
разрешит не более трех одновременных соединений к нашему веб-серверу с одного IP-адреса. Аналогичного эффекта можно добиться командами
<sourcesyntaxhighlight lang="bash">
iptables -A INPUT -p tcp --dport 80 -m connlimit ! --connlimit-above 3 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j DROP
</syntaxhighlight>
</source>
 
<tt>--connlimit-mask</tt> — маска подсети, для которой устанавливается общее ограничение на количество соединений. Например,
<sourcesyntaxhighlight lang="bash">
iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 5 --connlimit-mask 24 -j DROP
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
</syntaxhighlight>
</source>
разрешит не более пяти одновременных соединений на порт 80 с каждой подсети [[w:Бесклассовая адресация|класса 1C]].
 
Строка 1602:
 
Пример:
<sourcesyntaxhighlight lang="bash">
# Для соединений, по которым передано более 500 КБ, ставим минимальный приоритет TOS (требование максимальной полосы)
iptables -t mangle -I INPUT -m connbytes --connbytes 512000: --connbytes-dir both --connbytes-mode bytes -j TOS --set-tos Maximize-Throughput
Строка 1609:
iptables -t mangle -I INPUT -m connbytes ! --connbytes 51200: --connbytes-dir both --connbytes-mode bytes -j TOS --set-tos Minimize-Delay
iptables -t mangle -I OUTPUT -m connbytes ! --connbytes 51200: --connbytes-dir both --connbytes-mode bytes -j TOS --set-tos Minimize-Delay
</syntaxhighlight>
</source>
Таким образом, все пакеты, принадлежащие «легким» соединениям, будут маркироваться как наиболее приоритетные, а пакеты, проходящие по «тяжеловесным» соединениям — как наименее приоритетные. Такой подход позволит рационально распределять канал между различными задачами, например, просмотром веб-страниц («легкие» соединения) и скачиванием файлов («тяжелые» соединения).
 
Строка 1621:
 
Например:
<sourcesyntaxhighlight lang="bash">
sysctl net.ipv4.ip_forward=1 # Разрешаем шлюзу передавать транзитный трафик
iptables -F FORWARD # Очищаем цепочку FORWARD
iptables -A FORWARD -m quota --quota 1073741824 -j ACCEPT
iptables -P FORWARD DROP
</syntaxhighlight>
</source>
разрешает хосту передавать не более одного гигабайта транзитного трафика (учитывается трафик в обоих направлениях).
 
Строка 1632:
 
<tt>--name ''имя''</tt> — задает имя счетчика. Текущее значение счетчика содержится в файле <tt>/proc/net/xt_quota/''имя''</tt>. Его можно вывести на экран
<sourcesyntaxhighlight lang="bash">
cat /proc/net/xt_quota/имя
</syntaxhighlight>
</source>
или задать вручную
<sourcesyntaxhighlight lang="bash">
echo значение > /proc/net/xt_quota/имя
</syntaxhighlight>
</source>
Для осуществления этих действий, как и для работы с iptables, нужны полномочия [[w:Root|суперпользователя]].
 
<tt>[!] --quota ''количество''</tt> — проверяет текущее значение счетчика и уменьшает его на размер текущего пакета. Этот же параметр задает начальное значение счетчика (если счетчик с таким именем уже существует, он не сбрасывается). Пакет считается подпадающим под критерий, если счетчик еще не дошел до нуля (при указании логической инверсии — наоборот). Если немного усложнить наш предыдущий пример,
<sourcesyntaxhighlight lang="bash">
sysctl net.ipv4.ip_forward=1 # Разрешаем шлюзу передавать транзитный трафик
iptables -F FORWARD # Очищаем цепочку FORWARD
Строка 1653:
iptables -A FORWARD -s 192.168.2.0/24 -m quota2 --name second --quota 536870912 -j ACCEPT
iptables -P FORWARD DROP
</syntaxhighlight>
</source>
Теперь для подсети 192.168.1.0/24 установлена квота в 1 Гб, и при превышении этой квоты пакеты блокируются, причем их отправителям хост отвечает пакетами icmp-port-unreachable, уведомляя их, что передача данных заблокирована. Для подсети 192.168.2.0/24 устанавливается квота 512 Мб, и при ее превышении никаких сообщений не посылается (пакеты блокируются «молча»). Так же молча блокируются транзитные пакеты из всех остальных подсетей.
 
Строка 1659:
 
<tt>--grow</tt> — ''увеличивает'' счетчик, вместо того, чтобы уменьшать его. Критерий с этим параметром всегда возвращает истину. Позволяет создавать счетчики «приходно-расходного типа». Например,
<sourcesyntaxhighlight lang="bash">
-A INPUT -p tcp --dport 6881 -m quota2 --name bt --grow
-A OUTPUT -p tcp --sport 6881 -m quota2 --name bt
</syntaxhighlight>
</source>
позволяет в любой момент определять разность сумм входящих (на TCP-порт [[BitTorrent|6881]]) и исходящих (с этого же порта) байт через файл <tt>/proc/net/xt_quota/bt</tt>.
 
Строка 1684:
 
В качестве практического примера использования данного критерия можно рассмотреть установку высокого приоритета [[w:Тип обслуживания|TOS]] (требование минимальной задержки) для небольших транзитных пакетов (размер до 512 байт):
<sourcesyntaxhighlight lang="bash">
iptables -t mangle -I INPUT -m length --length :512 -j TOS --set-tos Minimize-Delay
</syntaxhighlight>
</source>
 
* '''length2''' — расширение критерия length, доступное в наборе xtables-addons. Отличается от классического length возможностью проверять размеры пакетов на различных уровнях [[w:Сетевая модель OSI|модели OSI]]. Принимает следующие опции:
Строка 1743:
 
<tt>--name ''имя''</tt> — позволяет указать имя списка при использовании нескольких списков. По умолчанию используется список DEFAULT. Каждый список представлен псевдофайлом <tt>/proc/net/xt_recent/''имя''</tt> (на старых ядрах критерий recent реализован только для {{w |IPv4}}, но не для {{w |IPv6}}, поэтому вместо <tt>xt_recent</tt> будет <tt>ipt_recent</tt>). В частности, вы можете:
<sourcesyntaxhighlight lang="bash">
cat /proc/net/xt_recent/имя # вывести список на экран
echo +адрес > /proc/net/xt_recent/имя # добавить адрес в список
echo -адрес > /proc/net/xt_recent/имя # удалить адрес из списка
echo / > /proc/net/xt_recent/имя # очистить список
</syntaxhighlight>
</source>
 
Теперь давайте рассмотрим несколько примеров.
* Блокирование {{w |bruteforce}}-атак (подбор пароля вслепую) на {{w |SSH}} и аналогичные сервисы.
<sourcesyntaxhighlight lang="bash">
iptables -N ssh_brute_check # Создаем цепочку для проверки попыток соединений на защищаемый порт
# Если за последние 10 минут (600 секунд) с одного адреса было 3 или более новых соединений — блокируем этот адрес
Строка 1765:
iptables -A INPUT -m conntrack --ctstate NEW -p tcp --dport 80 -j ACCEPT
iptables -P INPUT DROP # Что не разрешено — то запрещено
</syntaxhighlight>
</source>
Теперь все попытки открыть новое SSH-соединение проверяются, и с одного {{w |IP-адрес}}а можно открывать не более 2 соединений за 10 минут. Обратите внимание, что за одно соединение злоумышленник может проверить несколько паролей — число попыток аутентификации до обрыва соединения задает параметр MaxAuthTries в файле <tt>/etc/ssh/sshd_config</tt>. По умолчанию это число равно 6, так что в нашем примере злоумышленник сможет проверять не более 12 паролей за 10 минут.
 
Впрочем, данный пример весьма тривиален, и сходную функциональность можно получить и при помощи критерия hashlimit:
<sourcesyntaxhighlight lang="bash">
iptables -A INPUT -m conntrack --ctstate NEW -p tcp --dport 22 -m hashlimit --hashlimit-mode srcip --hashlimit-upto 5/hour --hashlimit-name ssh -j ACCEPT
</syntaxhighlight>
</source>
разрешит не более 5 новых соединений в час.
Данная реализация имеет недостаток по сравнению с recent — вы не можете произвольно задавать временной период.
 
* Защита от сканирования портов (заметим, что кроме критерия recent для этой задачи можно также использовать критерии psd и lscan из комплекта xtables-addons).
<sourcesyntaxhighlight lang="bash">
iptables -F INPUT # Очищаем цепочку INPUT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # Разрешаем пакеты по установленным соединениям
Строка 1789:
iptables -A INPUT -m recent --set
iptables -P INPUT DROP # Что не разрешено — то запрещено
</syntaxhighlight>
</source>
Все пакеты, попадающие на нерабочие порты сервера, регистрируются. После нескольких таких попыток за заданный интервал времени адрес их источника блокируется. Проверка TTL добавлена для защиты от блокировки легитимных клиентов после спуфинга злоумышленником от их имени. Хотя, с другой стороны, эта мера позволяет ему обойти защиту от сканирования, выставляя своим пакетам различный TTL. Так что вопрос о нужности этой проверки в вашем конкретном случае вам придется решать самостоятельно.
 
Другой пример использования критерия recent для детекции и блокирования сканирования портов:
<sourcesyntaxhighlight lang="bash">
# Создаем цепочку для проверки и блокирования портсканов
iptables -N portscan_check
Строка 1804:
# Пускаем на проверку всех, кто пришел к нам из интернета (интерфейс eth0)
iptables -I INPUT -i eth0 -j portscan_check
</syntaxhighlight>
</source>
В данном случае сканированием портов считается обращение на порт 139/tcp ({{w |SMB}}). Впрочем, вы можете задать другой порт или, используя критерий multiport, даже список портов. В отличие от предыдущего примера, здесь производится автоматическая очистка списка {{w |IP-адрес}}ов: по прошествии суток с момента блокировки, первое же обращение с заблокированного адреса на наш сервер приводит к удалению этого адреса из нашего recent-списка. Однако, если это обращение вновь является попыткой сканирования портов, то есть направлено на порт 139/tcp, адрес тут же блокируется вновь. Именно поэтому процедура блокировки расположена «ниже по течению», чем процедура удаления адреса из списка.
 
Для предыдущего примера реализация очистки списка адресов достигается следующим образом:
<sourcesyntaxhighlight lang="bash">
# Создаем цепочку для проверки на необходимость удаления
iptables -N recent_remove
Строка 1818:
# Вставляем эту проверку вторым правилом (сразу после проверки ctstate)
iptables -I INPUT 2 -j recent_remove
</syntaxhighlight>
</source>
 
Проводя аналогию с ныне почившим проектом [http://www.opennet.ru/docs/RUS/portsentry/portsentry-security.html.gz PortSentry], первый наш пример соответствует режиму PortSentry «Advanced Stealth Scan Detection», а второй — «Enhanced Stealth Scan Detection». (Правда, заметим, что в режиме Advanced Stealth Scan Detection блокировка производится после ''первого'' попадания пакета на нерабочий порт — для достижения такого же эффекта в нашем примере достаточно выкинуть проверки на seconds и hitcount из блокирующих правил.) Однако, использование iptables/recent имеет значительное преимущество перед примитивными userspace-системами наподобие PortSentry — возможность интеграции с системой [[conntrack]], что позволяет избежать ошибочной блокировки, например, при использовании высоких портов для {{w |NAT}}.
Строка 1829:
 
Самое простое — открывать порт ssh (22) после стука в заданный высокий порт:
<sourcesyntaxhighlight lang="bash">
iptables -N ssh_knock # Создаем цепочку для проверки попыток соединений на защищаемый порт
# Если за последние 60 секунд было 2 и более стука — блокируем, на всякий случай
Строка 1844:
iptables -A INPUT -m conntrack --ctstate NEW -p tcp -m multiport --dport 27519,27521 -m recent --remove
iptables -P INPUT DROP # Что не разрешено — то запрещено
</syntaxhighlight>
</source>
Даже в таком простом примере присутствуют жесткие меры защиты: защищаемый порт (22) открывается на 10 секунд после стука в заданный порт (27520), при этом более одного стука в этот порт в течение минуты считается ошибкой. Также стук в соседние с заданным порты сразу закрывает защищаемый порт. Это делается в целях защиты от подбора стука.
 
Если вам не интересны такие параноидальные меры безопасности, то обратите внимание на этот простой пример:
<sourcesyntaxhighlight lang="bash">
iptables -N ssh_knock # Создаем цепочку для проверки
# Если за последние 10 минут было 5 и более попыток соединения — блокируем
Строка 1863:
iptables -A INPUT -m conntrack --ctstate NEW -p tcp --dport 22 -j ssh_knock
iptables -P INPUT DROP # Что не разрешено — то запрещено
</syntaxhighlight>
</source>
Идея метода проста: защищаемый порт открывается со второй попытки, то есть стук идет непосредственно в него. Вторую попытку нужно сделать в течение 5 секунд после первой. При этом 5 и более попыток за десять минут блокируются (во избежание брутфорса).
 
Также можно организовать защиту через стук в серию портов:
<sourcesyntaxhighlight lang="bash">
iptables -N reset_knock # Цепочка для сброса процесса стука
iptables -A reset_knock -m recent --name PHASE1 --remove
Строка 1899:
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --rcheck --name PHASE4 --seconds 5 -j checked
iptables -P INPUT DROP # Дефолтное правило цепочки INPUT
</syntaxhighlight>
</source>
Принцип прост — при стуке в порт, соответствующий очередной фазе, проверяется наличие записи в предыдущей фазе. Теперь порт 22 откроется на 5 секунд после стука в порты 21210, 11992, 16043, 23050 со строгим соблюдением порядка перечисления и интервалами не более 5 секунд.
 
Строка 1905:
 
Последовательный стук в порты можно организовать, например, утилитой {{w |netcat}}
<sourcesyntaxhighlight lang="bash">
for it in {21210,11992,16043,23050}; do
echo " " | nc -w 1 host $it
done
ssh user@host
</syntaxhighlight>
</source>
 
Другие примеры реализации port knocking’а при помощи критерия recent можно посмотреть на [http://wiki.opennet.ru/Iptables_-m_recent OpenNet wiki], [http://www.debian-administration.org/articles/268 debian-administration.org], [http://silverghost.org.ua/2007/06/13/ssh-portknocking/ silverghost.org.ua], [http://www.opennet.ru/openforum/vsluhforumID3/41732.html#32 форуме OpenNet].