[OpenBSD]

[Предыдущая: Таблицы] [Содержание] [Следующая: Преобразование Сетевых Адресов]

PF: Фильтрация пакетов


Содержание


Вступление

Фильтрация пакетов это выборочное пропускание или блокирование пакетов с данными, во время их прохождения через сетевой интерфейс. Критерии, которыми пользуется pf(4), когда проверяет пакеты, основываются на 3-м Уровне (IPv4 и IPv6) и 4-м Уровне (TCP, UDP, ICMP, и ICMPv6) заголовков. Наиболее часто используемые критерии, это используемый протокол, исходный адрес и адрес назначения, исходный порт и порт назначения.

Правила фильтрации определяют критерии, которым пакет должен соответствовать и действия, блокировать или пропускать, которые выполняются, когда найдено соответствие. Правила фильтрации оцениваются в последовательном порядке, от первого к последнему. Если пакет соответствует правилу содержащему ключевое слово quick, действия над пакетом будут выполнены сразу не дожидаясь проверки всех фильтрующих правил. Последнее правило будет объявлено "победителем" и будет диктовать какие выполнять действия над пакетом. Если выставить pass all в начале фильтрующих правил, это будет означать, что если пакет не соответствует каким либо другим фильтрующим правилам, то он будет пропущен и выполнимое над ним действие будет pass.

Синтаксис Правил

Общий, сильно упрощённый синтаксис для фильтрующих правил:
action [direction] [log] [quick] [on interface] [af] [proto protocol] \
   [from src_addr [port src_port]] [to dst_addr [port dst_port]] \
   [flags tcp_flags] [state]
action
Действие, которое будет выполнено над пакетом, либо pass, либо block. Действие pass пропустит пакет обратно к ядру для дальнейшей обработки, в то время как действие block будет основываться на установках опции block-policy. Реакция по умолчанию может быть перекрыта опцией block drop или block return.
direction
Направление пакета относительно интерфейса, in или out.
log
Указывает, что пакет должен логироваться через pflogd(8). Если правило создаёт соединение(state), то логироваться будут только те пакеты, у которых соединение установлено(establishes) Для логирования всех пакетов, независимо от состояния соединения, используйте log (all).
quick
Если пакет соответствует правилу содержащему ключевое слово quick, то это правило считается последним подходящим правилом и указанное действие action будет выполнено.
interface
Название сетевого интерфейса или название группы интерфейсов, через что будет проходить пакет. Интерфейсы могут быть добавлены в группы с помощью программы ifconfig(8). Несколько групп уже созданы ядром: Это приведёт к тому, что правилу будет соответствовать любой пакет пересекающий ppp или carp интерфейс.
af
Семейство адресов пакета, inet для IPv4 или inet6 для IPv6. PF обычно в состоянии самостоятельно определить этот параметр, основываясь на исходном адресе и/или адресе назначения.
protocol
4 Уровень, протоколов пакета:
src_addr, dst_addr
Адрес источника/назначения в IP заголовке. Адреса могут быть указаны, как:
src_port, dst_port
Порт источника/назначения в заголовке пакета. Порты могут быть указаны, как:
tcp_flags
Определяет флаги, которые должны быть установлены в TCP заголовке. Флаги определяются, как flags check/mask. Например: flags S/SA - указывает PF смотреть только флаги S и A (SYN и ACK) и реагировать если включен только SYN флаг. Начиная с OpenBSD 4.1, флаг S/SA применяется для всех правил фильтрации TCP.
state
Сохранять информацию о состояние соединения.

Запрет по умолчанию

Рекомендуемая практика по настройке брандмауэра, использовать подход "запрет по умолчанию". То есть запретить всё и затем выборочно разрешить проходить определённому трафику через брандмауэр. Это рекомендуемый подход, потому что ошибки оказываются на закрытой стороне а также становится проще написание правил.

Чтобы создать политику запрета по умолчанию, первые два правила фильтрации должны быть:

block in  all
block out all

Будет блокирован весь трафик на всех интерфейсах, во все стороны, куда либо и откуда либо.

Пропуск трафика

Трафик теперь нужно явно пропустить через брандмауэр или он будет отброшен политикой по умолчанию. Вот где такие критерии, как источник/назначение портов, адресов и тип протокола вступают в игру. Всякий раз, когда трафику разрешается пройти через брандмауэр, правило(а) должно быть написано на столько узко, на сколько это возможно. Правило должно пропускать только необходимый трафик.

Некоторые примеры:

# Пропустить входящий трафик на интерфейс dc0 из локальной сети 192.168.0.0/24,
# на OpenBSD машину с IP адресом 192.168.0.1. Также пропустить возвращающийся
# трафик, выходящий на интерфейс dc0
pass in  on dc0 from 192.168.0.0/24 to 192.168.0.1
pass out on dc0 from 192.168.0.1 to 192.168.0.0/24


# Пропустить входящий TCP трафик на интерфейс fxp0 на веб сервер, запущенный
# на OpenBSD машине. Интерфейс под названием fxp0 используется, как адрес
# назначения, поэтому пакеты будут соответствовать только этому правилу если
# они предназначены OpenBSD машине
pass in on fxp0 proto tcp from any to fxp0 port www

Ключевое слово quick

Как отмечалось ранее, каждый пакет оценивается правилами идущими сверху вниз. По умолчанию, пакет отмеченный на пропуск может быть изменён туда и обратно несколько раз правилами расположенными ниже. Последнее подходящее правило "выигрывает". Существует исключение из этого: quick опция в правилах фильтрации влечёт за собой аннулирование каких-либо дальнейших правил обработки и заставляет указанное действие быть выполненным. Давайте рассмотрим пару примеров:

Ошибочно:

block in on fxp0 proto tcp from any to any port ssh
pass  in all

В этом случае, строка блокировки block будет рассмотрена, но никогда не выполнится, поскольку затем следует правило, которое пропускает всё.

Правильно:

block in quick on fxp0 proto tcp from any to any port ssh
pass  in all

Это правило рассматривается не много по другому. Если строка блокировки block подходит, пакет будет блокирован и остальная часть правил будет проигнорирована.

Хранение состояния соединений

Одной из важных возможностей пакетного фильтра является "хранение состояния соединений" или "проверка состояния соединений". Проверка состояния соединений возможна благодаря способности PF следить за состоянием или развитием сетевого соединения. Храня информацию о каждом соединении в таблице состояний, PF способен быстро определить принадлежит ли проходящий через брандмауэр пакет уже установившемуся соединению. Если это так, то он пройдёт через брандмауэр без проверки правилами.

Хранение состояний имеет много преимуществ, включая упрощение правил и улучшенное выполнение фильтрации пакетов. PF способен распознавать пакеты движущиеся в обоих направлениях, это означает, что писать правило пропускающее обратный трафик нет необходимости. И поскольку пакеты соответствующие установленному соединению не проходят проверку правилами, время которое PF тратит на обработку этих пакетов существенно меньше.

Когда правило создаёт стейт(запись в таблице состояний), первый пакет соответствующий правилу создаёт "state" между отправителем и принимающим. Теперь, не только пакеты идущие от отправителя к получателю соответствуют состоянию(state) и не проверяются правилами, но тоже самое происходит и с пакетами идущими от получателя к отправителю.

Начиная с OpenBSD 4.1 все фильтрующие правила автоматически создают state запись когда пакет соответствует правилу. В более ранних версиях OpenBSD в правиле фильтрации должна была быть явно указана опция keep state.

Пример использования OpenBSD 4.1 и более поздних версий:

pass out on fxp0 proto tcp from any to any

Пример использования OpenBSD 4.0 и более ранних версий:

pass out on fxp0 proto tcp from any to any keep state

Эти правила разрешают любой исходящий трафик на интерфейсе fxp0 и также позволяют пройти через брандмауэр ответному трафику. Хранение состояний является полезным свойством и его использование значительно улучшает производительность брандмауэра, поскольку поиск установившихся соединений значительно быстрее чем пропуск пакета через правила фильтрации.

Опция modulate state работает так же, как keep state, за исключением того, что опция применяется только к TCP пакетам. С modulate state начальная последовательность номеров (ISN) исходящих соединений рандомизирована. Это применяется для защиты соединений инициированных некоторыми операционными системами, которые проделывают плохую работу по выбору ISNs. Начиная с OpenBSD 3.5, опция modulate state может быть использована в правилах, которые указывают протоколы отличные от TCP.

Хранение состояния на исходящие TCP, UDP, и ICMP пакеты и включить TCP ISNs:

pass out on fxp0 proto { tcp, udp, icmp } from any \
    to any modulate state

Ещё одно преимущество хранения состояний заключается в том, что соответствующий ICMP трафик будет пропущен через брандмауэр. Например, если TCP соединение проходящее через брандмауэр будет отслеживать по состоянию, то ICMP сообщения относящиеся к этому соединению будут пропущены.

Область действия записи в таблице состояний контролируется глобальной опцией state-policy и через опции if-bound, group-bound, floating. Эти опции имеют тоже значение, как и при использовании опции state-policy. Например:

pass out on fxp0 proto { tcp, udp, icmp } from any \
    to any modulate state (if-bound)

Правило предписывает, что пакеты будут соответствовать записи в таблице состояний только в том случае, если они проходят через интерфейс fxp0.

Заметьте, что nat, binat, и rdr правила тоже создают записи в таблице состояний, эти записи хранятся до тех пор, пока фильтрующие правила их пропускают.

Хранение состояний для UDP

Каждый будет слышать высказывание, что "Никто не может установить соединение через UDP, поскольку UDP это протокол без логического соединения!" до тех пор пока это правда, что UDP сессия не имеет каких либо понятий установления соединения(за исключением начала и конца передачи), но всё это не имеет никакого отношения к способности PF создавать соединение для UDP сессии. В случае протоколов без "начальных" и "конечных" пакетов, PF просто следит сколько прошло времени с тех пор, как соответствующий пакет прошёл. Если тайм аут истёк, запись о соединение очищается. Значение тайм аута может быть задано в секции опции, конфигурационного файла pf.conf

Опции таблицы состояний

Правила фильтрации, которые создают записи о состоянии соединений могут задавать различные опции для управления поведением записей в таблице состояний. Доступны следующие опции:
max number
Ограничивает максимальное количество записей в таблице состояний, которое может создать правило. Если достигнут максимум, пакеты которые обычно создают соединение не будут соответствовать этому правилу до тех пор пока количество существующих соединений не уменьшится до лимита.
no state
Предотвращает правило от автоматического создания записи в таблице состояний.
source-track
Эта опция даёт возможность отслеживать количество соединений созданных исходным IP адресом. Опция имеет два формата: Итоговое количество исходных IP адресов отслеживающихся глобально контролируется src-nodes параметрами работы фильтра.
max-src-nodes number
Когда используется опция source-track, опция max-src-nodes ограничивает количество исходных IP адресов, которые могут одновременно создать соединение. Эта опция может быть использована только с опцией source-track rule.
max-src-states number
Когда используется опция source-track, опция max-src-states ограничивает количество одновременно созданных соединений с одного исходного IP адреса. Пределы этого лимита(например, записи в таблицу состояний созданы только этим правилом или записи созданы всеми правилами, что используют source-track) зависят от указаний опции source-track.

Опции указываются внутри круглых скобок и непосредственно после одного или ключевых слов (keep state, modulate state, or synproxy state). Несколько опций указываются через запятые. Начиная с OpenBSD 4.1, опция keep state подразумевается по умолчанию для любых фильтрующих правил. Вопреки этому, когда указывается опция таблиц состояния, одно из ключевых слов должно быть использовано перед опциями.

Пример:

pass in on $ext_if proto tcp to $web_server \
    port www keep state \
    (max 200, source-track rule, max-src-nodes 100, max-src-states 3)

Это правило задаёт следующее поведение:

Отдельные ограничения могут быть размещены на TCP соединения, которые закончили 3-х этапное рукопожатие.

max-src-conn number
Ограничить максимальное количество одновременных TCP соединений, закончивших 3х этапное рукопожатие, которых может сделать единственный хост.
max-src-conn-rate number / interval
Ограничить количество новых соединений, которые можно открывать за указанный интервал времени.

Обе этих опции автоматически вызывают опцию source-track rule и не совместимы с опцией source-track global.

Поскольку эти опции ограничивают только прошедшие 3х этапное рукопожатие TCP соединения, можно использовать более агрессивные дейcтвия, которые могут быть применены на неприятные вам IP адреса.

overload <table>
Поместить не приятные IP адреса в указанную таблицу.
flush [global]
Убить все остальные соединения, которые соответствуют этому правилу и которые были созданы этим исходным IP адресом. Когда указана global, убиваются все соединения созданные этим исходным адресом, независимо от того, какое правило его создало.

Пример:

table <abusive_hosts> persist
block in quick from <abusive_hosts>

pass in on $ext_if proto tcp to $web_server \
    port www flags S/SA keep state \
    (max-src-conn 100, max-src-conn-rate 15/5, overload <abusive_hosts> flush)

Читается как:

TCP Флаги

Проверка TCP пакетов основанная на флагах является наиболее часто используемой для фильтрации TCP пакетов, которые совершают попытку установить новое соединение. TCP флаги и их значения перечислены здесь:

Для проверки TCP флагов в процессе оценки правилами, используется ключевое слово flags:

flags check/mask
flags any

mask сообщает PF просматривать только указанные флаги а check указывает какие флаги должны быть включены в заголовок. Для разрешения любых комбинаций флагов, используйте ключевое слово any.

pass in on fxp0 proto tcp from any to any port ssh flags S/SA

Вышеуказанное правило пропускает TCP трафик с установленным SYN флагом, хотя просматривает только SYN и ACK флаги. Пакет с флагами SYN и ECE будет соответствовать указанному правилу, а пакет с флагами SYN и ACK или просто ACK не будет.

Начиная с OpenBSD 4.1, применяемые флаги для TCP правил это flags S/SA. Скомбинированные с выставляемым по умолчанию keep state правилом фильтрации, эти два правила будут эквивалентны:

pass out on fxp0 proto tcp all flags S/SA keep state
pass out on fxp0 proto tcp all

TCP пакеты с SYN флагом буду подходить под каждое правило. Под каждое правило будут подходить TCP пакеты с установленным флагом SYN, и не подходить с флагом ACK, и каждое правило создаст запись в таблице состояний для подходящего пакета. Используемые по умолчанию флаги могут быть переписаны с помощью опции flags.

В OpenBSD 4.0 и более ранних версиях, применяемых флагов по умолчанию не было. В каждом правиле должны были задаваться флаги и указываться опция keep state.

pass out on fxp0 proto tcp all flags S/SA keep state

Необходимо быть осторожным при использовании флагов - понимайте что вы делаете и зачем и будьте осторожны с советами людей. Некоторые люди советуют создавать записи в таблице "только если установлен SYN флаг и никаких других". Подобное правило может оканчиваться так:

     . . . flags S/FSRPAUEW  плохая идея!!

Теоретически, стейт создаётся только на начало TCP сессии и сессия должна начинаться с SYN флага, и никакого другого. Проблема в том, что некоторые системы используют ECN флаг в начале сессии и любые системы делающие это будут отвергнуты подобным правилом. Гораздо лучшая политика вообще не указывать никакие флаги и позволить PF установить флаги используемые по умолчанию. Если вам необходимо самому указывать флаги, тогда следующая комбинация должна быть безопасна:

. . . flags S/SAFR

Это практично и безопасно, но нет необходимости проверять флаги FIN и RST, если трафик нормализован с помощью скраба. Процесс нормализации предписывает PF отбрасывать любые входящие пакеты с запрещёнными комбинациями TCP флагов (таких, как SYN и RST) и нормализовать потенциальные двусмысленные комбинации(такие, как SYN и FIN).

Проксирование TCP SYN

Обычно, когда клиент инициирует TCP соединение на сервер, PF пропускает пакеты рукопожатия между двумя конечными точками. PF имеет возможность, проксировать рукопожатие. При проксировании рукопожатия PF самостоятельно завершает рукопожатие с клиентом, инициирует рукопожатие с сервером и затем пропускает пакеты между ними. Преимущество этого процесса состоит в том, что никаких пакетов не посылается серверу, перед тем, как клиент завершит рукопожатие. Это исключает угрозу TCP SYN спуфинг флуда имеющего эффект на сервер, потому что рукопожатие не будет завершено.

Проксирование TCP SYN запускается ключевым слово synproxy state в правилах фильтрации. Пример:

pass in on $ext_if proto tcp from any to $web_server port www \
   flags S/SA synproxy state

Здесь соединения на веб сервер будут проксироваться фильтром.

synproxy state также включает функциональность keep state и modulate state.

Проксирование SYN не будет работать если PF запущен на мосту(4).

Блокирование спуфинга (spoofed packets)

Адреса спуфятся(spoofing), когда злоумышленник подделывает исходные IP адреса в пакетах, они передаются либо в оболочку своих реальных адресов либо изображают другой узел в сети. После того, как пользователь был заспуфен они смогут запустить сетевую атаку без разоблачения реального источника атаки или попытаться получить доступ к сетевым сервисам, которые ограничены на определённые IP адреса.

PF предлагает защиту против спуфинга через ключевое слово antispoof:

antispoof [log] [quick] for interface [af]
log
Указывает, что пакеты должны логироваться через pflogd(8).
quick
Если пакет соответствует этому правилу, тогда правило будет объявлено "победителем" и дальнейшая оценка правилами будет остановлена.
interface
Сетевой интерфейс на котором необходимо активировать защиту от спуфинга. Это также может быть список интерфейсов.
af
Семейство адресов для которых должна быть активирована защита от спуфинга, inet для IPv4 или inet6 для IPv6.

Пример:

antispoof for fxp0 inet

Когда правило будет загружено, оно разложится в два правила. Предположим, что интерфейс fxp0 имеет IP адрес 10.0.0.1 и маску подсети 255.255.255.0 ( /24), antispoof правило будет выглядеть так:

block in on ! fxp0 inet from 10.0.0.0/24 to any
block in inet from 10.0.0.1 to any

Эти правила выполняют две вещи:

Обратите внимание: Правила фильтрации, которые используют antispoof правило могут также заблокировать пакеты посылаемые через кольцевой интерфейс локальным адресам. Хорошая практика не фильтровать трафик на кольцевом интерфейсе, но это просто необходимо при использовании антиспуф правил:

set skip on lo0

antispoof for fxp0 inet

Использование antispoof должно быть ограничено на интерфейсы, которым присвоены IP адреса. Использование antispoof на интерфейсе без IP адресов будет выглядеть как:

block drop in on ! fxp0 inet all
block drop in inet all

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

Unicast Reverse Path Forwarding

Начиная с OpenBSD 4.0 PF предлагает возможности Unicast Reverse Path Forwarding (uRPF). Когда пакет проходит через uRPF проверку, исходные IP адреса пакета проверяются в таблице маршрутизации. Если исходящий интерфейс в таблице маршрутизации такой же, как и интерфейс с которого только что пришёл пакет, тогда uRPF проверка пройдена. Если интерфейсы не совпадают, тогда возможно исходный адрес пакета был подделан(спуфинг атака).

uRPF проверка может выполняться на пакеты, через использование ключевого слова urpf-failed:

block in quick from urpf-failed label uRPF

Обратите внимание,что uRPF проверка имеет смысл только в условиях, когда маршрутизация симметрична.

uRPF предоставляет те же функции, как и при антиспуф правилах.

Пассивное Определение Операционной Системы

Пассивное определение Операционной Системы (OSFP) это метод пассивного определения типа операционной системы на удалённом хосте, основанный на некоторых TCP SYN характеристиках этого хоста. Эта информация может быть использована в качестве критерия в фильтрующих правилах.

PF определяет удалённую операционную систему сравнением TCP SYN характеристик с fingerprint файлом, который по умолчанию находится в /etc/pf.os. После включения PF текущий fingerprint список можно просмотреть с помощью команды:

# pfctl -s osfp

В фильтрующих правилах операционные системы могут быть указаны классами, версиями, или подтипами/уровнями. Каждый из этих параметров выводится на вывод через pfctl команду показанную выше. Для указания операционной системы в правиле, используется ключевое слово os:

pass  in on $ext_if from any os OpenBSD keep state
block in on $ext_if from any os "Windows 2000"
block in on $ext_if from any os "Linux 2.4 ts"
block in on $ext_if from any os unknown

Указание класса операционной системы, как unknown, позволяет соответствовать пакетам, когда операционная система не известна.

Возьмите на заметку следующее:

IP Опции

По умолчанию, PF блокирует пакеты с выставленными IP опциями. Это может сделать работу более затруднительной для "OS fingerprinting" утилит, таких, как nmap. Если вам требуется пропускать такие пакеты, как multicast или IGMP, вы можете использовать директиву allow-opts:
pass in quick on fxp0 all allow-opts

Пример правил фильтрации

Ниже приведён пример правил фильтрации. Mашина на которой запущен PF, выполняет роль брандмауэра между малой, внутренней сетью и Интернетом. Показаны только фильтрующие правила; queueing, nat, rdr, и тд., не показаны в этом примере.

ext_if  = "fxp0"
int_if  = "dc0"
lan_net = "192.168.0.0/24"

# таблица содержащая все IP адреса принадлежащие брандмауэру
table <firewall> const { self }

# не фильтровать трафик на кольцевом интерфейсе
set skip on lo0

# нормализовать входящие пакеты
scrub in all

# установка политики по умолчанию
block all

# активировать спуфинг защиту для всех интерфейсов
block in quick from urpf-failed

# разрешить ssh соединения только из локальной сети доверенного
# компьютера, 192.168.0.15. Используйте "block return" для того чтобы 
# высылался TCP флаг RST для закрытия блокированных соединений немедленно.
# используйте "quick" чтобы правило не было перекрыто правилами расположенными
# ниже.
block return in quick on $int_if proto tcp from ! 192.168.0.15 \
   to $int_if port ssh

# пропускать весь трафик в и из локальной сети.
# эти правила будут создавать записи в таблице состояний,
# благодаря дефолтной опции "keep state", которая будет применена
# автоматически
pass in  on $int_if from $lan_net to any
pass out on $int_if from any to $lan_net

# выпустить tcp, udp и icmp трафик на внешнем (Интернет) интерфейсе. 
# tcp соединения буду модулироваться, udp/icmp будут отслеживаться
# по таблице состояний.
pass out on $ext_if proto { tcp udp icmp } all modulate state

# разрешить входящие ssh соединения на внешнем интерфейсе, если они НЕ
# предназначены брандмауэру (например, они предназначены машине внутри
# сети). Логировать инициализационный пакет, так чтобы позже мы смогли
# сказать кто пытался соединиться. используйте tcp syn proxy для проксирования
# соединения. Флаг "S/SA" используемый по умолчанию будет автоматически
# применён к правилу.
pass in log on $ext_if proto tcp from any to ! <firewall> \
   port ssh synproxy state

[Предыдущая: Таблицы] [Содержание] [Следующая: Преобразование Сетевых Адресов]


[back] www@openbsd.org
$OpenBSD: filter.html,v 1.4 2009/08/01 21:41:39 tobias Exp $