Конфигурационный файл фильтра пакетов
/etc/pf.conf
ЗОСРВ «Нейтрино»
Фильтр пакетов pf изменяет, сбрасывает или передает пакеты в соответствии с правилами или определениями в файле /etc/pf.conf
. По умолчанию этот файл расположен по пути /etc/pf.conf
.
Рассмотрим особенности формирования конфигурационного файла:
Файл /etc/pf.conf
может содержать операторы следующих типов:
/etc/pf.conf
. За исключением макросов и таблиц, операторы следует группировать по типу и добавлять в файл /etc/pf.conf
в приведенном выше порядке, поскольку он соответствует порядку выполнения операций базового механизма фильтрации пакетов. По умолчанию соблюдение этого порядка обеспечивается утилитой pfctl (см. описание set require-order далее).
Аналогично {cpp} или m4, имеется возможность определения макросов и их последующего расширения в контексте. Имя макроса должно начинаться с буквы и может содержать буквы, цифры и символ подчеркивания. В качестве имен макросов не допускается использовать зарезервированные слова (например, pass, in, out). Расширение макросов, заключенных в кавычки, не выполняется.
Пример:
ext_if = "kue0" all_ifs = "{" $ext_if lo0 "}" pass out on $ext_if from any to any keep state pass in on $ext_if proto tcp from any to any port 25 keep state
Таблицы – это именованные структуры, содержащие набор адресов и сетей. Поиск по таблицам в pf выполняется относительно быстро, что означает, с точки зрения потребления ресурсов процессора и памяти, что применение отдельных правил к таблицам намного более эффективно по сравнению с большим количеством правил, в которых различаются только IP-адреса (созданные явно или автоматически, в результате расширения правила).
Таблицы используются в качестве источника или назначения правил фильтрации, правил очистки и правил трансляции, например nat или rdr (дополнительная информация о различных типах правил приведена далее). Таблицы также используются для переадресации правил nat и rdr и в опциях маршрутизации правил фильтрации, но только для циклических пулов.
Таблицы можно определить с помощью любого из перечисленных ниже механизмов pfctl. Как и для макросов, в качестве имени таблицы не допускается использовать зарезервированные слова.
/etc/pf.conf
/etc/pf.conf
используется табличный оператор; этот способ наиболее эффективен для определения временных таблиц. Содержимое предварительно созданной таблицы, для инициализации которой список адресов в определении не использовался, при загрузке файла /etc/pf.conf
не изменяется. Таблица, инициализированная на основе пустого списка { }, при загрузке очищается. Для таблиц можно определить следующие атрибуты:
Пример:
table <private> const { 10/8, 172.16/12, 192.168/16 } table <badhosts> persist block on fxp0 from { <private>, <badhosts> } to any
В результате создается таблица с именем private, в которой сохраняются блоки частных сетей RFC 1918, и таблица с именем badhosts, которая изначально является пустой. Для блокирования трафика, поступающего со всех адресов, содержащихся в какой- либо из этих таблиц, настраивается правило фильтрации.
Изменить содержимое таблицы private невозможно, а таблицу badhosts невозможно удалить, даже если активные ссылающиеся на нее правила фильтрации отсутствуют. В дальнейшем в таблицу badhosts можно добавлять адреса, что позволяет блокировать трафик от этих хостов, с помощью следующей команды:
# pfctl -t badhosts -Tadd 204.92.77.111
Таблицу также можно инициализировать путем ввода списка адресов в один или несколько внешних файлов, с использованием следующего синтаксиса:
table <spam> persist file "/etc/spammers" file "/etc/openrelays" block on fxp0 from <spam> to any
Файлы /etc/spammers
и /etc/openrelays
содержат списки IP-адресов (по одному на строку). Строки, начинающиеся с символа #
, считаются комментариями и игнорируются. Помимо определения хостов по IP-адресу, их также можно определить по именам. При вызове преобразователя адресов для добавления имени хоста в таблицу все полученные в результате адреса IP и IP6 добавляются в эту таблицу. IP-адреса также можно добавлять в таблицу путем определения действительного имени интерфейса или ключевого слова self. При этом в таблицу добавляются все адреса, присвоенные интерфейсу (интерфейсам).
С помощью команды set можно настроить pf для различных ситуаций:
Последствия применения противоречивого набора правил могут быть нестандартны и неочевидны. Следует убедиться, что деактивация этого порядка действительно необходима. |
Нормализация трафика используется для очистки содержимого пакета и позволяет исключить неточную интерпретацию пакета на принимающей стороне. Нормализатор объединяет IP-фрагменты для предотвращения атак, влияющих на системы обнаружения вторжения путем передачи перекрывающихся IP-фрагментов. Нормализация пакетов запускается посредством директивы scrub, для которой можно установить следующие опции:
Пример:
scrub in on $ext_if all fragment reassemble
Если перед правилом очистки указана опция no, то обработка соответствующих пакетов не выполняется, аналогично быстрому сбросу в фильтре пакетов (см. далее). Этот механизм используется при необходимости исключения обработки определенных пакетов более сложными правилами очистки.
В целях управления полосой пропускания пакеты могут присваиваться очередям. Для настройки очередей необходимо выполнить, по меньшей мере, два объявления, и в дальнейшем в любое правило фильтрации пакетов можно добавить в качестве ссылок имена определенных очередей. Во время выполнения фильтрации компонентов, определенных в /etc/pf.conf
, пакеты, переданные согласно правилам передачи, помещаются в очередь, имя которой было указано в последней ссылке, а для правил блокировки он определяет очередь, в которую помещаются все итоговые пакеты ICMP и TCP RST. Диспетчер определяет алгоритм, используемый для определения пакетов, которые должны быть отложены, сброшены или немедленно переданы.
В настоящее время поддерживаются следующие диспетчеры:
Интерфейсы, в которых требуется активировать формирование очередей, объявляются посредством декларации altq on, для чего используются следующие ключевые слова:
Правила трансляции обеспечивают изменение адреса источника или получателя для пакетов, соответствующих соединению с запоминанием состояния. Соединение с запоминанием состояния автоматически создается для отслеживания пакетов, удовлетворяющих данному правилу, если они не блокируются секцией фильтрации /etc/pf.conf
. Механизм трансляции модифицирует указанный адрес и/или порт пакета, выполняет перерасчет контрольных сумм IP, TCP и UDP по мере необходимости, и передает пакет в фильтр пакетов для обработки.
Поскольку трансляция производится до фильтрации, механизм фильтрации получает пакеты в том виде, который они имеют после трансляции всех адресов и портов. Таким образом, правила фильтрации обеспечивают фильтрацию на основе уже преобразованного адреса и номера порта. Пакеты, соответствующие правилу трансляции, передаются автоматически только в том случае, если указан модификатор pass; в противном случае они подлежат обработке по правилам блокирования и передачи.
Созданная запись состояния позволяет pf отслеживать адрес источника для трафика, связанного с этим состоянием, и корректно перенаправлять обратный трафик для данного соединения.
Возможные типы трансляций в pf:
Помимо изменения адреса, некоторыми правилами трансляции могут быть изменены порты источника или назначения для соединений TCP или UDP; в случае правил nat – неявно, в случае правил rdr – явно. В случае правил binat трансляция номеров портов не производится.
Для каждого пакета, обрабатываемого транслятором, правила трансляции применяются последовательно, от первого к последнему. Выполняемое действие определяется первым правилом сопоставления.
Если перед правилом трансляции указана опция no, то трансляция пакетов не выполняется, аналогично опции быстрого сброса drop quick в фильтре пакетов (см. далее). Если пакету не соответствует ни одно правило, то этот пакет передается механизму фильтрации в исходном виде.
Правила трансляции применяются только к тем пакетам, которые проходят через указанный интерфейс; если интерфейс не указан, то трансляция применяется к пакетам на всех интерфейсах. Например, перенаправление с порта 80 на внешнем интерфейсе на внутренний web-сервер выполняется только для соединений, инициируемых извне. Соединения на адрес внешнего интерфейса, инициируемые локальными хостами, не перенаправляются, поскольку такие пакеты не проходят через внешний интерфейс. При перенаправлении пакеты не могут быть отправлены через интерфейс, по которому они поступают; они могут быть перенаправлены только на хосты, подключенные по другим интерфейсам, или непосредственно на межсетевой экран.
Следует отметить, что перенаправление внешних входящих соединений на адрес замыкания, например:
rdr on ne3 inet proto tcp to port 8025 -> 127.0.0.1 port 25
позволяет внешнему хосту подключиться к сервисам, однозначно привязанным к адресу замыкания, и, таким образом, предотвратить стандартное блокирование таких соединений на реальном интерфейсе. Если такая обработка не требуется, то любой локальный адрес, не являющийся адресом замыкания, должен использоваться как целевой адрес перенаправления; при этом будут разрешены внешние подключения только к сервисам, привязанным к данному адресу или не привязанным ни к какому адресу. См. раздел "Примеры трансляции" далее.
Псевдоустройство pf осуществляет блокирование или передачу пакетов на основе атрибутов заголовков уровня 3 (см. разделы IP и IP6 в руководстве по библиотекам справочной системы комплекта разработчика ЗОСРВ «Нейтрино») и заголовков уровня 4 (см. разделы ICMP, ICMP6, TCP и UDP). Кроме того, в целях управления пропускной способностью пакеты могут присваиваться очередям.
Для каждого пакета, обрабатываемого фильтром пакетов, правила фильтрации применяются последовательно, от первого к последнему. Выполняемое действие определяется последним правилом сопоставления.
В фильтре можно определить следующие действия:
Если пакет не соответствует ни одному правилу, то по умолчанию осуществляется передача пакета. Для блокирования всех пакетов по умолчанию и передачи только тех пакетов, которые соответствуют явно заданным правилам, используется следующая команда:
block all
Она указывается в качестве первого правила фильтрации. См. раздел "Примеры фильтрации" далее.
Параметры правил представляют собой описание пакетов, к которым применяется правило. Любой пакет поступает или проходит через какой-либо интерфейс. Большинство параметров являются необязательными. Если задан какой-либо параметр, то данное правило применяется только к пакетам с соответствующими атрибутами. Некоторые параметры могут иметь форму списков, при этом все требуемые комбинации правил генерируются посредством pfctl.
/var/log/pflog
в бинарном формате pcap. Оператор | Значение |
---|---|
= | Равно |
!= | Не равно |
< | Меньше |
<= | Меньше или равно |
> | Больше |
>= | Больше или равно |
: | Диапазон, включая крайние значения |
>< | Диапазон, исключая крайние значения |
<> | Кроме диапазона |
Запись | Значение |
---|---|
port 2000:2004 | Все порты ≥ 2000 и ≤ 2004, т.е. порты 2000, 2001, 2002, 2003 и 2004 |
port 2000 >< 2004 | Все порты > 2000 и < 2004, т.е. порты 2001, 2002 и 2003 |
port 2000 <> 2004 | Все порты < 2000 или > 2004, т.е. порты 1–1999 и 2005–65535 |
Запись | Значение |
---|---|
flags S/S | Установлен флаг SYN. Остальные флаги игнорируются. |
flags S/SA | Из флагов SYN и ACK может быть установлен только флаг SYN. Этой записи соответствуют сочетания SYN, SYN+PSH и SYN+RST, но не соответствуют сочетания SYN+ACK, ACK и ACK+RST. По сравнению с предыдущим примером установлено больше ограничений. |
flags /SFRA | Если первый набор не задан, то по умолчанию он отсутствует. Флаги SYN, FIN, RST и ACK должны быть отменены. |
Макрос | Значение |
---|---|
$if | Интерфейс |
$srcaddr | IP-адрес источника |
$dstaddr | IP-адрес назначения |
$srcport | Спецификация порта источника |
$dstport | Спецификация порта назначения |
$proto | Имя протокола |
$nr | Номер правила |
Если пакет соответствует правилу, для которого установлена опция маршрутизации, то этот пакет отправляется фильтром пакетов в соответствии с типом опции маршрутизации. Если это правило создает состояние, то опция маршрутизации применяется ко всем пакетам, относящимся к данному соединению.
В правилах nat и rdr (а также для опций route-to, reply-to и dup-to этих правил), для которых имеется одиночный адрес перенаправления с маской подсети меньше 32 для IP или 128 для IP6 (более одного IP-адреса), присвоение этого адреса можно выполнить несколькими способами:
Кроме того, можно указать опцию sticky-address, что обеспечит сопоставление нескольких соединений от одного источника с одним и тем же адресом перенаправления. Эту опцию можно использовать совместно с опциями пула random и round-robin. Обратите внимание, что по умолчанию эти сопоставления удаляются, если ссылающиеся на них состояния больше не существуют; для сохранения сопоставлений на период, превышающий срок существования состояний следует увеличить значение соответствующей глобальной опции set timeout source-track. Другие способы отслеживания источников описаны в разделе "Опции отслеживания с запоминанием состояния".
Фильтр пакетов pf обеспечивает фильтрацию с запоминанием состояния (stateful); это означает, что в нем реализована поддержка отслеживания состояния соединения. Например, вместо перенаправления всего трафика на порт 25 может быть перенаправлен только первый пакет, после чего начинается поддержка состояния. Движение последующего трафика обусловлено тем, что фильтр отслеживает соединение.
Если какой-либо пакет соответствует правилу состояния pass ... keep, то для данного соединения фильтром создается новое состояние, и все последующие пакеты этого соединения передаются автоматически.
Перед применением правил фильтр проверяет пакет на соответствие какому-либо состоянию. Если соответствие найдено, то пакет передается без применения каких-либо правил.
Состояния удаляются после закрытия соединения или завершения по таймауту.
За счет этого реализуются следующие преимущества:
Пример.
block all pass out proto tcp from any to any flags S/SA keep state pass in proto tcp from any to any port 25 flags S/SA keep state
Данный набор правил по умолчанию создает полную блокировку. Разрешаются только исходящие соединения и входящие соединения на порт 25. Первый пакет каждого соединения содержит установленный флаг SYN, передается и создает состояние. Все последующие пакеты, проходящие по этим соединениям, передаются только в том случае, если соответствуют состоянию.
По умолчанию состоянию могут соответствовать входящие и исходящие пакеты любого интерфейса, однако это поведение можно изменить путем назначения состояний определенному интерфейсу или группе интерфейсов.
Политика по умолчанию определяется глобальной опцией state-policy, при этом ее можно изменить для каждого правила путем добавления ключевых слов if-bound, group-bound или floating к опции keep state. Например, определено следующее правило:
pass out on ppp from any to 10.12/16 keep state (group-bound)
В этом случае состояние, созданное на интерфейсе ppp0, будет соответствовать пакетам на всех интерфейсах PPP и не будет соответствовать пакетам, проходящим через интерфейс fxp0 или какой-либо другой.
При функционировании межсетевого экрана в среде с динамической маршрутизацией отсутствие необходимости сопоставления с правилами позволяет достичь большей гибкости. С другой стороны, при этом возникают потенциальные проблемы с безопасностью, поскольку состояние, созданное одной доверенной сетью, может допустить пропуск опасных пакетов, поступающих с других интерфейсов.
При установке флагов S/SA состояние создается только для первого пакета SYN в подтверждении связи по TCP. Это ограничение можно ослабить, разрешив создание состояний на основе промежуточных пакетов (без флага SYN). При этом pf выполняет синхронизацию с существующими соединениями, например после очистки таблицы состояний.
В случае использования протокола UDP, для которого запоминание состояния не предусмотрено, опция keep state также позволяет создавать состояния. UDP-пакеты сопоставляются с состояниями только на основе адресов и портов хостов.
ICMP-сообщения делятся на две категории. ICMP-сообщения об ошибках, связанные только с TCP- или UDP-пакетами, сопоставляются с тем соединением, с которым они связаны. Если состояние TCP-соединения сохраняется и поступает сообщение подавления источника ICMP, связанное с данным TCP-соединением, то оно сопоставляется с требуемым состоянием и передается.
Для ICMP-запросов keep state создает состояние ICMP, а pf получает информацию для сопоставления ICMP-ответов с состояниями. Пример.
pass out inet proto icmp all icmp-type echoreq keep state
Эта команда разрешает исходящие запросы отклика (например, создаваемые посредством команды ping), создает состояние и корректно сопоставляет входящие запросы отклика с состояниями.
Правила nat, binat и rdr неявно создают состояния соединений. |
Многие аспекты безопасности TCP определяются степенью правильности выбора начальных номеров последовательностей (Initial Sequence Number; ISN). Во многих случаях при реализации стека выбор ISN недостаточно усложнен, что обычно повышает риск предсказания ISN. При применении правила модуляции состояния к TCP-соединению pf создает номер последовательности высокой степени случайности для каждой конечной точки соединения.
Директива modulate state неявно сохраняет состояние в правиле и применяется только в отношении TCP-соединений. Пример.
block all pass out proto tcp from any to any modulate state pass in proto tcp from any to any port 25 flags S/SA modulate state
При использовании модуляции состояний следует учитывать ряд особенностей:
По умолчанию pf передает пакеты, относящиеся к подтверждению связи между конечными точками по TCP. Опция synproxy state используется для принудительного подтверждения связи между pf и активной конечной точкой с последующим подтверждением связи с пассивной точкой для передачи пакетов между ними.
До подтверждения связи с активной конечной точкой передача пакетов пассивной конечной точке невозможна, следовательно, т.н. SYN-лавина с подменными адресами источников не достигнет пассивной конечной точки, поскольку связь не подтверждена отправителем.
Этот прокси является прозрачным для обеих конечных точек, т.е. каждой из точек известно единственное соединение с другой конечной точкой. Фильтром пакетов pf выбираются случайные начальные номера последовательностей для подтверждений связи с обеих сторон. После подтверждения связи для преобразования последующих пакетов этого соединения используются модуляторы номеров последовательностей (см. предыдущий раздел). Таким образом, synproxy state включает в себя modulate state и keep state.
Правила с опцией synproxy не выполняются, если pf функционирует на подключении типа bridge.
Пример:
pass in proto tcp from any to any port www flags S/SA synproxy state
Каждое из правил keep state, modulate state и synproxy state поддерживает следующие опции:
Можно указать несколько опций, разделенных запятой:
pass in proto tcp from any to any \ port www flags S/SA keep state \ (max 100, source-track rule, max-src-nodes 75, \ max-src-states 3, tcp.established 60, tcp.closing 5)
Если указано ключевое слово source-track, то отслеживается количество состояний для каждого IP-адреса источника:
Можно установить следующие ограничения:
Поскольку 3-стороннее подтверждение связи снижает риск подмены адреса источника, на основе этих ограничений возможным становится более агрессивное действие. Если задана опция переполнения таблицы состояний table, IP-адреса источников, соответствующие любому из указанных ограничений для установленного соединения, добавляются в данную таблицу. Эту таблицу можно указать в наборе правил для блокирования действий хоста-нарушителя, перенаправления их в процесс TARPIT или ограничения соответствующей пропускной способности.
Необязательное ключевое слово flush уничтожает все состояния, созданные правилом сопоставления, которые были инициированы хостом, превысившим указанные ограничения. Модификатор global команды flush уничтожает все состояния, инициированные хостом-нарушителем, вне зависимости от того, каким правилом было создано состояние.
Например, следующие правила защищают web-сервер от атак хостов, создающих более 100 соединений в течение 10 секунд. Адрес любого хоста, создающего соединения со скоростью, которая превышает указанную, добавляется в таблицу bad_hosts, и все инициируемые им состояния удаляются. Новые пакеты, поступающие от этого хоста, отбрасываются безусловно по правилу block.
block quick from bad_hosts pass in on $ext_if proto tcp to $webserver port www flags S/SA keep state \ (max-src-conn-rate 100/10, overload bad_hosts flush global)
Пассивное получение отпечатков ОС – это механизм, позволяющий проанализировать подробную информацию о начальном TCP-пакете SYN и определить операционную систему хоста. К сожалению, эта информация может быть имитирована злоумышленником, поэтому отпечаток не целесообразно использовать для принятия решений, связанных с безопасностью. Однако на основе отпечатков можно принимать достаточно обоснованные стратегические решения.
Отпечаток несет информацию о классе операционной системы, версии или подтипе/уровне исправлений. Класс операционной системы, как правило, определяется поставщиком или жанром; в случае межсетевого экрана pf это OpenBSD. Наиболее ранней из доступных версий OpenBSD на основном FTP является версия 2.6. Следовательно, записывается следующий отпечаток:
"OpenBSD 2.6"
Подтип операционной системы обычно используется для описания уровня исправлений, если после внедрения соответствующего исправления изменилось поведение стека TCP. В случае OpenBSD подтип существует только для отпечатка, нормализованного опцией очистки no-df, и отображается следующим образом:
"OpenBSD 3.3 no-df"
Отпечатки для наиболее популярных операционных систем содержатся в файле /etc/pf.os
. После запуска pf полный список известных отпечатков операционных систем можно получить следующей командой:
# pfctl -so
На основе полученного отпечатка можно применить правила фильтрации для определения политики на любом уровне спецификации операционной системы. На основе этой политики возможно ограничение трафика в направлении указанной операционной системы или даже блокировка трафика от хостов, на которых не установлен последний пакет обновления.
Кроме того, в качестве отпечатка можно использовать класс unknown, соответствующий пакетам, для которых отпечаток операционной системы не известен.
Примеры:
pass out proto tcp from any os OpenBSD keep state block out proto tcp from any os Doors block out proto tcp from any os "Doors PT" block out proto tcp from any os "Doors PT SP3" block out from any os "unknown" pass on lo0 proto tcp from any os "OpenBSD 3.3 lo0" keep state
Получение отпечатка операционной системы возможно только для TCP-пакета SYN. Таким образом, этот подход невозможно применить в случае использования других протоколов, поскольку не обеспечивается соответствие текущему установленному соединению.
Отпечатки операционных систем в некоторых случаях могут оказаться некорректными. Существует несколько проблем:
|
Спуфинг – это фальсификация IP-адресов, как правило, в злонамеренных целях. В результате расширения директивы antispoof возникает набор правил фильтрации, блокирующих весь трафик по IP-адресу источника из сети (сетей), непосредственно подключенной к указанному интерфейсу (интерфейсам), с целью исключения его поступления в систему через любой другой интерфейс. Например, строка
antispoof for lo0
после расширения принимает следующий вид:
block drop in on ! lo0 inet from 127.0.0.1/8 to any block drop in on ! lo0 inet6 from ::1 to any
В отношении интерфейсов, не являющихся интерфейсами обратной связи, применяются дополнительные правила блокирования входящих пакетов с IP-адресом источника, идентичным IP-адресам интерфейса. Например, предположим, что интерфейс wi0 имеет IP-адрес 10.0.0.1 и маску сети 255.255.255.0, тогда строка
antispoof for wi0 inet
после расширения принимает следующий вид:
block drop in on ! wi0 inet from 10.0.0.0/24 to any block drop in inet from 10.0.0.1 to any
Правила, созданные директивой antispoof, приводят к возникновению конфликта в отношении пакетов, передаваемых через интерфейсы обратной связи на локальные адреса. Передача таких пакетов должна осуществляться явным образом. |
Размер IP-дейтаграмм (пакетов) может существенно превышать максимальный размер пакета (Maximum Transmission Unit, MTU) в сети. В тех случаях, когда передача таких больших пакетов является необходимой или целесообразной, большой пакет фрагментируется на несколько пакетов меньшего размера, каждый из которых соответствует допустимым для сети пределам. С точки зрения межсетевых экранов недостатком является то, что только первый логический фрагмент содержит необходимую информацию заголовка для подпротокола, которая позволяет pf выполнять фильтрацию, например портов TCP, или трансляцию NAT.
В дополнение к использованию правил очистки, описанных в вышеприведенном разделе "Нормализация трафика", существует три опции обработки фрагментов фильтром пакетов.
Одна из них – фильтрация отдельных фрагментов при помощи правил фильтрации. Если к фрагменту не применяется правило очистки, то он передается фильтру. На основе правил фильтрации с параметрами, соответствующими заголовку IP-пакета, определяется необходимость передачи или блокирования фрагмента, аналогично тому, как происходит фильтрация целых пакетов. В отсутствие объединения фрагментов возможна их фильтрация только на основе полей заголовка IP-пакета (адрес источника/назначения, протокол), поскольку поля заголовка подпротокола недоступны (номера портов TCP/ UDP, код/тип ICMP). С помощью опции fragment определяется ограничение для применения правил фильтрации только к фрагментам, а не к целым пакетам. Правила фильтрации без опции fragment также применимы к фрагментам, но только в том случае, если они содержат поля заголовка IP-пакета. Например, правило
pass in proto tcp from any to any port 80
никогда не применяется к фрагменту, даже если этот фрагмент является частью TCP-пакета с портом назначения 80, поскольку без объединения эта информация доступна не всем фрагментам. Это также означает, что фрагменты не могут создавать новые состояния или соответствовать существующим записям таблицы состояний, таким образом, фильтрация с запоминанием состояния и трансляция адресов (NAT, перенаправление) для фрагментов невозможны.
Кроме того, допускается объединение только определенных фрагментов, путем определения в правилах очистки адресов либо протоколов источника или назначения в качестве параметров.
В большинстве случаев преимущества объединения более важны, чем увеличение объема потребляемой памяти. Для объединения всех фрагментов рекомендуется использовать правила очистки с модификатором fragment reassemble.
Ограничить объем памяти, выделяемый для кэширования фрагментов, можно с помощью pfctl. По достижении заданного предела фрагменты, подлежащие кэшированию, игнорируются до наступления таймаута записи. Также можно изменить значение таймаута.
В настоящее время реализована поддержка только фрагментов IP. Фрагменты IP6 безусловно блокируются.
Помимо основного набора правил, pfctl позволяет загружать наборы правил в точки присоединения закладок. Закладка – это контейнер, который может содержать правила, таблицы адресов и другие закладки.
Закладка имеет имя, определяющее путь, используемый pfctl для доступа к закладке в целях выполнения, например, таких операций, как присоединение дочерних закладок или загрузка правил. Закладки могут быть вложенными, при этом компоненты разделяются символами косой чертой (/), аналогично иерархиям файловых систем. Основной набор правил по сути является закладкой по умолчанию; таким образом, в любой закладке могут содержаться, например, правила фильтрации и трансляции.
Закладка может ссылаться на другую точку присоединения закладок посредством правил следующих видов:
Когда при реализации основного набора правил достигается правило закладки, pf реализует все правила, указанные в этой закладке.
Правила сопоставления с фильтром и трансляции в закладках с опцией quick считаются окончательными и отменяют реализацию правил в других закладках и основном наборе правил.
Правила закладки реализуются в отношении закладки, в которой они содержатся. Например, все правила закладки, указанные в основном наборе правил, ссылаются на точки присоединения закладок, являющиеся дочерними по отношению к основному набору правил, и правила закладки, указанные в файле, загруженном из правила загрузки закладки, помещаются в эту точку добавления закладок.
Правила могут содержаться в точках присоединения закладок, которые в момент загрузки основного набора правил не содержат каких-либо правил, и затем pfctl позволяет выполнять манипуляции такими закладками без повторной загрузки основного набора правил или других закладок. Пример.
ext_if = "kue0" block on $ext_if all anchor spam pass out on $ext_if all keep state pass in on $ext_if proto tcp from any \ to $ext_if port smtp keep state
Эта команда блокирует все пакеты на внешних интерфейсах по умолчанию, затем реализует все правила в закладке с именем spam, и, наконец, передает все исходящие и входящие соединения на порт 25.
Следующая команда загружает в закладку одно правило, которое блокирует все пакеты с указанного адреса:
# echo "block in quick from 1.2.3.4 to any" | \ pfctl -a spam -f -
Закладка также может быть создана путем добавления правила load anchor после правила закладки:
anchor spam load anchor spam from "/etc/pf-spam.conf"
При загрузке /etc/pf.conf
утилитой pfctl в закладку также загружаются все правила из файла /etc/pf-spam.conf
.
Кроме того, правила закладки могут определять направление, интерфейс, семейство адресов, протокол и адрес/порт источника/назначения параметра с использованием того же самого синтаксиса, что и для правил фильтрации. При использовании параметров правило закладки анализируется только в отношении соответствующих пакетов. Таким образом, обеспечивается условная реализация закладок, например:
block on $ext_if all anchor spam proto tcp from any to any port smtp pass out on $ext_if all keep state pass in on $ext_if proto tcp from any to $ext_if port smtp keep state
Реализация правил, содержащихся в закладке spam, выполняется только в отношении TCP-пакетов с портом назначения 25. Следовательно, команда
# echo "block in quick from 1.2.3.4 to any" | \ pfctl -a spam -f -
блокирует соединения только с адреса 1.2.3.4 на порт 25.
Закладки могут заканчиваться символом "звездочка" (*); в этом случае все закладки, присоединенные в этой точке, подлежат реализации в алфавитном порядке, в соответствии с именами. Пример.
anchor "spam/*"
Эта команда реализует каждое правило в каждой закладке, присоединенной к закладке spam. Обратите внимание, что данная команда реализует только те закладки, которые присоединены непосредственно к закладке spam, и не реализует нижестоящие закладки.
Поскольку закладки реализуются относительно той закладки, в которой они содержатся, действует механизм доступа к родительским и прародительским закладкам данной закладки. Аналогично разрешению имен путей в файловой системе, если в пути к компоненту закладки используется последовательность .., то родительская закладка текущей закладки при реализации в данной точке становится новой текущей закладкой. Рассмотрим следующий пример:
# echo ' anchor "spam/allowed" ' | pfctl -f - # echo -e ' anchor "../banned" \n pass' | \ pfctl -a spam/allowed -f -
Реализация основного набора правил приводит к закладке spam/allowed, которая реализует правила в закладке spam/banned, при их наличии, и только после этого реализует правило передачи pass.
Поскольку спецификацией анализатора для имен закладок является строка, для любой ссылки на имя закладки, содержащее символы косой черты (/), имя закладки следует заключить в кавычки (").
В этом примере рассматривается сопоставление входящих запросов по порту 80 с портом 8080, который используется сервисом (например, по той причине, что он не был запущен с правами root и, таким образом, полномочия на привязку к порту 80 отсутствуют):
# Использование макроса в качестве имени интерфейса обеспечивает простоту его изменения ext_if = "ne3" # Сопоставление сервиса на порте 8080 с портом 80 rdr on $ext_if proto tcp from any to any port 80 -> 127.0.0.1 port 8080
При использовании модификатора pass пакеты, соответствующие правилу трансляции, передаются без выполнения проверки правил фильтрации:
rdr pass on $ext_if proto tcp from any to any port 80 -> 127.0.0.1 \ port 8080
В приведенном ниже примере для vlan12 определен адрес 192.168.168.1; выполняется автоматическая трансляция всех пакетов, поступающих с адреса 192.168.168.0/24 на 204.92.77.111, если для их передачи используется любой интерфейс, отличный от vlan12. При этом трафик с сетевого адреса 192.168.168.0/24, определяется как трафик с маршрутизируемого Интернет-адреса 204.92.77.111 к узлам за любым интерфейсом маршрутизатора, за исключением узлов в vlan12. (Таким образом, адрес 192.168.168.1 может обращаться к узлам 192.168.168.0/24.)
nat on ! vlan12 from 192.168.168.0/24 to any -> 204.92.77.111
В приведенном ниже примере компьютер расположен между ложной внутренней сетью 144.19.74.* и маршрутизируемым внешним IP-адресом 204.92.77.100. Правило no nat предотвращает трансляцию протокола AH:
# NO NAT no nat on $ext_if proto ah from 144.19.74.0/24 to any nat on $ext_if from 144.19.74.0/24 to any -> 204.92.77.100
В приведенном ниже примере пакеты для одного определенного сервера, а также пакеты, созданные системными администраторами, не передаются через прокси; для всех прочих соединений справедливо следующее:
# NO RDR no rdr on $int_if proto { tcp, udp } from any to $server port 80 no rdr on $int_if proto { tcp, udp } from $sysadmins to any port 80 rdr on $int_if proto { tcp, udp } from any to any port 80 -> 127.0.0.1 \ port 80
В этом более объемном примере используется как трансляция сетевого адреса (NAT), так и перенаправление. Внешний интерфейс имеет адрес 157.161.48.183. На внутреннем интерфейсе запущен ftp-proxy, прослушивающий исходящие сеансы ftp принимаемые портом 8021:
# NAT # Трансляция адресов источников исходящих пакетов (любой протокол). # В этом случае отображается любой адрес, за исключением внешнего адреса шлюза. nat on $ext_if inet from ! ($ext_if) to any -> ($ext_if) # NAT PROXYING # Отображение порта источника исходящих пакетов и назначенного прокси-порта вместо # произвольного порта. # В этом случае исходящий isakmp с портом 500 в шлюзе направляется через прокси. nat on $ext_if inet proto udp from any port = isakmp to any -> ($ext_if) \ port 500 # BINAT # Трансляция адреса источника исходящих пакетов (любой протокол). # Трансляция адреса назначения входящих пакетов на узел во внутренней сети # (двунаправленная). binat on $ext_if from 10.1.2.150 to any -> $ext_if # RDR # Трансляция адресов назначения входящих пакетов. # Пример. Перенаправление портов TCP и UDP на узел во внутренней сети. rdr on $ext_if inet proto tcp from any to ($ext_if) port 8080 \ -> 10.1.2.151 port 22 rdr on $ext_if inet proto udp from any to ($ext_if) port 8080 \ -> 10.1.2.151 port 53 # RDR # Трансляция исходящих управляющих соединений с ftp для их отправки на локальный хост # в качестве прокси посредством ftp-proxy(8), работающего на порте 8021. rdr on $int_if proto tcp from any to any port 21 -> 127.0.0.1 port 8021
В этом примере на шлюзе NAT настраивается трансляция внутренних адресов посредством пула публичных адресов (192.0.2.16/28) и перенаправление входящих подключений к web-серверу на группу web-серверов во внутренней сети:
# NAT LOAD BALANCE # Трансляция адресов источников исходящих пакетов с использованием адресного пула. # Обязательная трансляция указанного адреса источника в адрес того же пула с # использованием ключевого слова source-hash. nat on $ext_if inet from any to any -> 192.0.2.16/28 source-hash # RDR ROUND ROBIN # Трансляция входящих соединений web-сервера с группой web-серверов во # внутренней сети. rdr on $ext_if proto tcp from any to any port 80 \ -> { 10.1.2.155, 10.1.2.160, 10.1.2.161 } round-robin
# Используется внешний интерфейс kue0 # (единственный маршрутизируемый адрес – 157.161.48.183), # и частная сеть 10.0.0.0/8, для которой выполняется NAT. # Использование макроса в качестве имени интерфейса обеспечивает простоту его изменения ext_if = "kue0" # Нормализация всего входящего трафика scrub in on $ext_if all fragment reassemble # Блокировка всего трафика по умолчанию с регистрацией в журнале block return log on $ext_if all # Блокировка всего трафика, поступающего из источника, для которого отсутствуют обратные маршруты block in from no-route to any # Блокировка и регистрация исходящих пакетов, у которых адрес источника не совпадает с нашим адресом, # т.е. они либо имитируются, либо возникла ошибка конфигурации (например, отключена # функция NAT), мы предпочитаем не рассылать свой мусор. block out log quick on $ext_if from ! 157.161.48.183 to any # Широковещательные пакеты (шум от кабельного модема) игнорируются без оповещения. block in quick on $ext_if from any to 255.255.255.255 # Блокировка и регистрация входящих пакетов из резервного адресного пространства и недействительных # адресов, т.е. они либо имитируются, либо возникла ошибка конфигурации. В любом случае, # на них невозможно ответить (следовательно, отсутствует return-rst). block in log quick on $ext_if from { 10.0.0.0/8, 172.16.0.0/12, \ 192.168.0.0/16, 255.255.255.255/32 } to any # ICMP # Передача определенных входящих/исходящих ICMP-запросов и сохранение состояния (ping) # Сопоставление состояний выполняется по адресам хоста и ICMP ID (не типу/коду), # поэтому ответы (как 0/0 для 8/0) будут соответствовать запросам # Сообщения об ошибках ICMP (которые всегда относятся к пакету TCP/UDP) # обрабатываются состояниями TCP/UDP pass on $ext_if inet proto icmp all icmp-type 8 code 0 keep state # UDP # Передача определенных исходящих UDP-соединений и сохранение состояний pass out on $ext_if proto udp all keep state # Передача определенных входящих UDP-соединений и сохранение состояний (DNS) pass in on $ext_if proto udp from any to any port domain keep state # TCP # Передача всех исходящих TCP-подключений и модуляция состояния pass out on $ext_if proto tcp all modulate state # Передача определенных входящих TCP-соединений и сохранение состояния (SSH, SMTP, DNS, IDENT) pass in on $ext_if proto tcp from any to any port { ssh, smtp, domain, \ auth } flags S/SA keep state # Передача входящих соединений в режиме передачи данных для ftp-proxy, функционирующего на этом хосте. # (для получения дополнительной информации см. ftp-proxy(8)) pass in on $ext_if proto tcp from any to 157.161.48.183 port >= 49152 \ flags S/SA keep state # Запрет SMTP-соединений Windows 9x, поскольку они, как правило, являются # вирусными червями. В качестве альтернативы для этих ОС можно установить ограничение в 1 соединение. block in on $ext_if proto tcp from any os {"Windows 95", "Windows 98"} \ to any port smtp # Присвоение тегов пакетам # Три интерфейса: $int_if, $ext_if, and $wifi_if (беспроводной). NAT выполняется # в $ext_if для всех исходящих пакетов. Теги пакетов создаются в # $int_if, и пакеты с тегами выходят в $ext_if. Всем прочим # исходящим пакетам (т.е. пакетам из беспроводной сети) разрешен # доступ только через порт 80. pass in on $int_if from any to any tag INTNET keep state pass in on $wifi_if from any to any keep state block out on $ext_if from any to any pass out quick on $ext_if tagged INTNET keep state pass out on $ext_if proto tcp from any to any port 80 keep state
Для /etc/pf.conf
в BNF используется следующий синтаксис:
line = ( option | pf-rule | nat-rule | binat-rule | rdr-rule | antispoof-rule | altq-rule | queue-rule | anchor-rule | trans-anchors | load-anchors | table-rule ) option = "set" ( [ "timeout" ( timeout | "{" timeout-list "}" ) ] | [ "optimization" [ "default" | "normal" | "high-latency" | "satellite" | "aggressive" | "conservative" ] ] [ "limit" ( limit-item | "{" limit-list "}" ) ] | [ "loginterface" ( interface-name | "none" ) ] | [ "block-policy" ( "drop" | "return" ) ] | [ "state-policy" ( "if-bound" | "group-bound" | "floating" ) ] [ "require-order" ( "yes" | "no" ) ] [ "fingerprints" filename ] | [ "debug" ( "none" | "urgent" | "misc" | "loud" ) ] ) pf-rule = action [ ( "in" | "out" ) ] [ "log" | "log-all" ] [ "quick" ] [ "on" ifspec ] [ route ] [ af ] [ protospec ] hosts [ filteropt-list ] filteropt-list = filteropt-list filteropt | filteropt filteropt = user | flags | icmp-type | icmp6-type | tos | ( "keep" | "modulate" | "synproxy" ) "state" [ "(" state-opts ")" ] | "fragment" | "no-df" | "min-ttl" number | "max-mss" number | "random-id" | "reassemble tcp" | fragmentation | "allow-opts" | "label" string | "tag" string | [ ! ] "tagged" string "queue" ( string | "(" string [ [ "," ] string ] ")" ) | "probability" number"%" nat-rule = [ "no" ] "nat" [ "pass" ] [ "on" ifspec ] [ af ] [ protospec ] hosts [ "tag" string ] [ "tagged" string ] [ "->" ( redirhost | "{" redirhost-list "}" ) [ portspec ] [ pooltype ] [ "static-port" ] ] binat-rule = [ "no" ] "binat" [ "pass" ] [ "on" interface-name ] [ af ] [ "proto" ( proto-name | proto-number ) ] "from" address [ "/" mask-bits ] "to" ipspec [ "tag" string ] [ "tagged" string ] [ "->" address [ "/" mask-bits ] ] rdr-rule = [ "no" ] "rdr" [ "pass" ] [ "on" ifspec ] [ af ] [ protospec ] hosts [ "tag" string ] [ "tagged" string ] [ "->" ( redirhost | "{" redirhost-list "}" ) [ portspec ] [ pooltype ] ] antispoof-rule = "antispoof" [ "log" ] [ "quick" ] "for" ( interface-name | "{" interface-list "}" ) [ af ] [ "label" string ] table-rule = "table" "<" string ">" [ tableopts-list ] tableopts-list = tableopts-list tableopts | tableopts tableopts = "persist" | "const" | "file" string | "{" [ tableaddr-list ] "}" tableaddr-list = tableaddr-list [ "," ] tableaddr-spec | tableaddr-spec tableaddr-spec = [ "!" ] tableaddr [ "/" mask-bits ] tableaddr = hostname | ipv4-dotted-quad | ipv6-coloned-hex | interface-name | "self" altq-rule = "altq on" interface-name queueopts-list "queue" subqueue queue-rule = "queue" string [ "on" interface-name ] queueopts-list subqueue anchor-rule = "anchor" string [ ( "in" | "out" ) ] [ "on" ifspec ] [ af ] [ "proto" ] [ protospec ] [ hosts ] trans-anchors = ( "nat-anchor" | "rdr-anchor" | "binat-anchor" ) string [ "on" ifspec ] [ af ] [ "proto" ] [ protospec ] [ hosts ] load-anchor = "load anchor" string "from" filename queueopts-list = queueopts-list queueopts | queueopts queueopts = [ "bandwidth" bandwidth-spec ] | [ "qlimit" number ] | [ "tbrsize" number ] | [ "priority" number ] | [ schedulers ] schedulers = ( cbq-def | priq-def | hfsc-def ) bandwidth-spec = "number" ( "b" | "Kb" | "Mb" | "Gb" | "%" ) action = "pass" | "block" [ return ] | [ "no" ] "scrub" return = "drop" | "return" | "return-rst" [ "( ttl" number ")" ] | "return-icmp" [ "(" icmpcode [ [ "," ] icmp6code ] ")" ] | "return-icmp6" [ "(" icmp6code ")" ] icmpcode = ( icmp-code-name | icmp-code-number ) icmp6code = ( icmp6-code-name | icmp6-code-number ) ifspec = ( [ "!" ] interface-name ) | "{" interface-list "}" interface-list = [ "!" ] interface-name [ [ "," ] interface-list ] route = "fastroute" | ( "route-to" | "reply-to" | "dup-to" ) ( routehost | "{" routehost-list "}" ) [ pooltype ] af = "inet" | "inet6" protospec = "proto" ( proto-name | proto-number | "{" proto-list "}" ) proto-list = ( proto-name | proto-number ) [ [ "," ] proto-list ] hosts = "all" | "from" ( "any" | "no-route" | "self" | host | "{" host-list "}" | "route" string ) [ port ] [ os ] "to" ( "any" | "no-route" | "self" | host | "{" host-list "}" | "route" string ) [ port ] ipspec = "any" | host | "{" host-list "}" host = [ "!" ] ( address [ "/" mask-bits ] | "<" string ">" ) redirhost = address [ "/" mask-bits ] routehost = ( interface-name [ address [ "/" mask-bits ] ] ) address = ( interface-name | "(" interface-name ")" | hostname | ipv4-dotted-quad | ipv6-coloned-hex ) host-list = host [ [ "," ] host-list ] redirhost-list = redirhost [ [ "," ] redirhost-list ] routehost-list = routehost [ [ "," ] routehost-list ] port = "port" ( unary-op | binary-op | "{" op-list "}" ) portspec = "port" ( number | name ) [ ":" ( "*" | number | name ) ] os = "os" ( os-name | "{" os-list "}" ) user = "user" ( unary-op | binary-op | "{" op-list "}" ) unary-op = [ "=" | "!=" | "<" | "<=" | ">" | ">=" ] ( name | number ) binary-op = number ( "<>" | "><" | ":" ) number op-list = ( unary-op | binary-op ) [ [ "," ] op-list ] os-name = operating-system-name os-list = os-name [ [ "," ] os-list ] flags = "flags" [ flag-set ] "/" flag-set flag-set = [ "F" ] [ "S" ] [ "R" ] [ "P" ] [ "A" ] [ "U" ] [ "E" ] [ "W" ] icmp-type = "icmp-type" ( icmp-type-code | "{" icmp-list "}" ) icmp6-type = "icmp6-type" ( icmp-type-code | "{" icmp-list "}" ) icmp-type-code = ( icmp-type-name | icmp-type-number ) [ "code" ( icmp-code-name | icmp-code-number ) ] icmp-list = icmp-type-code [ [ "," ] icmp-list ] tos = "tos" ( "lowdelay" | "throughput" | "reliability" | [ "0x" ] number ) state-opts = state-opt [ [ "," ] state-opts ] state-opt = ( "max" number | timeout | "source-track" [ ( "rule" | "global" ) ] | "max-src-nodes" number | "max-src-states" number | "max-src-conn" number | "max-src-conn-rate" number "/" number | "overload" "<" string ">" [ "flush" ] | "if-bound" | "group-bound" | "floating" ) fragmentation = [ "fragment reassemble" | "fragment crop" | "fragment drop-ovl" ] timeout-list = timeout [ [ "," ] timeout-list ] timeout = ( "tcp.first" | "tcp.opening" | "tcp.established" | "tcp.closing" | "tcp.finwait" | "tcp.closed" | "udp.first" | "udp.single" | "udp.multiple" | "icmp.first" | "icmp.error" | "other.first" | "other.single" | "other.multiple" | "frag" | "interval" | "src.track" | "adaptive.start" | "adaptive.end" ) number limit-list = limit-item [ [ "," ] limit-list ] limit-item = ( "states" | "frags" | "src-nodes" ) number pooltype = ( "bitmask" | "random" | "source-hash" [ ( hex-key | string-key ) ] | "round-robin" ) [ sticky-address ] subqueue = string | "{" queue-list "}" queue-list = string [ [ "," ] string ] cbq-def = "cbq" [ "(" cbq-opt [ [ "," ] cbq-opt ] ")" ] priq-def = "priq" [ "(" priq-opt [ [ "," ] priq-opt ] ")" ] hfsc-def = "hfsc" [ "(" hfsc-opt [ [ "," ] hfsc-opt ] ")" ] cbq-opt = ( "default" | "borrow" | "red" | "ecn" | "rio" ) priq-opt = ( "default" | "red" | "ecn" | "rio" ) hfsc-opt = ( "default" | "red" | "ecn" | "rio" | linkshare-sc | realtime-sc | upperlimit-sc ) linkshare-sc = "linkshare" sc-spec realtime-sc = "realtime" sc-spec upperlimit-sc = "upperlimit" sc-spec sc-spec = ( bandwidth-spec | "(" bandwidth-spec number bandwidth-spec ")" )
Базовые подсистемы ЗОСРВ «Нейтрино», NetBSD
/etc/hosts, pf, pfctl, /etc/protocols, route, /etc/services
ICMP, ICMP6, IP, IP6, ROUTE, TCP, UDP
ftp-proxy, pcap, pflog, pflogd
Предыдущий раздел: Конфигурационные файлы