Списки
Списки позволяют определять множества, имеющие общие признаки в пределах правила – такие как IP адреса, номера портов и т.д. Таким образом, вместо прописывания нескольких правил фильтрации для каждого IP адреса, который должен быть заблокирован, мы можем определить список IP адресов в пределах одного правила. Списки должны находиться внутри скобок {}.
Когда pfctl(8) доходит до списка при загрузке наборов правил, он раскладывает их на отдельные правила, для каждого элемента списка. Для примера:
будет преобразован в:
При этом в правилах PF можно использовать как числовые обозначения номеров портов, так и их эквиваленты, взятые с файла /etc/services. Множественные списки могут применяться не только для блокировки:
Запятая в списке является необязательной.
Макросы
Макросы – определяемые пользователем переменные, которые могут держать IP адреса, номера портов, имена интерфейсов, и т.д. Макросы позволят облегчить написание и чтение наборов правил и сделать поддержание набора правил гораздо легче, потому нет причин ими пренебрегать.
Имена макросов должны начаться с символа и могут содержать символы, цифры, и символы подчеркивания. Названия макросов не могут носить имена зарезервированных слов, типа pass, out, или queue.
Строка
создаст макрос с именем ext_if. При использовании макроса его имени должен предшествовать знак $:
Макросы также могут быть расширены до списков.
Макросы могут определяться рекурсивно. В этом случае должен использоваться следующий синтаксис:
Теперь макрос $all_hosts расширен до значений 192.168.1.1, 192.168.1.2.
Фактически макросы и списки могут определяться в любой точке файла, с единственным условием, чтобы они были определены до первого использования. Впрочем не стоит пренебрегать элементарными соображениями удобства и придерживаться практики помещения всех константных данных в начало файла.
Таблицы
Использование таблиц
Таблицы используются для хранения группы адресов IPv6 и/или IPv4. Поиски в таблице занимают гораздо меньше времени и потребляют меньше ресурсов, чем в случае со списками. По этой причине таблица идеальна идеальна для хранения больших групп адресов, поскольку время поиска в таблице, содержащей 50 000 адресов, не намного больше чем в таблице с 50 адресами. Таблицы могут использоваться следующими способами:
- источник и/или адрес назначения для filter, scrub, NAT, и redirection rules
- адрес трансляции для правил NAT
- адрес переназначения для правил редиректа
- адрес назначения для правил фильтрации route-to, reply-to, и dup-to
Таблицы определяются в pf.conf или с помощью pfctl(8).
В pf.conf таблицы создаются, используя директиву table. При этом для каждой таблицы могут быть заданы такие опциональные аттрибуты:
- const – содержание таблицы не может быть изменено после ее создания. Когда этот атрибут не определен, с помощью pfctl(8) можно добавлять или удалять адреса из таблицы в любое время при работе с уровнем безопасности (securelevel(7)), равным двум и выше.
- persist – заставляет ядро постоянно хранить таблицу в памяти, даже когда никакие правила к ней не обращаются. Без этого атрибута, ядро автоматически удалит таблицу, когда последнее правило, ссылающееся на нее, будет отработано.
Пример:
Адреса могут также быть определены, используя модификатор типа отрицание (или "не"):
Таблица goodguys теперь содержит адреса сети 192.0.2.0/24, за исключением адреса 192.0.2.5.
Обратите внимание, что имена таблицы всегда включаются в <>.
Таблицы могут также могут заполняться из файлов. Вы можете отдельно поддерживать файл с регулярно обновляемыми списками IP-адресов хостов и сетей, в таком случае после изменения такого файла достаточно лишь перезагрузить таблицы и не модифицировать /etc/pf.conf:
Файл /etc/spammers содержал бы список IP адресов, а /etc/rfc1918list адреса сетей с приватным диапазоном IP-адресов. Любая строка начинающаяся о знака #, будет обработана как комментарий и проигнорирована.
Управление таблицами с помощью pfctl
Таблицами можно управлять на лету, используя pfctl(8). Например, для добавления в таблицу <spammers> :
Это также создаст таблицу, если она ранее не существовала. Посмотреть значения в таблице можно такой командой:
Аргумент -v может использоваться совместно с -Tshow для отображнения каждой записи в таблице. Для удаления адреса из таблицы выполяют следующее:
Определение адресов
В дополнение к IP адресу, хосты в таблице могут указываться по имени. Когда имя хоста разрешается в IP адрес, то оно может быть помещено в таблицу. При этом если имени хоста назначено несколько IP-адресов, после DNS-запроса все они будут помещены в таблицу. Также IP адреса могут быть введены в таблицу с использованием имени интерфейса или ключевого слова "self", при этом в таблицу будут добавляться все IP адреса, назначенные интерфейсу.
Соответствие адресов
Поиск адреса в таблице возвратит наиболее соответствующее значение. Это учитывается при создании следующим образом:
В приведенном примере к любому пакету, входящему через dc0 будут применяться следующие правила:
- 172.16.50.5 – самое точное соответствие – 172.16.0.0/16; пакет соответствует таблице и будет передаваться
- 172.16.1.25 – самое точное соответствие – !172.16.1.0/24; пакет соответствует входу в таблицу, но тот вход инвертирован (использует "!" модификатор); пакет не соответствует таблице и будет блокирован
- 172.16.1.100 – точное значение 172.16.1.100; пакет соответствует таблице и будет передан
- 10.1.4.55 – не соответствует таблице и будет блокирован
Нормализация пакетов
"Scrubbing" – это нормализация пакета, производимая таким образом, чтобы не осталось двусмысленности в определении адресата пакета. Директива scrub также пересобирает фрагментированные пакеты, осуществляя защиту некоторых операционных систем от определенного видов атак и отбрасывает пакеты с недопустимыми флагами. Самая простая форма:
Это позволит прооизводить нормализацию всех пакетов на всех интерфейсах. Одной из причин отказаться scrub – если вы прокидываете NFS через PF. Некоторые не-OpenBSD операционные системы посылают(и ожидают) странные пакеты – фрагментированные, но с установленным битом "do not fragment", которые будут отклоняться при использовании scrub. Это может быть решено с помощью опции no-df. Еще одной причиной могут быть игры, у которых могут возникнуть проблемы при работе через scrub.
Синтаксис этой директивы очень похож на синтаксис правила фильтрации, что позволяет проводить выборочный scrub.
Дополнительная информация на эту тему может быть найдена в Network Intrusion Detection: Evasion, Traffic Normalization, and End-to-End Protocol Semantics"
Опции
Scrub может принимать следующие опции:
- no-df
- random-id
- min-ttl num
- max-mss num
- fragment reassemble
- fragment crop
- fragment drop-ovl
- reassemble tcp
- Ни одной из сторон не позволяется уменьшить IP TTL. Это сделано для того, чтобы защититься от посылок пакетов, запрашивающих соединение, но исчезающих до достижения адресата. TTL всех пакетов устанавливается в максимум.
- Модуляция временных меток RFC1323 в заголовке TCP пакета со случайным числом. Это препятствует определению uptime хоста или определению количества хостов за маршрутизатором, использующем NAT.
Пример:
Опции PF
Опции контролируют работу PF. Они могут быть указаны в pf.conf, используя следующие директивы:
- set block-policy
- drop – пакеты удаляются без уведомления
- return – возвращается пакет TCP RST для блокированного TCP пакета и ICMP Unreachable для всего остального
- set debug
- none – отладочные сообщения не отображаются
- urgent – отображаются серьезные ошибки. Является значением по умолчанию
- misc – ошибки различной тяжести (например отображение статуса normalizer/scrubber пакета и ошибки создания правила state)
- loud – отображать события, возникающие в ходе обычной работы (например результат работы пассивной системы определения удаленной ОС)
- set fingerprints file
- set limit
- frags – максимальный размер памяти в строках, используемой для пересборки пакета (scrub правило). По умолчанию 5000.
- src-nodes – максимальный размер памяти в строках, используемой для хранения IP адресов источника. По умолчанию 10000.
- states – максимальный размер памяти в строках, используемой для хранения таблицы состояний (правила фильтрации keep state). По умолчанию 10000.
- set loginterface int
- set optimization
- normal – подходит почти для всех сетей. Является значением по умолчанию.
- high-latency – сети с высоким временем задержки, например, спутниковые.
- aggressive – позволяет уменьшить объемы занимаемой памяти за счет малого таймаута соединений в таблице состояний.
- conservative – максимально консервативные настройки. Позволяет продлить время жизни записи в таблице состояний за счет некоторого увеличения объема занимаемой памяти и процессорного времени.
- set state-policy
- if-bound – правила привязаны к интерфейсу, на котором открыты. Если трафик соответствует записи, но пересекает интерфейс указанный в ней, то соответствия не произойдет. Тогда пакет должен соответствовать фильтрующему правилу или быть отброшен.
- group-bound – также, как и с параметром if-bound, за исключением того, что принимаются интерфейсы одной грунны – например все ppp интерфейсы.
- floating – записи могут соответствовать пакету на любом интерфейсе. Пока пакет соответствует записи, не имеет значения, через какой интерфейс он проходит и он будет пропущен. Является значением по умолчанию.
- set timeout
- interval – число секунд между пакетами, соответствующих правилу. Превышение приводит к удалению правила.
- frag – время устаревания несобранного фрагмента пакета.
Пример: