/etc/pf.conf
Конфигурационный файл
фильтра пакетов
Имя
/etc/pf.conf
Описание:
Фильтр пакетов pf изменяет, сбрасывает или
передает пакеты в соответствии с правилами или определениями
в файле pf.conf. По умолчанию этот файл
расположен по пути /etc/pf.conf.
Порядок
операторов
Файл pf.conf может содержать операторы
следующих типов:
Макросы
Пользователь может
определить собственные переменные и использовать их в
дальнейшем, что позволяет упростить конфигурационный файл.
Макросы необходимо определить до добавления ссылки на них в
pf.conf.
Таблицы
Таблицы представляют собой
инструмент повышения эффективности и гибкости правил с
большим количеством адресов источников и назначения.
Опции
Опции определяют поведение
механизма фильтрации пакетов.
Нормализация трафика
(например, очистка)
Нормализация трафика
позволяет защитить машины во внутренней сети от конфликтов
Интернет-протоколов и реализаций.
Формирование очередей
Формирование очередей
обеспечивает управление полосой пропускания на основе
правил.
Трансляция (различные
формы NAT)
Правила трансляции
определяют способы отображения адресов или перенаправления
на другие адреса.
Фильтрация пакетов
Фильтрация пакетов с
запоминанием и без запоминания состояния обеспечивает
блокировку или передачу пакетов на основе правил.
За исключением макросов и
таблиц, операторы следует группировать по типу и добавлять в
файл 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. Как и для макросов, в
качестве имени таблицы не допускается использовать
зарезервированные слова.
Вручную
Постоянные таблицы можно
создать вручную, с использованием опции добавления или опции
замены pfctl, до или после загрузки
набора правил.
С помощью pf.conf
Определения таблиц можно
поместить непосредственно в этот файл и загрузить их
одновременно с другими правилами, атомарно. В определениях
таблиц в файле pf.conf используется табличный
оператор; этот способ наиболее эффективен для определения
временных таблиц. Содержимое предварительно созданной
таблицы, для инициализации которой список адресов в
определении не использовался, при загрузке файла pf.conf не изменяется. Таблица,
инициализированная на основе пустого списка { }, при загрузке очищается.
Для таблиц можно определить
следующие атрибуты:
persist
Менеджер io-pkt сохраняет таблицу даже при
отсутствии ссылающихся на нее правил. Если этот флаг не
установлен, то io-pkt автоматически удаляет
таблицу после исполнения последнего ссылающегося на нее
правила.
const
После создания
содержимое таблицы не доступно для изменения пользователем.
Если этот флаг не установлен, то с помощью утилиты pfctl можно добавлять или удалять
адреса из таблицы в любой момент, даже в том случае, если securelevel =
2.
Пример.
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-адресу, их
также можно определить по именам. При вызове преобразователя
адресов для добавления имени хоста в таблицу все полученные
в результате адреса IPv4 и IPv6 добавляются в эту таблицу.
IP-адреса также можно добавлять в таблицу путем определения
действительного имени интерфейса или ключевого слова self.
При этом в таблицу добавляются все адреса, присвоенные
интерфейсу (интерфейсам).
Опции:
С помощью команды set можно настроить pf для различных ситуаций:
set timeout аргументы
Настроить таймаут,
определенный аргументами:
-
interval – интервал между
удалением завершенных состояний и фрагментов.
-
frag – количество секунд до
завершения несобранного фрагмента.
-
src.track – период хранения
записи отслеживания источника после завершения
последнего состояния.
Если пакет
соответствует соединению с запоминанием состояния,
оставшееся время соединения в секундах устанавливается
равным proto.modifier, что соответствует
состоянию соединения. При поступлении пакета,
соответствующего этому состоянию, время существования
сбрасывается. Установка этих значений позволяет повысить
эффективность межсетевого экрана, однако при этом
присутствует риск сброса неактивных действительных
соединений.
-
tcp.first – состояние после
первого пакета.
-
tcp.opening – состояние до отправки
первого пакета хостом-получателем.
-
tcp.established – полностью стабильное
состояние.
-
tcp.closing – состояние после
отправки первого FIN.
-
tcp.finwait – состояние после
обмена обоими FIN и закрытия соединения. Некоторые хосты
(в частности, web-серверы Solaris) отправляют TCP-пакеты
даже после закрытия соединения. Увеличение значения tcp.finwait (и, возможно, tcp.closing) позволяет
предотвратить блокирование таких пакетов.
-
tcp.closed – состояние после
отправки RST одной из конечных точек.
Обработка трафика ICMP и UDP
выполняется аналогично обработке TCP, однако набор состояний
ограничен:
-
udp.first – состояние после
первого пакета.
-
udp.single – состояние после
отправки хостом-отправителем более одного пакета,
которые не были возвращены хостом- получателем.
-
udp.multiple – состояние после
отправки пакетов обоими хостами.
-
icmp.first – состояние после
первого пакета.
-
icmp.error – состояние после
возвращения ошибки ICMP в ответ на отправку ICMP-пакета.
Обработка трафика других
протоколов выполняется аналогично UDP:
-
other.first
-
other.single
-
other.multiple
Значения таймаута можно
адаптивно уменьшать при увеличении количества записей в
таблице состояний.
-
adaptive.start – запуск адаптивного
масштабирования, если количество записей состояния
превышает это значение. Все значения таймаута
масштабируются линейно с использованием коэффициента,
вычисляемого как (adaptive.end − количество состояний)
/ (adaptive.end − adaptive.start).
-
adaptive.end – по достижении этого
количества записей состояния все значения таймаутов
обнуляются, что приводит к немедленному удалению всех
записей состояния. Это значение используется для
определения коэффициента масштабирования; фактическое
достижение которого не рекомендуется (следует установить
более низкое предельное значение, см. далее).
Эти значения
можно установить как для всех правил, так и для каждого по
отдельности. Если значения установлены для каждого правила,
то они относятся к количеству состояний, созданных правилом,
в противном случае – к общему количеству состояний. Пример.
set timeout tcp.first 120
set timeout tcp.established
86400
set timeout { adaptive.start
6000, adaptive.end 12000 }
set limit states 10000
При наличии в таблице 9000
записей состояний значения таймаута масштабируются до 50% (tcp.first 60, tcp.established 43200).
set
loginterface
Активировать сбор статистики
пакетов и байтов по данному интерфейсу. Для просмотра этой
статистики используется следующая команда:
pfctl -s info
В этом примере pf собирает статистику по
интерфейсу с именем dc0:
set loginterface dc0
Интерфейс регистрации данных
можно деактивировать с помощью следующей команды:
set loginterface none
set limit
Установить жесткие ограничения
для пулов памяти, используемых фильтром пакетов. Пример.
set limit states 20000
С помощью этой
команды максимальное количество записей в пуле памяти,
используемых записями в таблице состояний (созданными
правилами сохранения состояний), установлено на уровне 20
000.
set limit frags 20000
С помощью этой
команды максимальное количество записей в пуле памяти,
используемых для восстановления пакета из фрагментов
(созданных правилами очистки), установлено на уровне 20 000.
set limit src-nodes 2000
С помощью этой
команды максимальное количество записей в пуле памяти,
используемых для отслеживания IP-адресов источника
(созданных с помощью sticky-адресов иопций отслеживания
источников), установлено на уровне 2000.
Эти команды можно
использовать в комбинации:
set limit { states 20000,
frags 20000, src-nodes 2000 }
set
optimization
Оптимизировать механизм для
одной из следующих сетевых сред:
-
normal – нормальная сетевая
среда. Подходит почти для всех сетей.
-
high-latency – среда с большими
задержками (например, спутниковое соединение).
-
satellite – псевдоним для high-latency.
-
aggressive – агрессивное
завершение соединений. Позволяет существенно
оптимизировать использование памяти межсетевого экрана
путем раннего сброса неактивных соединений.
-
conservative – наиболее
консервативные настройки. Позволяет предотвратить сброс
действительных соединений за счет увеличения потребления
памяти (значительно более существенного в загруженной
сети) и незначительного увеличения потребления ресурсов
процессора.
Пример.
set optimization aggressive
set
block-policy
Определить поведение по
умолчанию для действия блокировки пакетов:
-
drop – сброс пакета без
оповещения.
-
return – для блокированных
TCP-пакетов возвращается флаг "TCP RST", для
блокированных UDP-пакетов – "ICMP UNREACHABLE",
остальные пакеты отбрасываются без оповещения.
Пример.
set block-policy return
set
state-policy
Определить поведение по умолчанию для следующих состояний:
-
if-bound – состояния привязаны к
интерфейсу.
-
group-bound – состояния привязаны к
группе интерфейсов (например, ppp).
-
floating – состояния могут
соответствовать пакетам в любых интерфейсах (по
умолчанию).
Пример.
set state-policy if-bound
set
require-order
По умолчанию pfctl обеспечивает соблюдение
следующего порядка следования операторов по типам в наборе
правил: опции, нормализация, формирование очередей,
трансляция и фильтрация. Если для этой опции установлено
значение no, этот порядок не
соблюдается.
Примечание.
Последствия применения противоречивого набора правил могут
быть нестандартны и неочевидны. Следует убедиться, что
деактивация этого порядка действительно необходима.
set
fingerprints
Загрузить
отпечатки известных операционных систем на основе имени
файла. По умолчанию отпечатки известных операционных систем
автоматически загружаются из файла /etc/pf.os; с помощью данной опции
этот путь можно переопределить. Пример.
set fingerprints
"/etc/pf.os.devel"
Если эта опция
установлена, то в течение непродолжительного периода
отпечатки, на которые ссылается активный в настоящий момент
набор правил, могут конфликтовать до завершения загрузки
нового набора правил.
set skip on спецификация_интерфейса
Создать список
интерфейсов, для которых не требуется фильтрация пакетов.
Пакеты, проходящие через такие интерфейсы в обоих
направлениях, передаются так же, как и при деактивированной
утилите pf, т.е. pf не выполняет их обработку.
Это удобно для интерфейса обратной связи и других
виртуальных интерфейсов, когда фильтрация пакетов
нежелательна и может привести к неожиданным результатам.
Пример.
set skip on lo0
set debug
Установить один из следующих
уровней отладки:
-
none – не создавать
сообщения отладки.
-
urgent – создавать сообщения
отладки только для серьезных ошибок.
-
misc – создавать сообщения
отладки для различных ошибок.
-
loud – создавать сообщения
отладки для общих условий.
Нормализация трафика
Нормализация трафика
используется для очистки содержимого пакета и позволяет
исключить неточную интерпретацию пакета на принимающей
стороне. Нормализатор объединяет IP-фрагменты для
предотвращения атак, влияющих на системы обнаружения
вторжения путем передачи перекрывающихся IP- фрагментов.
Нормализация пакетов запускается посредством директивы scrub, для которой можно
установить следующие опции:
no-df
Удалить бит dont-fragment из соответствующего
IP-пакета. В некоторых операционных системах при
установленном бите dont-fragment все равно создаются
фрагментированные пакеты. Это, в частности, справедливо для
NFS. Если значение no-df не определено, то такие
фрагментированные пакеты dont-fragment сбрасываются директивой
scrub.
К сожалению, в некоторых
операционных системах также генерируются пакеты dont-fragment, которые содержат в поле
IP- идентификатора нулевое значение. При последующей
фрагментации пакетов вышестоящим маршрутизатором удаление
бита dont- fragment в пакетах с
IP-идентификатором, равным нулю, может привести к
нежелательным результатам. Для обеспечения уникальности IP-
идентификаторов рекомендуется использовать модификатор random-id (см. далее) в сочетании с
модификатором no-df.
min-ttl число
Установить минимальное время
существования для соответствующих IP-пакетов.
max-mss число
Установить максимальный размер
сегмента для соответствующих TCP-пакетов.
random-id
Заменять
значение в поле IP-идентификатора произвольными значениями
для компенсации предсказуемых значений, генерируемых многими
хостами. Эта опция применяется только для пакетов, которые
не были фрагментированы после необязательного объединения
фрагментов.
fragment
reassemble
Фрагменты можно
объединить в процессе нормализации с использованием правил
очистки. В этом случае фрагменты помещаются в буфер и
остаются там до тех пор, пока не будет сформирован полный
пакет, который передается в фильтр. Преимущество состоит в
том, что на основе правил фильтрации обрабатываются только
полные пакеты, тогда как фрагменты пакетов игнорируются.
Возврат фрагментов из кэша повышает потребление ресурсов
памяти. Однако метод полного объединения является
единственным методом, применяемым в настоящее время для NAT.
Если модификатор фрагментации не указан, то это поведение
правила очистки применяется по умолчанию.
fragment crop
Поскольку метод
объединения фрагментов по умолчанию является
ресурсозатратным, можно использовать специальную опцию для
обрезки. В этом случае pf отслеживает фрагменты и
помещает в кэш дескриптор с небольшим диапазоном.
Дублирующиеся фрагменты сбрасываются, а частично совпадающие
фрагменты обрезаются. Таким образом, обеспечивается только
однократная передача данных. В отличие от случая применения
модификатора fragment reassemble, фрагменты не помещаются в
буфер, а передаются сразу после получения. Метод объединения
fragment crop для подключений NAT в
настоящее время не реализован.
fragment
drop-ovl
Эта опция
аналогична модификатору fragment crop, за исключением того, что
все частично совпадающие или дублирующиеся фрагменты, а
также все последующие соответствующие фрагменты
сбрасываются.
reassemble
tcp
Выполнять
нормализацию TCP-соединений с запоминанием состояния. Для
правил очистки reassemble tcp определять направление
("in/out") необязательно. Команда reassemble
tcp
позволяет выполнять следующие операции нормализации:
-
ttl – ни одной из сторон
соединения не разрешено уменьшать время существования
IP-пакетов. Злоумышленник может отправить пакет,
который, попадая в межсетевой экран, изменяет его
состояние и уничтожается до достижения хоста-получателя.
Команда reassemble tcp увеличивает время
существования всех пакетов до наибольшего допустимого
для данного соединения значения.
-
timestamp modulation – современные TCP-стеки
отправляют временную метку в каждом TCP-пакете и выводят
информацию о временной метке другой конечной точки. Во
многих операционных системах временная метка при первой
загрузке обнуляется, и затем ее значение увеличивается с
шагом приращения несколько секунд. Время безотказной
работы хоста можно рассчитать путем чтения временной
метки и ее умножения на константу. Также существует
возможность контроля нескольких временных меток для
подсчета хостов за устройством NAT. Для имитации
TCP-пакетов в соединении необходима информация о
действительных временных метках. Значения временных
меток должны равномерно увеличиваться и не могут
основываться на легко угадываемых временных данных.
Выполнение команды reassemble
tcp приводит к моделированию временных меток
TCP на основе случайных чисел при очистке.
-
extended PAWS checks – при использовании TCP
на мощных широких каналах возникает проблема, связанная
с возможной задержкой пакета на более продолжительное
время по сравнению с тем, которое требуется для
циклического сброса соединением 32-битного пространства
последовательностей. В каждом таком случае старый пакет
неотличим от нового и обрабатывается как новый. Решить
эту проблему позволяет алгоритм PAWS (Protection Against
Wrapped Sequence – защита от некорректной нумерации
последовательностей). Эта защита обеспечивается за счет
предотвращения уменьшения значения временной метки в
каждом пакете. Команда reassemble
tcp также позволяет не допустить превышения
значения временной метки в пакете (по сравнению с
максимальным допустимым значением RFC). При этом pf принудительно повышает
безопасность номеров последовательностей TCP, удлиняя их
на 10–18 битов, когда хост использует соответствующие
произвольные временные метки; таким образом,
злоумышленник также будет вынужден определять временную
метку вслепую.
Пример.
scrub in on $ext_if all
fragment reassemble
Если перед
правилом очистки указана опция no, то обработка
соответствующих пакетов не выполняется, аналогично быстрому
сбросу в фильтре пакетов (см. далее). Этот механизм
используется при необходимости исключения обработки
определенных пакетов более сложными правилами очистки.
Формирование
очередей
В целях управления полосой
пропускания пакеты могут присваиваться очередям. Для
настройки очередей необходимо выполнить, по меньшей мере,
два объявления, и в дальнейшем в любое правило фильтрации
пакетов можно добавить в качестве ссылок имена определенных
очередей. Во время выполнения фильтрации компонентов,
определенных в pf.conf, пакеты, переданные согласно
правилам передачи, помещаются в очередь, имя которой было
указано в последней ссылке, а для правил блокировки он
определяет очередь, в которую помещаются все итоговые пакеты
ICMP и TCP RST. Диспетчер определяет алгоритм, используемый
для определения пакетов, которые должны быть отложены,
сброшены или немедленно переданы.
В настоящее время
поддерживаются следующие диспетчеры:
cbq
Class Based
Queueing, формирование очередей на основе класса. Очереди,
связанные с интерфейсом, формируют дерево; таким образом,
каждая очередь может иметь несколько дочерних очередей.
Каждой очереди можно назначить приоритет и полосу
пропускания. Приоритет задает время, необходимое для
отправки пакетов, тогда как полоса пропускания определяет,
прежде всего, производительность.
Диспетчер cbq обеспечивает как
декомпозицию, так и разделение полосы пропускания канала
между классами с иерархической структурой. Каждый класс
имеет собственную очередь, и ему присваивается определенная
доля полосы пропускания. При наличии избыточной полосы
пропускания дочерний класс может использовать полосу
пропускания родительского класса (см. описание опции borrow далее).
priq
Priority
Queueing, формирование очередей на основе приоритета.
Очереди имеют прямую привязку к интерфейсу, в результате
чего формирование дочерних очередей невозможно. Каждой
очереди присваивается уникальный приоритет от 0 до 15.
Сначала обрабатываются пакеты в очереди с наиболее высоким
приоритетом.
hfsc
Hierarchical
Fair Service Curve, иерархическая характеристика
обслуживания. Очереди, связанные с интерфейсом, формируют
дерево; таким образом, каждая очередь может иметь несколько
дочерних очередей. Каждой очереди можно назначить приоритет
и полосу пропускания. Приоритет задает время, необходимое
для отправки пакетов, тогда как полоса пропускания
определяет, прежде всего, производительность.
Диспетчер hfsc поддерживает совместное
использование канала и гарантированное качество обслуживания
в режиме реального времени. Он основан на модели QoS,
построенной на характеристике обслуживания. Уникальная
функция этого диспетчера – возможность разделения связи
между задержками и распределением полосы пропускания.
Интерфейсы, в которых
требуется активировать формирование очередей, объявляются
посредством декларации altq on, для чего используются
следующие ключевые слова:
интерфейс
Активировать формирование
очередей в указанном интерфейсе.
диспетчер
Выбрать диспетчер для
формирования очередей. Допускаются следующие значения:
-
cbq – формирование очередей
на основе класса.
-
priq – формирование очередей
на основе приоритета
-
hfsc – иерархическая
характеристика обслуживания.
bandwidth пропускная_способность
Максимальная
скорость двоичной передачи в битах для всех очередей в
интерфейсе. Пропускную способность интерфейса можно
определить в виде абсолютного или процентного значения. При
выборе абсолютного значения можно использовать индексы b, Kb, Mb и Gb, обозначающие
соответственно биты, килобиты, мегабиты и гигабиты в
секунду. Это значение не должно превышать пропускную
способность интерфейса. Если аргумент bandwidth не указан, используется
пропускная способность интерфейса.
qlimit предел
Максимальное количество
пакетов в очереди. Значение по умолчанию – 50.
tbrsize размер
Изменить размер регулятора
наборов лексем в байтах. Если размер не задан явно, то он
определяется эвристическим методом на основе пропускной
способности интерфейса.
queue список
Определить
список подочередей, которые требуется создать в интерфейсе.
В следующем примере
интерфейс dc0 должен формировать четыре
четырехсекундные очереди до 5 Мбит/с с использованием
формирования очередей на основе класса. Эти четыре очереди
будут описаны в примерах далее.
altq on dc0 cbq bandwidth
5Mb queue { std, http, mail, ssh }
После активации
интерфейсов для формирования очереди с помощью директивы altq можно определить
последовательность директив очереди. Назначенное очереди имя
должно соответствовать очереди, указанной в директиве altq (например, mail), или в объявлении
родительской очереди, за исключением диспетчера priq. Используются следующие
ключевые слова:
-
on интерфейс – интерфейс, с которым
работает очередь. Если эта опция не указана, то очередь
активируется во всех соответствующих интерфейсах.
-
bandwidth пропускная_способность – максимальная скорость
двоичной передачи в битах, обрабатываемая очередью. Это
значение не должно превышать значения родительской
очереди. Пропускную способность можно задать в виде
абсолютного значения или процентного значения от
пропускной способности родительской очереди. Если это
ключевое слово не указано, то пропускная способность по
умолчанию составляет 100% пропускной способности для
родительской очереди. Для диспетчера priq поддержка определения
пропускной способности не реализована.
-
priority уровень – для очередей можно
установить уровень приоритета. Диапазон значений для cbq и hfsc – от 0 до 7; для priq – от 0 до 15. По
умолчанию для всех диспетчеров установлено значение 1.
Очереди PRIQ с более высоким приоритетом всегда
обрабатываются первыми. В случае перегрузки первыми
обрабатываются очереди CBQ и HFSC с высоким приоритетом.
-
qlimit предел – максимальное
количество пакетов в очереди. Значение по умолчанию –
50.
В планировщик можно передавать
дополнительные параметры в формате диспетчер (параметры).
Доступны следующие параметры:
-
default – в эту очередь
помещаются пакеты, не назначенные какой-либо другой
очереди. Очередь по умолчанию должна быть единственной.
-
red – активировать RED
(Random Early Detection – раннее случайное обнаружение)
для данной очереди. RED сбрасывает пакеты с
вероятностью, пропорциональной средней длине очереди.
-
rio – активировать RIO для
данной очереди. RIO представляет собой RED с функциями
IN/OUT, соответственно, количество запусков RED, в два
раза превышающее количество запусков RIO, приведет к
тому же результату. В настоящее время не реализована
поддержка RIO в ядре GENERIC.
-
ecn – активировать ECN
(Explicit Congestion Notification – уведомление о
перегрузке) для этой очереди. ECN предполагает
обязательное использование RED.
Диспетчер CBQ поддерживает
дополнительную опцию:
Диспетчер HFSC поддерживает
ряд дополнительных опций (sc – сокращение от "service
curve", характеристика обслуживания):
-
realtime sc – минимальная
необходимая пропускная способность для данной очереди.
-
upperlimit sc – максимальная
допустимая пропускная способность для данной очереди.
-
linkshare sc – доля пропускной
способности для задержанной очереди.
Для
характеристик обслуживания можно указать спецификацию в
следующем формате: (m1, d, m2).
Переменная m2 определяет пропускную способность,
выделенную для очереди; переменные m1 и d
являются необязательными и могут использоваться для
управления первоначально выделенной пропускной способностью.
На первые d миллисекунд очереди предоставляется
пропускная способность, указанная для m1, а затем –
для m2.
Кроме того, в случае CBQ и
HFSC можно указать дочерние очереди, как в объявлении altq, в результате чего
формируется дерево очередей, занимающих часть пропускной
способности соответствующих родительских очередей.
Пакеты можно присваивать
очередям на основании правил фильтрации с помощью ключевого
слова queue. Как правило, указывается
только одна очередь; если указана вторая очередь, то в нее
помещаются пакеты с типом обслуживания lowdelay и TCP-пакеты ACK без
значащих данных.
В нижеприведенных примерах,
в продолжение предыдущего примера, определяются четыре
очереди, на которые была создана ссылка, а также несколько
дочерних очередей. Впоследствии на эти очереди можно
добавить ссылку в правилах фильтрации (см. раздел
"Фильтрация пакетов" далее).
queue std bandwidth 10%
cbq(default)
queue http bandwidth 60%
priority 2 cbq(borrow red) \
{ employees, developers }
queue developers bandwidth
75% cbq(borrow)
queue employees bandwidth
15%
queue mail bandwidth 10%
priority 0 cbq(borrow ecn)
queue ssh bandwidth 20%
cbq(borrow) { ssh_interactive, ssh_bulk }
queue ssh_interactive
bandwidth 50% priority 7 cbq(borrow)
queue ssh_bulk bandwidth 50%
priority 0 cbq(borrow)
block return out on dc0 inet
all queue std
pass out on dc0 inet proto
tcp from $developerhosts to any port 80 \
keep state queue developers
pass out on dc0 inet proto
tcp from $employeehosts to any port 80 \
keep state queue employees
pass out on dc0 inet proto
tcp from any to any port 22 \
keep state queue(ssh_bulk,
ssh_interactive)
pass out on dc0 inet proto
tcp from any to any port 25 \
keep state queue mail
Трансляция
Правила трансляции
обеспечивают изменение адреса источника или получателя для
пакетов, соответствующих соединению с запоминанием
состояния. Соединение с запоминанием состояния автоматически
создается для отслеживания пакетов, удовлетворяющих данному
правилу, если они не блокируются секцией фильтрации pf.conf. Механизм трансляции
модифицирует указанный адрес и/или порт пакета, выполняет
перерасчет контрольных сумм IP, TCP и UDP по мере
необходимости, и передает пакет в фильтр пакетов для
обработки.
Поскольку трансляция
производится до фильтрации, механизм фильтрации получает
пакеты в том виде, который они имеют после трансляции всех
адресов и портов. Таким образом, правила фильтрации
обеспечивают фильтрацию на основе уже преобразованного
адреса и номера порта. Пакеты, соответствующие правилу
трансляции, передаются автоматически только в том случае,
если указан модификатор pass; в противном случае они
подлежат обработке по правилам блокирования и передачи.
Созданная запись состояния
позволяет pf отслеживать адрес источника
для трафика, связанного с этим состоянием, и корректно
перенаправлять обратный трафик для данного соединения.
Возможные типы трансляций в
pf:
binat
Двунаправленное отображение
между внешним и внутренним блоками сетевых IP-адресов.
nat
Активация
изменения IP-адресов пакетов при прохождении через данный
интерфейс. Этот метод обеспечивает пропуск сетевого трафика
через транслятор, имеющий один или несколько IP-адресов, для
передачи большому количеству узлов "внутри" сети.
Теоретически во внутренней сети можно использовать любые
IP-адреса, однако настоятельно рекомендуется использовать
один из диапазонов адресов, определенных в стандарте RFC
1918. Сетевые блоки:
-
10.0.0.0–10.255.255.255 (вся сеть 10, т.е.
10/8)
-
172.16.0.0–172.31.255.255 (т.е. 172.16/12)
-
192.168.0.0–192.168.255.255 (т.е. 192.168/16)
rdr
Пакет
пересылается на другой адрес назначения и, возможно, на
другой порт. В правилах rdr можно указать как отдельные
порты, так и диапазоны портов. Пример.
rdr ... port 2000:2999 -> ... port
4000
С помощью этой команды данные
с портов 2000–2999 (включительно) перенаправляются на порт
4000.
rdr ... port 2000:2999 -> ... port
4000:*
С помощью этой
команды данные с порта 2000 перенаправляются на порт 4000, с
порта 2001 – на порт 4001, ..., с порта 2999 – на порт 4999.
Помимо
изменения адреса, некоторыми правилами трансляции могут быть
изменены порты источника или назначения для соединений 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 и IPv6 в руководстве по библиотекам Neutrino
Library
Reference) и заголовков уровня 4 (см. разделы ICMP,
ICMP6, TCP и UDP). Кроме того, в целях управления пропускной
способностью пакеты могут присваиваться очередям.
Для каждого пакета,
обрабатываемого фильтром пакетов, правила фильтрации
применяются последовательно, от первого к последнему.
Выполняемое действие определяется последним правилом
сопоставления.
В фильтре можно определить
следующие действия:
block
Блокировать
пакет. Блокирование пакета правилом блокирования выполняется
несколькими способами. По умолчанию пакеты сбрасываются без
оповещения; это поведение можно изменить, т.е. активировать
оповещение о действии глобально (путем установки опции
политики блокирования) или для каждого правила посредством
одной из следующих опций:
-
drop – сброс пакета без
оповещения.
-
return-rst – только для
TCP-пакетов: выдача TCP RST, что приводит к закрытию
соединения.
-
return-icmp, return-icmp6 – возврат
ICMP-сообщений для пакетов, соответствующих правилам. По
умолчанию используется сообщение "ICMP UNREACHABLE";
можно задать другое сообщение, указав его код или номер.
-
return – возврат TCP RST для
TCP-пакетов и ICMP UNREACHABLE для UDP-пакетов и пакетов
остальных типов.
В настоящее
время опции возврата ICMP-пакетов не действуют, если pf функционирует на
подключении типа bridge, поскольку код реализации
этой функции еще не внедрен.
pass
Передача
пакета.
Если пакет не соответствует
ни одному правилу, то по умолчанию осуществляется передача
пакета. Для блокирования всех пакетов по умолчанию и
передачи только тех пакетов, которые соответствуют явно
заданным правилам, используется следующая команда:
block all
Она указывается
в качестве первого правила фильтрации.
См. раздел "Примеры
фильтрации" далее.
Параметры
Параметры правил
представляют собой описание пакетов, к которым применяется
правило. Любой пакет поступает или проходит через какой-либо
интерфейс. Большинство параметров являются необязательными.
Если задан какой-либо параметр, то данное правило
применяется только к пакетам с соответствующими атрибутами.
Некоторые параметры могут иметь форму списков, при этом все
требуемые комбинации правил генерируются посредством pfctl.
in или out
Правило
применяется к входящим или исходящим пакетам. Если параметр
in или out, не указан, то правилу
будут соответствовать пакеты, проходящие в обоих
направлениях.
log
В дополнение к
указанному действию в журнале создается сообщение. Сообщения
в журнале регистрируются для всех пакетов, проходящих по
данному соединению; однако если указана какая-либо из опций
keep state, modulate
state или synproxy state, то регистрируются только
те пакеты, которые устанавливают соответствующие состояние.
(См. описания опций keep state, modulate
state и synproxy state далее). Зарегистрированные
пакеты передаются на интерфейс pflog. Этот интерфейс
отслеживается сервисом регистрации pflogd который записывает дамп
регистрируемых пакетов в файл /var/log/pflog в бинарном формате pcap.
log-all
Используется
совместно с правилами keep state, modulate
state или synproxy state для принудительной
регистрации всех пакетов, проходящих через какое-либо
соединение. Как и в случае log, пакеты регистрируются в pflog.
quick
Если пакет
соответствует правилу, для которого установлена опция quick, это правило считается
последним правилом сопоставления и проверка на соответствие
остальным правилам не выполняется.
on интерфейс
Правило
применяется только к тем пакетам, которые поступают или
проходят через указанный интерфейс. Кроме того, можно
указать имя драйвера интерфейса, например ppp или fxp, тогда правилу будут
соответствовать пакеты, проходящие через данную группу
интерфейсов.
af
Правило применяется только к
тем пакетам, которые относятся к этому семейству адресов.
Допустимые значения – inet и inet6.
proto протокол
Правило
применяется только к пакетам указанного протокола. К
общепринятым протоколам относятся ICMP, ICMP6, TCP и UDP.
Список всех сопоставлений имен и номеров протоколов,
используемых в pfctl, содержится в файле /etc/protocols.
from источник port источник os источник to адресат port адресат
Правило
применяется только к пакетам с указанными адресами и портами
источника и назначения. Адреса можно указать в нотации CIDR
(соответствующие блоки сетевых адресов), в виде символьных
имен хостов или имен интерфейсов, либо использовать
следующие ключевые слова:
-
any – любой адрес.
-
route метка – любой адрес, с
которым связан маршрут, имеющий метку, указанную в
аргументе метка. См. описание протокола
ROUTE в руководстве по библиотекам Neutrino Library Reference и утилиты route.
-
no-route – любой
немаршрутизируемый адрес.
-
таблица – любой адрес,
соответствующий указанной таблице.
К именам интерфейсов можно
добавлять следующие модификаторы:
-
:network – преобразовать в сеть
(сети), связанную с интерфейсом.
-
:broadcast – преобразовать в
широковещательный адрес(-а) интерфейса.
-
:peer – преобразовать в
адрес(-а) партнера интерфейса "точка-точка".
-
:0 – не добавлять
псевдонимы интерфейсов.
Опция :0 также может быть добавлена
к именам хостов для ограничения разрешения имен только
первыми из найденных адресов v4 и v6.
Разрешение имени хоста и
трансляция "интерфейс-адрес" выполняются только после
загрузки набора правил. При изменении адреса интерфейса (или
имени хоста) (например, при использовании DHCP или PPP)
набор правил необходимо загрузить повторно для учета этих
изменений в io-pkt.
Если имя интерфейса (вместе
с модификаторами) заключено в круглые скобки, то правило
обновляется автоматически при любом изменении адреса
интерфейса. Повторная загрузка набора правил в этом случае
не требуется. Эту опцию удобно использовать для nat.
Порты можно указать в виде
номера или имени. Например, порт 80 можно указать как www. Список всех сопоставлений
имен и номеров портов, используемых в pfctl, содержится в файле /etc/services.
Для определения портов и
диапазонов портов можно использовать следующие операторы:
Оператор
|
Значение
|
=
|
Равно
|
!=
|
Не равно
|
<
|
Меньше
|
<=
|
Меньше или равно
|
>
|
Больше
|
>=
|
Больше или равно
|
:
|
Диапазон, включая
крайние значения
|
><
|
Диапазон, исключая
крайние значения
|
<>
|
Кроме диапазона
|
Операторы ><, <> and : являются бинарными и имеют по
два аргумента. Пример.
Запись
|
Значение
|
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
|
Если заданы
правила TCP с модификатором операционной системы, то можно
указать операционную систему хоста-отправителя. Для
получения дополнительной информации см. раздел "Получение
отпечатка операционной системы".
Определение хоста, порта и
операционной системы необязательно, см. следующие примеры:
pass in all
pass in from any to any
pass in proto tcp from any
port <= 1024 to any
pass in proto tcp from any to
any port 25
pass in proto tcp from
10.0.0.0/8 port > 1024 \
to ! 10.1.2.3 port != ssh
pass in proto tcp from any os
"OpenBSD" flags S/SA
pass in proto tcp from route
"DTAG"
all
Эквивалентно from any to any.
group группа
В данной версии NetBSD эта
функция не поддерживается.
user пользователь
Правило
применяется только к пакетам сокетов, владельцем которых
является указанный пользователь. Для исходящих соединений,
инициированных со стороны межсетевого экрана, это
пользователь, открывший соединение. Для входящих соединений
по отношению к сетевому экрану это пользователь,
прослушивающий порт назначения. Для перенаправленных
соединений, т.е. в том случае, если межсетевой экран не
является конечной точкой соединения, пользователь и группа
остаются неизвестными.
Все пакеты
одного соединения, как входящие, так и исходящие, связаны с
одним пользователем и одной группой. С пользователями могут
быть связаны только TCP- и UDP-пакеты; для остальных
протоколов эти параметры игнорируются.
Если сокет создается
процессом setuid или setgid, то пользователь и группа
соответствуют эффективным идентификаторам (в отличие от
реальных). Идентификаторы пользователя и группы сохраняются
при создании сокета; при создании прослушиваемого сокета
процессом с полномочиями root (например, путем привязки к
привилегированному порту) и последующей смене идентификатора
пользователя (для отмены привилегий) полномочия остаются
соответствующими пользователю root.
Идентификаторы пользователя
и группы можно указать в виде номеров или имен. Синтаксис их
записи соответствует синтаксису записи портов. Значение unknown соответствует пакетам в
перенаправляемых соединениях. Опцию unknown допускается указывать
только с операторами = и !=. Другие конструкции, такие
как user >= unknown, являются неверными.
Перенаправляемые пакеты с неизвестными идентификаторами
пользователя и группы соответствуют только правилам,
содержащим явное сравнение с unknown с операторами = или !=. Например, запись user >= 0 не соответствует
перенаправляемым пакетам. В нижеприведенном примере открытие
исходящих соединений разрешается только избранным
пользователям:
block out proto { tcp, udp }
all
pass out proto { tcp, udp }
all \
user { < 1000, dhartmei }
keep state
flags a/b | /b
Это правило
применяется только к TCP-пакетам с установленными флагами
для набора a в наборе b. Флаги,
отсутствующие в наборе b, игнорируются. Имеются
следующие флаги: (F)IN, (S)YN, (R)ST, (P)USH, (A)CK, (U)RG,
(E)CE, C(W)R. Пример.
Запись
|
Значение
|
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 должны быть отменены.
|
icmp-type тип code код
icmp6-type тип code код
Правило
применяется только к ICMP- или ICMPv6-пакетам, имеющим
указанный тип и код. Текстовые имена типов и кодов ICMP
приведены в описаниях ICMP и ICMP6 в руководстве по
библиотекам Neutrino Library Reference. Этот параметр действителен
только для правил, которые применяются к протоколам ICMP и
ICMP6. Протокол и индикатор типа ICMP (icmp-type или icmp6-type) должны совпадать.
tos строка | число
Правило
применяется к пакетам с указанным набором битов TOS (типа
обслуживания). Для типа обслуживания можно либо указать
значение lowdelay (малая задержка), throughput (пропускная способность), reliability (надежность), либо число в
шестнадцатеричном или десятичном формате. Например,
следующие правила являются одинаковыми:
-
pass all tos lowdelay
-
pass all tos 0x10
-
pass all tos 16
allow-opts
По умолчанию
пакеты, содержащие опции IP, блокируются. Если в правиле
передачи pass указана опция allow-opts, то пакеты, проходящие
через фильтр согласно этому (последнему, для которого
обнаружено соответствие) правилу, будут переданы даже при
наличии опций IP. Для пакетов с соответствующим состоянием
применяется правило, по которому было изначально создано это
состояние. Неявное правило передачи pass применяемое в том случае,
если пакет не соответствует ни одному правилу, не допускает
наличия опций IP.
label строка
Добавить к
правилу метку (имя), по которой можно идентифицировать
правило. Например, команда pfctl -s
labels позволяет просмотреть статистику по каждому из
правил, имеющих метки.
В метках можно использовать
следующие макросы:
Макрос
|
Значение
|
$if
|
Интерфейс
|
$srcaddr
|
IP-адрес источника
|
$dstaddr
|
IP-адрес
назначения
|
$srcport
|
Спецификация порта
источника
|
$dstport
|
Спецификация порта
назначения
|
$proto
|
Имя протокола
|
$nr
|
Номер правила
|
Пример.
ips = "{ 1.2.3.4, 1.2.3.5 }"
pass in proto tcp from any to
$ips \
port > 1023 label
"$dstaddr:$dstport"
после расширения принимает
следующий вид:
pass in inet proto tcp from
any to 1.2.3.4 \
port > 1023 label
"1.2.3.4:>1023"
pass in inet proto tcp from
any to 1.2.3.5 \
port > 1023 label
"1.2.3.5:>1023"
Расширение макроса для
директивы метки осуществляется только при синтаксическом
анализе конфигурационного файла (не при выполнении).
queue очередь | (очередь, очередь)
Пакеты,
соответствующие этому правилу, помещаются в указанную
очередь. Если указано две очереди, то в нее помещаются
пакеты с типом обслуживания lowdelay, а также флаги TCP ACK без
значащих данных. Для получения дополнительной информации см.
раздел "Формирование очередей" выше. Пример.
pass in proto tcp to port 25
queue mail
pass in proto tcp to port 22
queue(ssh_bulk, ssh_prio)
tag строка
Пакеты,
соответствующие этому правилу, отмечаются тегом с указанной
строкой. Тег представляет собой внутреннюю метку, которая
впоследствии используется для идентификации этих пакетов.
Присвоение тегов может применяться, например, для
установления доверенной связи между интерфейсами и для
проверки факта обработки пакетов по правилам трансляции.
Теги имеют атрибут "sticky";
это означает, что каждый пакет отмечается тегом даже в том
случае, если данное правило не является последним правилом
сопоставления. При применении остальных правил сопоставления
тег может быть заменен на другой, но не удаляется. Пакет
может иметь только один тег. Любое правило pass, в котором присутствует
ключевое слово tag, также должно содержать
опцию keep state, modulate
state или synproxy state.
Помимо правил фильтрации,
пакеты могут отмечаться тегами при применении правил nat, rdr и binat. Теги могут содержать
макросы, аналогичные макросам меток (см. выше).
tagged строка
Используется в
правилах фильтрации или трансляции; указывает, что этому
правилу могут соответствовать только пакеты, содержащие
данный тег. Можно выполнить обратное сопоставление, для чего
перед ключевым словом tagged следует указать оператор !.
probability число
В правило можно
добавить атрибут вероятности, который принимает значение от
0 до 1, исключая крайние значения. В этом случае правило
применяется только с учетом указанного значения вероятности.
Например, при сопоставлении со следующим правилом
сбрасывается 20% входящих ICMP- пакетов:
block in proto icmp
probability 20%
Маршрутизация
Если пакет соответствует
правилу, для которого установлена опция маршрутизации, то
этот пакет отправляется фильтром пакетов в соответствии с
типом опции маршрутизации. Если это правило создает
состояние, то опция маршрутизации применяется ко всем
пакетам, относящимся к данному соединению.
fastroute
Выполняется обычный поиск
маршрута с целью определения следующего транзитного узла для
данного пакета.
route-to
Пакет
отправляется на указанный интерфейс, при этом указывать
адрес следующего транзитного узла не обязательно. Если
правило route-to создает состояние, то по
данному маршруту отправляются только пакеты, передаваемые в
том же направлении, которое указано в правиле фильтрации.
Это не влияет на маршрутизацию пакетов, передаваемых в
обратном направлении (ответов).
reply-to
Аналогично route-to, но на указанный интерфейс
отправляются пакеты, передаваемые в обратном направлении
(ответы). Обратное направление задается только в контексте
записи состояния, т.е. reply-to используется только в
правилах, создающих состояние. Эту опцию можно указать для
систем, имеющих множество внешних соединений, для
маршрутизации всех исходящих пакетов по определенному
соединению через тот интерфейс, на который поступило
входящее соединение (принудительная симметричная
маршрутизация).
dup-to
Создается
дубликат пакета, маршрутизация которого выполняется
аналогично действию опции route-to. При этом маршрутизация
исходного пакета осуществляется в обычном порядке.
Опции
пула
В правилах nat и rdr (а также для опций route-to, reply-to и dup-to этих правил), для которых
имеется одиночный адрес перенаправления с маской подсети
меньше 32 для IPv4 или 128 для IPv6 (более одного
IP-адреса), присвоение этого адреса можно выполнить
несколькими способами:
bitmask
Применить сетевую часть адреса
перенаправления к изменяемому адресу (адрес источника –
правила nat, адрес назначения – правила rdr).
random
Выбирать адрес случайным
образом из указанного блока адресов.
source-hash
Использовать
хэш адреса источника для определения адреса перенаправления,
что обеспечит постоянство адреса перенаправления для
указанного источника. Кроме того, после этого ключевого
слова можно указать ключ в шестнадцатеричном формате или в
формате строки; по умолчанию pfctl генерирует опцию для source-hash случайным образом при
каждой повторной загрузке набора правил.
round-robin
Циклический перебор адресов
перенаправления.
Если задано несколько адресов
перенаправления, то тип round-robin является единственным
допустимым типом пула.
static-port
В случае
применения правил nat запретить изменение
псевдоустройством pf порта источника в TCP- и
UDP-пакетах.
Кроме того, можно указать
опцию sticky-address, что обеспечит
сопоставление нескольких соединений от одного источника с
одним и тем же адресом перенаправления. Эту опцию можно
использовать совместно с опциями пула random и round-robin. Обратите внимание, что по
умолчанию эти сопоставления удаляются, если ссылающиеся на
них состояния больше не существуют; для сохранения
сопоставлений на период, превышающий срок существования
состояний следует увеличить значение соответствующей
глобальной опции set timeout source-track. Другие способы
отслеживания источников описаны в разделе "Опции
отслеживания с запоминанием состояния".
Проверка
с запоминанием состояния
Фильтр пакетов pf обеспечивает фильтрацию с
запоминанием состояния (stateful); это означает, что в нем
реализована поддержка отслеживания состояния соединения.
Например, вместо перенаправления всего трафика на порт 25
может быть перенаправлен только первый пакет, после чего
начинается поддержка состояния. Движение последующего
трафика обусловлено тем, что фильтр отслеживает соединение.
Если какой-либо пакет
соответствует правилу состояния pass ... keep, то для данного соединения
фильтром создается новое состояние, и все последующие пакеты
этого соединения передаются автоматически.
Перед применением правил
фильтр проверяет пакет на соответствие какому-либо
состоянию. Если соответствие найдено, то пакет передается
без применения каких-либо правил.
Состояния удаляются после
закрытия соединения или завершения по таймауту.
За счет этого реализуются
следующие преимущества:
-
Сопоставление пакета с состоянием включает
в себя проверку его номеров последовательностей. Если
номера последовательностей выходят за узкие рамки
ожидаемых значений, пакет отбрасывается. За счет этого
обеспечивается защита от атак типа "спуфинг", например,
в том случае, если злоумышленник посылает пакеты с
подменного адреса/порта источника, но не имеет
информации о номерах последовательностей соединения.
-
Поиск по состояниям обычно занимает меньше
времени, чем реализация правил. При наличии 50 правил
последовательная реализация каждого из них выполняется
за O(n). С другой стороны, даже при наличии 50 000
состояний для сопоставления одного состояния необходимо
всего 16 операций, поскольку состояния хранятся в
бинарном дереве поиска, позволяющем выполнить поиск за
O(log2 n).
Пример.
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
При использовании модуляции
состояний следует учитывать ряд особенностей:
-
Правило modulate
state не следует применять к уже созданному, но
немодулированному соединению. Это может привести к
рассинхронизации четких последовательностей TCP между
двумя конечными точками. Вместо этого pf обрабатывает
модификатор modulate state как модификатор keep
state, и уже созданное соединение функционирует
без защиты, предоставляемой модуляцией.
-
Другая особенность связана с изменением
текущих модулированных состояний при потере таблицы
состояний (перезагрузка межсетевого экрана, удаление
данных из таблицы состояний и т.д.). Фильтр пакетов pf не может повторно
создать соединение после удаления модулятора соединения
вследствие очистки таблицы состояний. При потере
состояния соединение может остаться "повисшим" вплоть до
наступления таймаута соединения на соответствующих
конечных точках. Попытки повторной синхронизации
конечных точек в высокоскоростной локальной сети после
потери модулятора могут вызвать ACK-лавину. Для
предотвращения ACK-лавин в высокоскоростных сетях
рекомендуется в правиле modulate
state указать модификатор flags
S/SA.
Прокси SYN
По умолчанию 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 поддерживает следующие опции:
max число
Ограничить
количество параллельных состояний, создаваемых правилом. По
достижении этого предельного значения последующие пакеты,
соответствующие правилу, которое создает состояние,
отбрасываются до истечения лимита времени для существующих
состояний.
timeout секунды
Изменить
значения таймаута для состояний, созданных данным правилом.
Список всех допустимых имен таймаута приведен в разделе
"Опции" выше.
Можно указать несколько опций, разделенных запятой:
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-адреса источника:
source-track
rule
Максимальное
количество состояний, созданных данным правилом, ограничено
значениями опций max-src-nodes и max-src-state этого правила. Ограничения
для данного правила распространяются только на записи
состояния, созданные именно по этому правилу.
source-track
global
Ограничить
количество состояний, созданных всеми правилами, в которых
указана эта опция. Каждое правило может содержать различные
опции max-src-nodes и max-src-states, однако на записи
состояния, созданные любым из этих правил, распространяются
только ограничения, указанные в каждом отдельном правиле.
Можно установить следующие
ограничения:
max-src-nodes число
Ограничить максимальное
количество адресов источников, для которых в таблице состояния
могут одновременно существовать записи.
max-src-states число
Ограничить максимальное
количество записей состояния, которое может быть создано
данным правилом для одного адреса источника.
Для TCP-соединений с
запоминанием состояния ограничения для установленных
соединений (соединений, для которых выполнено 3-стороннее
подтверждение связи по TCP) также возможно определить по
каждому IP-адресу источника.
max-src-conn число
Ограничить
максимальное количество одновременных TCP-соединений с
3-сторонним подтверждением связи, которое допускается для
отдельного хоста.
max-src-conn-rate число / секунды
Ограничить
скорость новых подключений по истечении определенного
интервала времени. Скорость подключения вычисляется
приблизительно, как среднее скользящее значение.
Поскольку 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. По достижении заданного
предела фрагменты, подлежащие кэшированию, игнорируются до
наступления таймаута записи. Также можно изменить значение
таймаута.
В настоящее время
реализована поддержка только фрагментов IPv4. Фрагменты IPv6
безусловно блокируются.
Закладки
Помимо основного набора
правил, pfctl позволяет загружать наборы
правил в точки присоединения закладок. Закладка – это
контейнер, который может содержать правила, таблицы адресов
и другие закладки.
Закладка имеет имя,
определяющее путь, используемый pfctl для доступа к закладке в
целях выполнения, например, таких операций, как
присоединение дочерних закладок или загрузка правил.
Закладки могут быть вложенными, при этом компоненты
разделяются символами косой чертой (/), аналогично иерархиям
файловых систем. Основной набор правил по сути является
закладкой по умолчанию; таким образом, в любой закладке
могут содержаться, например, правила фильтрации и
трансляции.
Закладка может ссылаться на
другую точку присоединения закладок посредством правил
следующих видов:
nat-anchor имя
Реализовать правила nat в указанной закладке.
rdr-anchor имя
Реализовать правила rdr в указанной закладке.
binat-anchor имя
Реализовать правила binat в указанной закладке.
anchor имя
Реализовать правила фильтрации
в указанной закладке.
load anchor имя from файл
Загрузить правила из
указанного файла в имя закладки.
Когда при
реализации основного набора правил достигается правило
закладки, 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"
При загрузке 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
Грамматика
Для 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 ")" )
Связанные
файлы
/etc/hosts
База данных имен хостов.
/etc/pf.os
Местоположение отпечатков
операционных систем по умолчанию.
/etc/protocols
База данных имен протоколов.
/etc/services
База данных имен служб.
/usr/share/examples/pf
Примеры наборов правил.