Ключевые слова:pf, firewall, openbsd, bsd, filter, packet, (найти похожие документы)
From: Сгибнев Михаил <http://www.dreamcatcher.ru>
Newsgroups: http://www.dreamcatcher.ru
Date: Mon, 17 May 2004 18:21:07 +0000 (UTC)
Subject: PF: Пакетный фильтр OpenBSD
Оригинал: http://www.dreamcatcher.ru/docs/pf.html
Перевод: Сгибнев Михаил
Оглавление
1. Начальная конфигурация
+ PF: Начало
+ Списки и макросы
+ Таблицы
+ Фильтрация пакетов
+ NAT
+ Перенаправление трафика
+ Шаблоны для создания наборов правил
PF: Начало
----------
Активация:
Для активизации и чтения правил конфигурации pf при начальной
загрузке, необходимо в [6]/etc/rc.conf прописать следующее:
pf=YES
Для вступления изменений в силу, необходимо перезагрузить систему.
Активизировать и остановить pf можно используя программу pfctl(8):
# pfctl-e # pfctl-d
для запуска и останова соответственно. Обратите внимание, что загрузка
правил при этом не производится, их необходимо подгружать отдельно, до
или после запуска pf.
Конфигурация:
pf читает правила конфигурации из файла [7]/etc/pf.conf во время
загрузки, когда выполняются [8]rc.scripts. Обратите внимание, что
/etc/pf.conf - название по умолчанию и он представляет собой просто
текстовый файл, содержащий наборы правил, загружаемые и
интерпретируемые pfctl(8) и вставляемые в pf(4). Для некоторых
приложений наборы правил могут располагаться в других файлах и
подгружаться после загрузки системы. Как и другие приложения UNIX, pf
обладает большой гибкостью.
Файл pf.conf состоит из семи частей:
1. Макросы: Определяемые пользователем переменные, которые могут
содержать адреса IP, имена интерфейсов, и т.д.
2. Таблицы: Применяются для хранения списков IP адресов
3. Опции: Параметры, влияющие на работу pf
4. Scrub: Подготовка пакета к нормализации и дефрагментации
5. Очереди: Обеспечивает управление полосой пропускания и
установку приоритетов пакета.
6. Трансляции: Контроль NAT и перенаправлением пакета
7. Правила фильтрации: Осуществляют выборочную фильтрацию пакетов
на интерфейсах
За исключением макросов и таблиц порядок следования разделов в файле
должен быть таким же, но допускается отсутствие некоторых пунктов.
Пустые строки игнорируются, и строки начинающийся с # считаются
комментарием.
Управление:
После начальной загрузки управление pf может осуществляться через
программу pfctl(8). Вот несколько примеров:
# pfctl -f /etc/pf.conf загрузить pf.conf
# pfctl -nf /etc/pf.conf анализировать файл, но не загружать
# pfctl -Nf /etc/pf.conf загрузить только правила NAT из файла
# pfctl -Rf /etc/pf.conf загрузить только правила фильтрации
# pfctl -sn показать текущие правила NAT
# pfctl -sr показать текущие правила фильтрации
# pfctl -ss показать текущее состояние таблиц
# pfctl -si показать статистику правил и состояние счетчиков
# pfctl -sa показать все
Для полного списка команд смотрите man pfctl(8).
Списки и макросы
----------------
Списки:
Списки позволяют определять множества, имеющие общие признаки в
пределах правила - такие как IP адреса, номера портов и т.д. Таким
образом, вместо прописывания нескольких правил фильтрации для каждого
IP адреса, который должен быть заблокирован, мы можем определить
список IP адресов в пределах одного правила. Списки должны находиться
внутри скобок {}.
Когда pfctl(8) доходит до списка при загрузке наборов правил, он
раскладывает их на отдельные правила, для каждого элемента списка. Для
примера:
block out on fxp0 from { 192.168.0.1, 10.5.32.6 } to any
Будет преобразован в:
block out on fxp0 from 192.168.0.1 to any
block out on fxp0 from 10.5.32.6 to any
Множественные списки могут применяться не только для блокировки:
rdr on fxp0 proto tcp from any to any port { 22 80 } -> 192.168.0.6
block out on fxp0 proto { tcp udp } from { 192.168.0.1, 10.5.32.6 } to any port { ssh telnet }
Стоит отметить, что запятая в списке является необязательной.
Макросы:
Макросы - определяемые пользователем переменные, которые могут держать
IP адреса, номера портов, имена интерфейсов, и т.д. Макросы позволят
облегчить написание наборов правил и сделают поддержание набора правил
несравненно легче.
Имена макросов должны начаться с символа и могут содержать символы,
цифры, и символы подчеркивания. Названия макросов не могут носить
имена зарезервированных слов, типа pass, out, или queue.
ext_if = "fxp0"
block in on $ext_if from any to an
Это создаст макрос с именем ext_if. При использовании макроса, его
имени должен предшествовать знак $.
Макросы также могут быть расширены до списков.
friends = "{ 192.168.1.1, 10.0.2.5, 192.168.43.53 }"
Макросы могут определяться рекурсивно. В этом случае должен
использоваться следующий синтаксис:
host1 = "192.168.1.1"
host2 = "192.168.1.2"
all_hosts = "{" $host1 $host2 "}"
Теперь макрос $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 - заставляет ядро сохранять таблицу в памяти, даже когда
никакие правила к ней не обращаются. Без этого атрибута, ядро
автоматически удалит таблицу, когда последнее правило, ссылающееся
на нее будет отработано.
Пример:
table <goodguys> { 192.0.2.0/24 }
table <rfc1918> const { 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 }
table <spammers> persist
block in on fxp0 from { , } to any
pass in on fxp0 from <goodguys> to any
Адреса могут также быть определены, используя модификатор типа
отрицание (или "не"):
table <goodguys> { 192.0.2.0/24, !192.0.2.5 }
Таблица goodguys теперь содержит адреса сети 192.0.2.0/24, за
исключением адреса 192.0.2.5.
Обратите внимание, что имена таблицы всегда включаются в <>.
Таблицы могут также могут заполняться из файлов, содержащих список
адресов IP и сетей:
table persist file "/etc/spammers"
block in on fxp0 from to any
Файл/etc/spammers содержал бы список IP адресов или сетей CIDR. Любая
строка начинающаяся с #, будет обработана как комментарий и
проигнорирована.
Управление с помощью pfctl:
Таблицы могут управляться на лету, используя pfctl(8). Например, для
добавления в таблицу <spammers> :
# pfctl -t spammers -Tadd 218.70.0.0/16
Это также откроет таблицу, если она не существует. Посмотреть значения
в таблице:
# pfctl -t spammers -Tshow
Аргумент -v может использоваться совместно с -Tshow для отображнения
каждой записи в таблице. Для удаления адреса из таблицы:
# pfctl -t spammers -Tdelete 218.70.0.0/16
Для получения дополнительной информации, смотрите pfctl(8).
Определение адресов:
В дополнение к IP адресу, хосты могут обозначаться по имени. Когда имя
хоста разрешается в IP адрес, то оно может быть помещено в таблицу.
Также IP адреса могут быть введены в таблицу с использованием имени
интерфейса или ключевого слова "self", при этом в таблицу будут
добавляться все IP адреса, назначенные интерфейсу.
Соответствие адресов:
Поиск адреса в таблице возвратит наиболее соответствующее значение.
Это учитывается при создании таблиц типа:
table { 172.16.0.0/16, !172.16.1.0/24, 172.16.1.100 }
block in on dc0 all
pass in on dc0 from to any
К любому пакету, входящему через 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 - не соответствует таблице и будет блокирован
Фильтрация пакетов
------------------
Введение:
Фильтрация пакета - выборочное принятие или блокирование пакета,
проходящего через сетевой интерфейс. pf(4) может использоваться для
контроля пакетов на 3 уровне модели OSI (IPv4 и IPv6) и 4 уровне
модели OSI (TCP, UDP, ICMP, и ICMPv6). Наиболее часто используется в
качестве критериев оценки - протокол, адрес назначения, источник и
порт адресата.
Правила фильтра определяют критерии, которым пакет должен
соответствовать. Правила просматриваются последовательно. Если пакет
не соответствует правилу, содержащему ключевое слово quick, пакет
будет оценен, как не соответствующий ни одному правилу, прежде, чем
будетпросмотрен весь список. Приоритет имеет последнее правило списка,
соответствующее пакету. Есть подход "разрешать все" в начале списка,
тогда, если пакет не блокируется ни одним последующим правилом, он
будет пропущен.
Синтаксис правил:
Полный синтаксис правила:
action direction [log] [quick] on interface [af] [proto protocol] \
from src_addr [port src_port] to dst_addr [port dst_port] \
[tcp_flags] [state]
- action
Действие, которое будет применяться к пакету - это pass или block.
pass передаст пакет назад к ядру для дальнейшей обработки , в то время
как block будет реагировать в соответствии с [16]block-policy. Реакция
по умолчанию может быть отменена, определяя или block drop или block
return.
- direction
Описывает направление движения пакета - in или out.
- log
Определяет, что пакет должен быть зарегистрирован через pflogd(8).
Если правило определено как keep state, modulate state, или synproxy
state, то будет зарегистрирован только первый пакет, создавший
правило. Чтобы регистрировать все пакеты, используйте log-all.
- quick
Если пакет соответствует правилу quick, правило считают последним
правилом соответствия, и предпринимается указанное действие.
- interface
Имя сетевого интерфейса, через который проходит пакет.
- af
Семейство адреса пакета, или inet для IPv4 или inet6 для IPv6. PF
обычно способен определить этот параметр, анализируя источник и/или
адрес назначения.
- protocol
Протокол пакета:
* tcp
* udp
* icmp
* icmp6
* Имя протокола из [17]/etc/protocols
* Номер протокола от 0 до 255
* Используя [18]список
- src_addr, dst_addr
Источник/адрес назначения в заголовке IP. Адреса могут быть определены
как:
* Единственный адрес IPv4 или адрес IPv6.
* Блок сети CIDR
* Полностью определенное имя домена, которое будет определено через
DNS после загрузки правил. Все определенные имена будут заменены
IP адресами.
* Имя сетевого интерфейса. Любые адреса, назначенные интерфейсу,
будут вставлены в правило.
* Имя сетевого интерфейса, сопровождаемого / сетевой маской
(например /24). Каждый адрес на интерфейсе будет объединен с
сетевой маской, чтобы формировать блок сети CIDR
* Имя сетевого интерфейса в круглых скобках (). Это говорит PF
модифицировать правило, если IP адрес(а) на названном интерфейсе
изменяется. Это полезно на интерфейсе, получающего адрес через
DHCP или модемного соединения, поскольку не надо каждый раз
перезагружать правила.
* Имя сетевого интерфейса, сопровождаемого ключевыми словами
:network или :broadcast. В результате в правила будет загружен
адрес сети CIDR (к примеру 192.168.0.0/24) или широковещательный
адрес (к примеру 192.168.0.255)
* Таблица.
* Любое вышеупомянутое, но отрицаемое(инвертированное) с
использованием ! ("не") модификатора.
* Набор адресов, используя список
* Ключевое слово any
* Ключевое слово all, сокращенное от from any to any.
- src_port, dst_port
Порт источника/адресата. Порты могут быть определены как:
* Номер между 1 и 65535
* Имя протокола из [19]/etc/services
* Набор портов, используя список
* Диапазон:
+ ! = (не равный)
+ < (меньше чем)
+ > (больше чем)
+ <= (меньше чем или равный)
+ >= (больше чем или равный)
+ > <(диапазон)
+ <> (обратный диапазон)
Последние два - двойные операторы (они берут два параметра) и не
включают параметры в диапазон.
- tcp_flags
Определяет флаги, которые должны быть установлены в заголовке TCP при
использовании proto tcp. Флаги определяются как flags check/mask.
Например: flags S/SA - PF будет смотреть на S и A (SYN и ACK) флаги и
соответствовать правилу, если установлен флаг SYN.
state
Определяет, сохраняется ли информация о состоянии на пакетах,
соответствующих этому правилу.
* keep state - работает с TCP, UDP, и ICMP
* modulate state - работы только с TCP. PF генерирует Initial
Sequence Numbers (ISNs) для пакетов, соответствующих этому
правилу.
* synproxy state - проксирует входящие TCP соединения, что помогает
защищать сервера от TCP SYN флуда и спуфинга. Эта опция включает
функциональные возможности keep state и modulate state.
Default Deny:
Рекомендуемой практикой является "default deny".
Для создания политики "default deny":
block in all
block out all
Это заблокирует все пакеты, протоколы, IP адреса отовсюду куда угодно.
Прохождение трафика:
Теперь трафику необходимо явно пройти разрешающиее правило, чтобы быть
пропущеным. Это - то, где критерии пакета типа порта
источника/адресата, источника/адреса назначения, и протокола входят в
игру. Всякий раз, когда трафику разрешают пройти через систему сетевой
защиты, правило(а) должно быть записано настолько конкретным,
насколько это возможно. Это должно гарантировать,что только
разрешенный трафик пройдет через систему защиты.
Некоторые примеры:
# Pass traffic in on dc0 from the local network, 192.168.0.0/24,
# to the OpenBSD machine's IP address 192.168.0.1. Also, pass the
# return traffic out on 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
# Pass TCP traffic in on fxp0 to the web server running on the
# OpenBSD machine. The interface name, fxp0, is used as the
# destination address so that packets will only match this rule if
# they're destined for the OpenBSD machine.
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 in quick on fxp0 proto tcp from any to any port ssh
pass in all
Эти првила оцениваются по другому, так как блокирующее правило указано
с параметром quick, что приведет к блокировке и прекращению обработки
пакета, пришедшего на порт ssh. Весь остальной трафик будет пропущен.
Keeping State:
Одной из важных способностей PF является "keeping state" или "stateful
inspection". Контроль состояния относится к способности PF проследить
состояние или прогресс сетевого подключения. информацию о каждом
подключении в таблице состояний, PF способен быстро определить,
принадлежит ли пакет, проходящий через систему сетевой защиты уже
установленному подключению. Если пакет принадлежит уже установленному
соединению, то проверки его правилами не происходит.
Keeping state имеет много преимуществ, включая упрощение набора правил
и повышение производительности пакетного фильтра. PF способен
согласовывать пакеты, двигающиеся в любом направлении, и так как
пакеты, соответствующие stateful подключению не проходят ruleset
оценку, время, затрачиваемое PF на обработку пакетов может быть
значительно уменьшено.
Когда в правиле установлена опция keep state, первый пакет,
соответствующий правилу создает "state" между отправителем и
получателем. Теперь не только пакеты от отправителя к получателю, но и
от получателя к отправителю не проходят проверку правилами фильтрации.
Например:
pass out on fxp0 proto tcp from any to any keep state
Таким образом любой исходящий трафик TCP от кого угодно куда угодно и
обратный трафик будет пропущен и будут созданы временные правила
"state" между отправителем и получателем. Это - хорошая особенность,
ее использование значительно улучшает работу вашей системы сетевой
защиты, поскольку поиски в правилах " state" значительно быстрее чем
прохождение пакета через правила фильтра.
modulate state работает точно так же как и keep state, за исключением
того, что это работает только с пакетами TCP. С использованием
modulate state Initial Sequence Number (ISN) исходящего соединения
будет рандомизирован. Это полезно для того, чтобы защитить
подключения, инициализированные некоторыми операционными системами, у
которых ISN легко предсказуем.
Keep state на исходящих пакетах TCP, UDP, и ICMP с рандомизацией ISN:
pass out on fxp0 proto tcp from any to any modulate state
pass out on fxp0 proto { udp, icmp } from any to any keep state
Другое преимущество keeping state - это передача ICMP трафика через
пакетный фильтр. Для примера, если keep state установлен для TCP
соединений, ICMP пакеты в обычном состоянии заблокированы, то после
установления соединения ICMP пакеты будут проходить через через
систему сетевой защиты.
Важно обратить внимание, что stateful подключения ограничены
интерфейсом, на котором они были созданы. особенно важно это на
маршрутизаторах и системах сетевой защиты, работающих с pf, особенно
когда действует политика "default deny". Если система сетевой защиты
сохраняет состояние на всех исходящих подключениях на внешнем
интерфейсе, пакетам все еще нужно явно проходить внутренний интерфейс.
Обратите внимание что правила nat, binat, и rdr неявно создают "state"
для того, чтобы пакет прошел фильтр.
Keeping State для UDP:
Каждый слышал, что для UDP нельзя создать "state" , так как это
протокол без обратной связи! В то время как истинно, что UDP сеанс
связи не имеет никакого понятия состояния (явное начало и останов
сеанса связи), это не имеет никакого воздействия на способность PF
создать состояние для UDP сеанса. В случае протоколов без "начальных"
и "конечных" пакетов, PF просто следит, сколько прошло времени с
момента прохождения первого пакета. Если время ожидания превышено,
правило "state" удаляется. Значения времени ожидания могут быть
установлены в разделе [20]вариантов pf.conf
Флаги TCP:
* F: Отправитель закончил посылку байтов.
* S: Флаг для синхронизации номеров сегментов, используется при
установлении связи.
* R: Прерывание связи.
* P: Этот сегмент требует выполнения операции push. Получатель
должен передать эти данные прикладной программе как можно быстрее.
* A: Номер октета, который должен прийти следующим, правилен.
* U: Флаг важной информации, поле Указатель важной информации имеет
смысл, если urg=1.
* E: ECE - Уведомление о перегрузке (пр.п. - не знаю что такое...)
* W: CWR - Уменьшение окна перегрузки (пр.п. - не знаю что такое...)
PF оценивает флаги когда в правиле встречается ключевое слово flags и
имеет следующий синтаксис:
flags check/mask
mask говорит смотреть только указанные флаги и check определяет, какой
флаг(и) должен "включен" в заголовке пакета для соответствия правилу.
pass in on fxp0 proto tcp from any to any port ssh flags S/SA
Вышеупомянутое правило передает трафик TCP с установленным флагом SYN,
и только при наличии флагов SYN и ACK.
Обратите внимание: в предыдущих версиях OpenBSD, поддерживался
следующий синтаксис:
. . . flags S
Теперь нет. /mask всегда должна быть определена.
Контоль флагов очень часто используется вместе с keep state правилами
для улучшения контроля за динамическими правилами.
pass out on fxp0 proto tcp all flags S/SA keep state
Это разрешило бы создание динамического правила на любом уходящем
пакете TCP с установленным флагом SYN, и только при наличии флагов SYN
и ACK.
Нужно быть внимательным при использовании флагов., понимать что
конкретно делается и почему. Советам тоже особо не верьте, так как
многие бы посоветовали создавать правило "только если флажок SYN
установлен и никакие другие". Это кончится следующим:
. . . flags S/FSRPAUEW bad idea!!
Проблема - некоторые сайты, используют флаг ECN и любой сайт,
используя ECN, пробуя соединиться с Вами, будет отклонен в
соответствии с правилом выше. Намного лучше:
. . . flags S/SAFR
В то время как это является практическим и безопасным, также ненужно
проверять флаги FIN и RST если трафик [21]scrubbed.
Процесс вычищения заставит PF блокировать любые входящие пакеты с
незаконными комбинациями флагов TCP (типа SYN и FIN или SYN и RST).
Строго рекомендуют всегда вычистить входящий трафик:
scrub in on fxp0
.
.
.
pass in on fxp0 proto tcp from any to any port ssh flags S/SA keep state
TCP SYN Proxy:
Обычно, когда клиент устанавливает соединение с сервером вне сети, pf
передает пакеты [22]установления связи между клиентом и сервером по
мере их поступления. PF также имеет способность проксировать процесс
создания соединения. При этом PF установит связь с клиентом,
инициирует установление связи с сервером и затем соединит клиента и
сервер. Преимущества этого процесса в том, что никакие пакеты не
посылаются серверу прежде, чем клиент завершит установление связи. Это
устраняет угрозу spoofed TCP SYN флуда, та как клиентское подключение
будет неспособно завершить установление связи и спуфить сервер.
pass in on $ext_if proto tcp from any to $web_server port www flags S/SA synproxy state
Теперь соединение с сервером будет проксировано пакетным фильтром.
Так как при работе synproxy state образуются динамические правила, то
SYN прокси включает в себя функциональность keep state и modulate
state.
SYN прокси-сервер не будет работать, если PF выполняется на bridge(4).
Блокирование спуфинга:
Под термином "spoofing" понимается подмена IP адреса в пакетах с целью
скрыть их реальный адрес или выдать себя за другой узел сети. При этом
злоумышленник может попытаться получить доступ к сетевым услугам,
которые ограничены некоторыми IP адресами или скрывать свой истинный
адрес при проведении атаки на сеть.
PF предлагает некоторую защиту против спуфинга с помощью ключевого
слова antispoof
antispoof [log] [quick] for interface [af]
- log
Определяет, что соответствующий пакет должен быть зарегистрирован
через pflogd (8).
- quick
Если пакет соответствует правилу quick, правило считают последним
правилом соответствия, и предпринимается указанное действие.
- interface
Сетевой интерфейс, на котором активизируется защита от спуфинга. Это
может также быть списком интерфейсов.
- af
Семейство адреса - или inet для IPv4 или inet6 для IPv6.
Пример:
antispoof for fxp0 inet
После загрузки наборов правил, там где встречено ключевое слово
antispoof правило будет расширено в два правила фильтра. Принимая, что
интерфейс fxp0 имеет адрес IP 10.0.0.1 и подсетевую маску
255.255.255.0 (то есть,/24), вышеупомянутое правило расширилось бы до:
block in on ! fxp0 inet from 10.0.0.0/24 to any
block in inet from 10.0.0.1 to any
Эти правила достигают двух вещей:
* Блокирует весь трафик, исходящий из сети 10.0.0.0/24, который не
проходит в через fxp0. Так как сеть 10.0.0.0/24 находится на
интерфейсе fxp0, пакеты с исходным адресом в этом сетевом блоке
никогда не должны появляться ни на каком другом интерфейсе.
* Блокирует весь входящий трафик с IP адреса 10.0.0.1 на fxp0. Хост
никогда не не должен посылать пакеты себе через внешний интерфейс,
так что любые входящие пакеты с исходным адресом, принадлежащим
хосту можно счесть злонамеренными.
ОБРАТИТЕ ВНИМАНИЕ: При расширении правила antispoof также блокируются
пакеты, посланные по петлевому интерфейсу к местным адресам. Эти
адреса нужно передать явно. Пример:
pass in quick on lo0 all
antispoof for fxp0 inet
Использование antispoof должно быть ограничено интерфейсами, которым
был назначен IP адрес. Использование antispoof на интерфейсе без IP
адреса приведет к правилам фильтрации типа:
block drop in on ! fxp0 inet all
block drop in inet all
С этими правилами есть риск блокирования всего прибывающего трафика на
всех интерфейсах.
Опции IP:
По умолчанию pf блокирует пакеты с установленными опциями IP. Это
затрудняет определение типа ОС такими утилитами, как nmap. Если Вы
имеете приложение, которое требует прохождения этих пакетов, типа
multicast или IGMP, Вы можете использовать директиву allow-opts:
pass in quick on fxp0 all allow-opts
Пример правил фильтрации:
Ниже - пример фильтрующих правил. Сервер с запущенным pf действует как
система сетевой защиты между маленькой, внутренней сетью и Internet.
Показаны только фильтрующие правила; правила queueing, nat, rdr и т.д.
в этом примере пропущены.
------------------------------------------------------------------------------
ext_if = "fxp0"
int_if = "dc0"
lan_net = "192.168.0.0/24"
# scrub incoming packets
scrub in all
# setup a default deny policy
block in all
block out all
# pass traffic on the loopback interface in either direction
pass quick on lo0 all
# activate spoofing protection for the internal interface.
antispoof quick for $int_if inet
# only allow ssh connections from the local network if it's from the
# trusted computer, 192.168.0.15. use "block return" so that a TCP RST is
# sent to close blocked connections right away. use "quick" so that this
# rule is not overridden by the "pass" rules below.
block return in quick on $int_if proto tcp from ! 192.168.0.15 \
to $int_if port ssh flags S/SA
# pass all traffic to and from the local network
pass in on $int_if from $lan_net to any
pass out on $int_if from any to $lan_net
# pass tcp, udp, and icmp out on the external (Internet) interface.
# keep state on udp and icmp and modulate state on tcp.
pass out on $ext_if proto tcp all modulate state flags S/SA
pass out on $ext_if proto { udp, icmp } all keep state
# allow ssh connections in on the external interface as long as they're
# NOT destined for the firewall (i.e., they're destined for a machine on
# the local network). log the initial packet so that we can later tell
# who is trying to connect. use the tcp syn proxy to proxy the connection.
pass in log on $ext_if proto tcp from any to { !$ext_if, !$int_if } \
port ssh flags S/SA synproxy state
------------------------------------------------------------------------------
NAT
---
Введение:
Трансляция Сетевого адреса (NAT) - отобразить полную сеть (или сети) к
единственному IP адресу. NAT необходим, когда количество IP адресов,
Выделенный Вам Вашим провайдером меньше чем общее количество
компьютеров, для которых Вы желаете обеспечить доступ Internet. NAT
описан в RFC 1631.
NAT позволяет Вам использовать в своих интересах зарезервированные
блоки адресов, описанные в RFC 1918. Как правило, Вы будете
использовать один или несколько блоков адресов из следующего
диапазона:
* 10.0.0.0/8 (10.0.0.0 - 10.255.255.255)
* 172.16.0.0/12 (172.16.0.0 - 172.31.255.255)
* 192.168.0.0/16 (192.168.0.0 - 192.168.255.255)
Для работы NAT в OpenBSD необходимо иметь как минимум два сетевых
интерфейса, один из которых выходит в Internet, а второй во внутреннюю
сеть. NAT будет транслировать запросы с внутренней сети, так, как
будто они исходят от Вашей OpenBSD системы.
Как работает NAT:
Когда клиент из внутренней сети посылает запрос в Internet, создаются
IP пакеты, которые шлются к месту назначения. Эти пакеты содержат всю
информацию об обратном адресе, необходимую для получения ответа. NAT
заинтересован этими частями информации:
* Исходный адрес IP (например, 192.168.1.35)
* Исходный TCP или UDP порт (например, 2132)
Когда пакеты проходят через NAT, они изменяются таким образом, чтобы
они, казалось, исходили от NAT шлюза непосредственно. NAT будет делать
запись изменений в таблице состояний так, чтобы это могло,
a), чтобы полностью вернуть изменения на возвращающихся пакетах и
b) гарантировать, что вернувшиеся пакеты пройдут систему сетевой
защиты и не будут блокированы.
Например, могут произойти следующие изменения:
* Исходный IP: будет заменен внешним адресом шлюза (например, 24.5.0.5)
* Исходный порт: будет заменен беспорядочно выбранным,
неиспользованным портом на шлюзе (например, 53136)
Ни внутренняя машина, ни главный компьютер Internet не знают об этих
шагах трансляции. Для внутренней машины NAT система - просто шлюз
Internet. Для ресурса, распложенного в Internet пакеты прибывают
непосредственно от NAT системы; он не догадывается о существовании
клиентской машины. Когда ресурс отвечает на запрос внутренней машины
он будет обращаться к внешнему IP адресу NAT шлюза (24.5.0.5) и порту
(53136). NAT шлюз будет искать соответствующую запись в таблице ,
чтобы определить, соответствуют ли пакеты ответа уже установленному
подключению. Уникальное соответствие будет найдено на основании
комбинации IP/порта, которая говорит PF, что пакеты принадлежат
подключению, инициализированному внутренней машиной 192.168.1.35. PF
будет тогда делать обратные изменеия для приходящих пакетов и
отправлять пакеты внутренней машине.
Трансляция ICMP пакетов происходит таким же способом, но без исходной
модификации порта.
NAT и фильтрация пакетов:
ОБРАТИТЕ ВНИМАНИЕ: Оттранслированные пакеты должны все еще должны
проходить через механизм фильтрации и будут блокированы или переданы
на основании правил фильтрации. Единственное исключение будет сделано
при наличии ключевого слова pass при использовании в пределах правила
NAT. Это заставит NATed пакеты передавать через механизм фильтрации.
Также знайте, что трансляция происходит перед фильтрацией и механизм
фильтрации будет видеть оттранслированный пакет с оттранслированным
адресом IP и портом.
IP форвардинг:
Так как NAT почти всегда используется на маршрутизаторах и сетевых
шлюзах, вероятно будет необходимо допустить форвардинг пакетом между
сетевыми интерфейсами OpenBSD машины. Это осуществляется с помощью
механизма sysctl(3) :
# sysctl -w net.inet.ip.forwarding=1 # sysctl -w
net.inet6.ip6.forwarding=1 (if using IPv6)
Для того, чтобы сделать изменения постоянными, внесите в
/etc/sysctl.conf:
net.inet.ip.forwarding=1 net.inet6.ip6.forwarding=1
Эти строки присутствуют, но прокомментированны (знаком #) в заданной
по умолчанию установке. Удалите #, и сохраните файл. Форвардинг
пакетов IP будет будет разрешен после перезагрузки.
Конфигурирование NAT:
Полный формат правила NAT выглядит следующим образом:
nat [pass] on interface [af] from src_addr [port src_port] to \
dst_addr [port dst_port] -> ext_addr [pool_type] [static-port]
- nat
Ключевое слово, которое начинает правило NAT
- pass
Транслировать пакеты, полностью обходя правила фильтрации
- interface
Имя сетевого интерфейса, на который будут транслироваться пакеты
- af
Семейство адреса пакета, или inet для IPv4 или inet6 для IPv6. PF
обычно способен определить этот параметр, анализируя источник
и/или адрес назначения.
- src_addr
Внутренний адрес пакетов, которые будут оттранслированы. Исходный
адрес может быть определен как:
* Единственный адрес IPv4 или адрес IPv6.
* Блок сети CIDR
* Полностью определенное имя домена, которое будет определено через
DNS после загрузки правил. Все определенные имена будут заменены
IP адресами.
* Имя сетевого интерфейса. Любые адреса, назначенные интерфейсу,
будут вставлены в правило.
* Имя сетевого интерфейса, сопровождаемого / сетевой маской
(например /24). Каждый адрес на интерфейсе будет объединен с
сетевой маской, чтобы формировать блок сети CIDR
* Имя сетевого интерфейса, сопровождаемого ключевыми словами
:network. В результате в правила будет загружен адрес сети CIDR (к
примеру 192.168.0.0/24).
* Таблица.
* Любое вышеупомянутое, но отрицаемое(инвертированное) с
использованием ! ("не") модификатора.
* Набор адресов, используя список
* Ключевое слово any
- src_port
Исходный порт. Порты могут быть определены как:
* Номер между 1 и 65535
* Имя протокола из [23]/etc/services
* Набор портов, используя список
* Диапазон:
+ ! = (не равный)
+ < (меньше чем)
+ > (больше чем)
+ <= (меньше чем или равный)
+ >= (больше чем или равный)
+ > <(диапазон)
+ <> (обратный диапазон)
Последние два - двойные операторы (они берут два параметра) и не
включают параметры в диапазон. Опция port обычно не используется в
правилах NAT, потому что весь трафик проходит через правило
независимо от используемого порта (ов).
- dst_addr
Адрес назначения пакетов, которые будут оттранслированы. Адрес
назначения имеет те же параметры, что и исходный адрес.
- dst_port
Порт адресата. Параметры такие же как и в src_port.
- ext_addr
Внешний адрес NAT роутера, к которому будут оттранслированы пакеты.
Внешний адрес может быть определен как:
* Единственный адрес IPv4 или адрес IPv6.
* Блок сети CIDR
* Полностью определенное имя домена, которое будет определено через
DNS после загрузки правил. Все определенные имена будут заменены
IP адресами.
* Имя внешнего сетевого интерфейса. Любые адреса, назначенные
интерфейсу, будут вставлены в правило.
* Имя сетевого интерфейса в круглых скобках (). Это говорит PF
модифицировать правило, если IP адрес(а) на названном интерфейсе
изменяется. Это полезно на интерфейсе, получающего адрес через
DHCP или модемного соединения, поскольку не надо каждый раз
перезагружать правила.
* Имя сетевого интерфейса, сопровождаемого ключевыми словами
:network. В результате в правила будет загружен адрес сети CIDR (к
примеру 192.168.0.0/24).
* Набор адресов, используя список
- pool_type
Определяет тип пула адреса, используемый для трансляции.
- static-port
Говорит PF не транслировать исходный порт в TCP и UDP пакетах.
Это приводит к следующему каноническому виду правила:
nat on tl0 from 192.168.1.0/24 to any -> 24.5.0.5
Это правило подразумевает то, что на интерфейсе tl0 применяется NAT к
любым пакетам, исходящих из сети 192.168.1.0/24 и исходный адрес
заменяется на IP 24.5.0.5.
В то время как вышеупомянутое правило правильно, такая форма записи не
рекомендуется. Обслуживание таких правил будет затруднено, так как
любое изменение внешних или внутренних сетевых адресов будет требовать
переколбашивать все правила в списке. Сравниете с этой записью (tl0,
является внешним, dc0 внутренним):
nat on tl0 from dc0/24 to any -> tl0
Преимущество должно быть довольно очевидно: Вы можете изменить IP
адрес любого интерфейса, не изменяя это правило.
При указании имени интерфейса для адреса трансляции как в примере
выше, IP адрес определяется в pf.conf во время загрузки, а не на лету.
Если Вы используете протокол DHCP, чтобы конфигурировать ваш внешний
интерфейс, это может быть проблемой. Так как адрес, назначенный
интерфейсу может измениться, а в правилах останется старый адрес,
пакеты клиентских машин будут уходить в никуда. Чтобы обойти это, Вы
можете сказать PF автоматически модифицировать адрес трансляции,
помещая имя интерфейса в круглые скобки.
nat on tl0 from dc0/24 to any -> (tl0)
Есть одно главное ограничение: только первый псевдоним на интерфейсе
будет оценен, когда имя интерфейса помещено в круглые скобки.
Bidirectional Mapping (1:1 mapping):
bidirectional mapping - это постоянное соединение, использующее
правило binat. Правило binat устанавливает соответствие портов между
внутренним адресом и внешним адресом. Это может быть полезно,
например, когда внутри сети находится сервер, который должен быть
доступен извне. Подключения от Internet до внешнего адреса будут
оттранслированы к внутреннему адресу, и подключения от сервера сети
(типа запросов DNS) будут оттранслированы к внешнему адресу.
Пример:
web_serv_int = "192.168.1.100"
web_serv_ext = "24.5.0.6"
binat on tl0 from $web_serv_int to any -> $web_serv_ext
Исключения из правил трансляции:
Исключения могут быть сделаны к правилам трансляции используя ключевое
слово no. Например:
no nat on tl0 from 192.168.1.10 to any
nat on tl0 from 192.168.1.0/24 to any -> 24.2.74.79
В этом случае вся сеть 192.168.1.0/24 за исключением 192.168.1.10
будет транслирована на IP адрес 24.2.74.79.
Обратите внимание, что первое правило побеждает, если указано "no", то
пакет не будет транслирован. Ключевое слово "no" нельзя использовать с
правилами with binat и rdr.
Проверка состояния NAT:
Просмотреть активные трансляции NAT можно с помощью pfctl(8),
используя опцию -s state.
# pfctl -s state
TCP 192.168.1.35:2132 -> 24.5.0.5:53136 -> 65.42.33.245:22 TIME_WAIT:TIME_WAIT
UDP 192.168.1.35:2491 -> 24.5.0.5:60527 -> 24.2.68.33:53 MULTIPLE:SINGLE
Расшифровка (только верхней строки):
TCP
Протокол, используемый подключением.
192.168.1.35:2132
Адрес (192.168.1.35) машины из внутренней сети и исходный порт (2132)
. Также это адрес, который заменяется в заголовке IP пакета.
24.5.0.5:53136
Адрес IP (24.5.0.5) и порт (53136) на шлюзе, к которому пакеты
транслируются.
65.42.33.245:22
Адрес IP (65.42.33.245) и порт (22), с которым внутренняя машина
соединяется.
TIME_WAIT:TIME_WAIT
Это указывает на состояние соединения - в состоянии подключения.
Перенаправление трафика
-----------------------
Введение:
Когда работает NAT, Вы можете обеспечить весь офис доступом в
Internet. Что делать, когда у Вас есть машина позади NAT, к которой
необходимо обращаться снаружи. Это тот случай, когда в игру вступает
перенаправление. Перенаправление позаоляет входящему трафику быть
посланным в нутрь сети, стоящей за NAT.
Рассмотрим пример:
rdr on tl0 proto tcp from any to any port 80 -> 192.168.1.20
Эта строка переадресовывает трафик, приходящий на TCP порт 80 на
машину, находящуюся в локальной сети, с адресом 192.168.1.20. from any
to any запись в правиле rdr может быть весьма полезна. Но если выз
наете какие IP адреса или подсети могут обращаться к этой машине, то
Вы можете ограничить доступ:
rdr on tl0 proto tcp from 27.146.49.0/24 to any port 80 -> 192.168.1.20
Это правило будет переадресовывать только одну подсеть 27.146.49.0/24.
Это наводит на мысль что таким образом можно предоставлять доступ
различным хостам внешней сети к различным хостам внутренней сети.
Например, Вы могли бы иметь удаленных пользователей, обращающихся к
своим собственным машинам внутри локальной сети.
Например:
rdr on tl0 proto tcp from 27.146.49.14 to any port 80 -> 192.168.1.20
rdr on tl0 proto tcp from 16.114.4.89 to any port 80 -> 192.168.1.22
rdr on tl0 proto tcp from 24.2.74.178 to any port 80 -> 192.168.1.23
Перенаправление и фильтрация пакетов:
ОБРАТИТЕ ВНИМАНИЕ: Оттранслированные пакеты должны все еще должны
проходить через механизм фильтрации и будут блокированы или переданы
на основании правил фильтрации.
Единственное исключение будет сделано при наличии ключевого слова pass
при использовании в пределах правила rdr. Это заставит
переадресованные пакеты передавать через механизм фильтрации.
Также учтите, что механизм фильтрации будет видеть оттранслированный
пакет, с уже измененными адресами и портами.
Рассмотрим такой сценарий:
* 192.0.2.1 - хост в интернете
* 24.65.1.13 - внешний адрес OpenBSD маршрутизатора
* 192.168.1.5 - внутренний адрес машины в локальной сети
Правило перенаправления:
rdr on tl0 proto tcp from 192.0.2.1 to 24.65.1.13 port 80 -> 192.168.1.5 8000
Перед правилом rdr пакет имеет характеристики:
* Исходный адрес: 192.0.2.1
* Исходный порт: 4028 (произвольно выбранный операционной системой)
* Адрес назначения: 24.65.1.13
* Порт адресата: 80
Пакет после правила rdr:
* Исходный адрес: 192.0.2.1
* Исходный порт: 4028
* Адрес назначения: 192.168.1.5
* Порт адресата: 8000
Пакет будет обрабатываться правилами фильтрации.
Значение защиты:
Перенаправление потенциально может создать проблемы с безопасностью.
Приходится делать отверстие в системе защиты, для того чтобы
внутренние сервисы были доступны снаружи. Если трафик был
перенаправлен на внутренний web сервер и в CGI скрипте или в сервере
была обнаружена уязвимость, это может скомпрометировать машину, а
оттуда и всю внутреннюю сеть.
Этот риск может быть уменьшен, если вывести все серверы, к которым
будут обращения извне, в отдельную сеть.Такая сеть называется
"демилитаризованной зоной"(DMZ) или "частная сервисная сеть"(PSN).
Даже если сервер сети будет скомпрометирован, все неприятные
последствия будут ограничены этой зоной, используя фильтрацию трафика
из и в DMZ.
Перенаправление и отражение:
Часто, правила переназначения используются, чтобы отправить входящие
подключения с Internet на местный сервер с частным адресом во
внутренней сети, как в данном примере:
server = 192.168.1.40
rdr on $ext_if proto tcp from any to $ext_if port 80 -> $server port 80
Но при проверке работы этого правила из локальной сети мы потерпим
неудачу. Это связано с тем, что правило применяется только к пакетам,
которые проходят через указанный интерфейс ($ext_if, внешний
интерфейс, в примере). Соединение с внешним адресом системы сетевой
защиты с машины внутри локальной сети , однако, не подразумевает, что
пакеты фактически пройдут через его внешний интерфейс. Стек TCP/IP на
системе сетевой защиты сравнивает адрес назначения входящих пакетов с
его собственными адресами и псевдонимами и обнаружит подключение с
собой, как только пакеты прийдут на внутренний интерфейс. Такие пакеты
физически не проходят через внешний интерфейс. Таким образом, PF
никогда не видит эти пакеты на внешнем интерфейсе, и правило не
срабатывает.
Добавление второго правила переназначения для внутреннего интерфейса
также не будет иметь положительного эффекта. Когда местный клиент
соединяется с внешним адресом системы сетевой защиты, начальный пакет
установления связи TCP достигает системы сетевой защиты через
внутренний интерфейс. Правило переназначения действительно
применяется, и адрес назначения заменяется адресом внутреннего
сервера. Но исходный адрес не был оттранслирован, и все еще содержит
адрес местного клиента, так что сервер посылает свои ответы
непосредственно клиенту. Система сетевой защиты никогда не видит ответ
и не имеет никакого шанса должным образом полностью контролировать
трансляцию. поскольку клиент получает ответ непосредственно от
сервера, а запрос на соединение посылается через pf, на сервере
сетевой защиты соединение никогда так и не будет установлено.
Однако бывает желательным соединять внутренних клиентов также как и
внешних. Решение данной проблемы существует.
Разделение горизонтов DNS:
Возможно такое конфигурирование сервера DNS, чтобы он отвечал на
запросы компьютеров локальной сети по другому, чем для запросов из
Internet. То есть, чтобы клиенты внутренней сети получали внутренний
адрес сервера, тогда они будут соединяться напрямую и система защиты
на роутере вообще задействоваться не будет. Это уменьшает локальный
трафик и снижает нагрузку на маршрутизатор.
Перемещение сервера в DMZ:
Добавив дополнительный сетевой интерфейс к маршрутизатору можно
создать демилитаризованную зону. Переместив туда все серверы, к
которым требуется обращение извне, можно организовать доступ из
локальной сети точно таким же образом, как и для клинтов из Internet.
Использование отдельных сетей имеет несколько преимуществ, включая
улучшение качества защиты. Если сервер (который в нашем случае
является доступным из Internet), будет скомпрометирован, он не сможет
обратиться к компьютерам локальной сети непосредственно, поскольку все
подключения должны будут пройти через систему сетевой защиты.
TCP проксирование:
На системе сетевой защиты может быть установлен универсальный
прокси-сервер TCP, или слушающий на порту, который будет
перенаправлен, или получающий подключения на внутреннем интерфейсе.
Когда локальный клиент соединяется с системой сетевой защиты,
прокси-сервер принимает подключение, устанавливает второе подключение
с внутренним сервером, и передает данные между этими двумя
подключениями.
Простое проксирование можно организовать используя inetd(8) и nc(1).
Следущие записи в etc/inetd.conf создают сокет, привязанный к
петлевому адресу (127.0.0.1) и порту 5000. Подключения
перенаправляются на порт 80 сервера 192.168.1.10.
127.0.0.1:5000 stream tcp nowait nobody /usr/bin/nc nc -w 20 192.168.1.10 80
Следущее правило перенапрвляет порт 80 на внутреннем интерфейсе к
прокси-серверу:
rdr on $int_if proto tcp from $int_net to $ext_if port 80 -> 127.0.0.1 port 5000
Комбинация RDR и NAT:
С дополнительным правилом NAT на внутреннем интерфейсе может быть
решена проблема недостающей переадресации.
rdr on $int_if proto tcp from $int_net to $ext_if port 80 -> $server
no nat on $int_if proto tcp from $int_if to $int_net
nat on $int_if proto tcp from $int_net to $server port 80 -> $int_if
Это заставит начальный пакет от клиента быть оттранслированным снова,
когда он пройдет назад через внутренний интерфейс, заменяя исходный
адрес клиента на внутренний адрес системы сетевой защиты. Внутренний
сервер ответит системе сетевой защиты, которая может полностью
изменить и NAT и RDR трансляции при отправке пакета локальному
клиенту. Эта конструкция довольно сложна, поскольку создает два
различных соединения. Необходимо проявить осторожность при написании
подобных правил, чтобы предотвратить передачу трафика к дугим ресурсам
в обход системы защиты. Обратите внимание, что правило rdr заставит
стек TCP/IP видеть, что пакеты прибывают на внутренний интерфейс с
адресом назначения во внутренней сети. Для того, чтобы запретить шлюзу
выдавать ICMP сообщение клиенту о том, что сервер доступен напрямую,
отключаем эту функцию:
# sysctl -w net.inet.ip.redirect=0
# sysctl -w net.inet6.ip6.redirect=0 (if using IPv6)
Вообще, не стоит использовать такое решение...
Шаблоны для создания наборов правил
-----------------------------------
Введение:
PF предлагает много путей упростить наборы правил. Некоторые из них -
использование макросов и списков. Кроме того грамматика или язык
посторения наборов правил предлагает некоторые шаблоны, призванные
облегчить написание правил. Как правило, чем проще набор правил, тем
легче его поддерживать.
Использование макросов:
Макросы полезны тем, что обеспечивают альтернативу написанию большого
количества раз одинаковых адресов, портов, интерфейсов, и т.д., в
наборе правил. IP адрес сервера изменился? Никаких проблем, только
модифицируйте макрос и используйте свободное время для
самосовершенствования.
Общее соглашение в PF состоит в том, чтобы определить макрос для
каждого сетевого интерфейса. Если, к примеру, была заменена сетевая
карта, то макрос модифицируется и правила выполняются как и прежде.
Другой пример - установка одинакового набора правил на многих машинах,
которые могут иметь разные сетевые интерфейсы:
# define macros for each network interface
IntIF = "dc0"
ExtIF = "fxp0"
DmzIF = "fxp1"
Другое соглашение состоит в использовании макросов для определения
аресов и сетевых блоков:
# define our networks
IntNet = "192.168.0.0/24"
ExtAdd = "24.65.13.4"
DmzNet = "10.0.0.0/24"
Если внутренняя сеть была изменена или расширена, то макрос
изменяется:
IntNet = " {192.168.0.0/24, 192.168.1.0/24} "
После перезагрузки набор правил будет функционировать.
Использование списков:
Давайте посмотрим, какие записи в наборе правил должны иметься, чтобы
отработать RFC 1918 - о блокировании на внешнем интерфейсе адресов из
приватного диапазона сетей.
block in quick on tl0 inet from 127.0.0.0/8 to any
block in quick on tl0 inet from 192.168.0.0/16 to any
block in quick on tl0 inet from 172.16.0.0/12 to any
block in quick on tl0 inet from 10.0.0.0/8 to any
block out quick on tl0 inet from any to 127.0.0.0/8
block out quick on tl0 inet from any to 192.168.0.0/16
block out quick on tl0 inet from any to 172.16.0.0/12
block out quick on tl0 inet from any to 10.0.0.0/8
Теперь взглянем на следующее упрощение:
block in quick on tl0 inet from { 127.0.0.0/8, 192.168.0.0/16, \
172.16.0.0/12, 10.0.0.0/8 } to any
block out quick on tl0 inet from any to { 127.0.0.0/8, \
192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 }
В результате, число записей было уменьшено с восьми до двух. Прилепим
сюда еще и список:
NoRouteIPs = "{ 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 }"
ExtIF = "tl0"
block in quick on $ExtIF from $NoRouteIPs to any
block out quick on $ExtIF from any to $NoRouteIPs
Обратите внимание, что макросы и списки упрощают строение файла
pf.conf, но при просмотре правил с помощью pfctl(8) они будут
расширены. Для примера выше это будет:
block in quick on tl0 inet from 127.0.0.0/8 to any
block in quick on tl0 inet from 192.168.0.0/16 to any
block in quick on tl0 inet from 172.16.0.0/12 to any
block in quick on tl0 inet from 10.0.0.0/8 to any
block out quick on tl0 inet from any to 10.0.0.0/8
block out quick on tl0 inet from any to 172.16.0.0/12
block out quick on tl0 inet from any to 192.168.0.0/16
block out quick on tl0 inet from any to 127.0.0.0/8
Как видно - все это способы упрощения написания файла pf.conf, а не
выполнения правил непосредственно pf(4).
Макросы также могут использоваться для определения непосредственно
правил:
pre = "pass in quick on ep0 inet proto tcp from "
post = "to any port { 80, 6667 } keep state"
# David's classroom
$pre 21.14.24.80 $post
# Nick's home
$pre 24.2.74.79 $post
$pre 24.2.74.178 $post
Расширяется до:
pass in quick on ep0 inet proto tcp from 21.14.24.80 to any \
port = 80 keep state
pass in quick on ep0 inet proto tcp from 21.14.24.80 to any \
port = 6667 keep state
pass in quick on ep0 inet proto tcp from 24.2.74.79 to any \
port = 80 keep state
pass in quick on ep0 inet proto tcp from 24.2.74.79 to any \
port = 6667 keep state
pass in quick on ep0 inet proto tcp from 24.2.74.178 to any \
port = 80 keep state
pass in quick on ep0 inet proto tcp from 24.2.74.178 to any \
port = 6667 keep state
Грамматика PF:
Грамматика PF весьма гибка, что определяет гибкость правил фильтрации.
PF способен вывести некоторые ключевые слова, что означает, что они не
должны быть явно заявлены в правиле.
Устранение ключевых слов:
Политика "default deny", состоящая из двух правил:
block in all block out all
Может быть сокращена до:
block all
Когда направление движения пакета не указано, pf предполагает, что
правило применяется к пакетам, двигающимся в обоих направлениях.
Точно так же и правила "from any to any" и "all" могут быть сокращены.
Для примера:
block in on rl0 all pass in quick log on rl0 proto tcp from any to any port 22 keep state
Упрощается до:
block in on rl0 pass in quick log on rl0 proto tcp to port 22 keep state
Упрощение Return:
В наборе правил обычно принято блокировать пакеты ICMP Unreachable и TCP RST :
block in all block return-rst in proto tcp all block return-icmp in
proto udp all block out all block return-rst out proto tcp all
block return-icmp out proto udp all
Эта конструкция может быть упрощена до:
block return
Когда PF видит ключевое слово return, он посылает надлежащий ответ,
или вообще ничего не посылает, в зависимости от протокола блокируемого
пакета.
Упорядочивание ключевых слов:
Порядок следования следования ключевых слов гибок в большинстве
случаев. Например, правило:
pass in log quick on rl0 proto tcp to port 22 \ flags S/SA keep state
queue ssh label ssh
Может быть записано и так:
pass in quick log on rl0 proto tcp to port 22 \ queue ssh keep state
label ssh flags S/SA
Другие подобные примеры также будут работоспособны.
P.S. Большая просьба всем читателям указывать на замеченные неточности
перевода в форуме:
http://www.dreamcatcher.ru//forum/postlist.php?Cat=&Board=stat_obs
PF. Часть 2. Расширенная конфигурация
-------------------------------------
Оригинал: http://dreamcatcher.ru/docs/pf-faq-part2-rus.html
Перевод: Сгибнев Михаил
Оглавление
* Опции запуска
* Scrub (Нормализация пакета)
* Именованые (Под)Наборы правил и якоря
* Организация очередей и приоритетов
* Адресные пулы и балансировка нагрузки
* Тэгирование пакетов
* Термины, используемые переводчиком
Опции запуска
-------------
Опции используются для контроля за операциями PF. Они могут быть также
указаны в pf.conf, используя следующие директивы:
- set block-policy
Устанавливает поведение по умолчанию для правил как всегда
блокирующее:
* drop - пакеты удаляются без уведомления
* return - возвращается пакет TCP RST для блокированного TCP пакета
и ICMP Unreachable для всего остального Обратите внимание, что
индивидуальные правила фильтра могут отменить заданный по
умолчанию ответ.
- set debug
Установить уровень отладки pf
* none - отладочные сообщения не отображаются
* urgent - отображаются серьезные ошибки. Является значением по
умолчанию
* misc - ошибки различной тяжести (например отображение статуса
normalizer/scrubber пакета и ошибки создания правила state)
* loud - отображать события, возникающие в ходе обычной работы
(например результат работы пассивной системы определения удаленной
ОС)
- set fingerprints file
Определить файл с сигнатурами операционных систем. Используется для
пассивного определения удаленной ОС. По умолчанию /etc/pf.os.
- set limit
* frags - максимальный размер памяти в строках, используемой для
пересборки пакета (scrub правило). По умолчанию 5000.
* src-nodes - максимальный размер памяти в строках, используемой для
хранения IP адресов источника. По умолчанию 10000.states -
максимальный размер памяти в строках, используемой для хранения
таблицы состояний (правила фильтрации keep state). По умолчанию
10000.
- set loginterface int
Устанавливает интерфейс, но котором PF должен вести статистику по
входящим/исходящим байтам и пропущенным/блокированным пакетам.
Одновременно статистика собирается только для однго интерфейса.
Обратите внимание на то, что счетчики в равилах работают
независимо от того, установлен этот параметр или нет.
- set optimization
Оптимизирует работу PF руководствуясь следующими образцами:
* normal - подходит почти для всех сетей. Является значением по
умолчанию.
* high-latency - сети с высоким временем задержки, например,
спутниковые.
* aggressive - позволяет уменьшить обьемы занимаемой памяти за счет
малого таймаута соединений в таблице состояний.
* conservative - максимально консервативные настройки. Позволяет
продлить время жизни записи в таблице состояний за счет некоторого
увеличения объема занимаемой памяти и процессорного времени.
- set state-policy
Определяет манеру работы PF с таблицей состояний (см. Keeping State).
* if-bound - правила привязаны к интерфейсу, на котором открыты.
Если трафик соответствует записи, но пересекает интерфейс
указанный в ней, то соответствия не произойдет. Тогда пакет должен
соответствовать фильтрующему правилу или скинут/отброшен.
* group-bound - также, как и с параметром if-bound, за исключением
того, что принимаются интерфейсы одной грунны - например все ppp
интерфейсы.
* floating - записи могут соответствовать пакету на любом
интерфейсе. Пока пакет соответствует записи не имеет значения,
через какой интерфейс он проходит и он будет пропущен. Является
значением по умолчанию.
- set timeout
* interval - число секунд между пакетами, соответствующих правилу.
Превышение приводит к удалению правила.
* frag - время устаревания несобранного фрагмента пакета.
Пример:
set timeout interval 10
set timeout frag 30
set limit { frags 5000, states 2500 }
set optimization high-latency
set block-policy return
set loginterface dc0
set fingerprints /etc/pf.os.test
set state-policy if-bound
Scrub (Нормализация пакета)
---------------------------
* Введение
* Опции
Введение
"Scrubbing" - это нормализация пакета таким образом, чтобы не осталось
двусмысленности в определении адресата пакета. Директива scrub также
пересобирает фрагментированные пакеты, таким образом осуществляя
защиту некоторых операционных систем от определенного вида атак и
отбрасывает пакеты с недопустимыми флагами. Самая простая форма:
scrub in all
Это позволит делать 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
Убирает бит "don't fragment" из заголовка IP пакета. Как уже
говормлось, некоторые операционные системы генерируют
фрагментированные пакеты с флагом "не фрагментировать", особенно
это характерно для NFS. Такие пакеты будут отброшены, если не
использовать no-df. Поскольку некоторые ОС генерируют "don't
fragment" пакеты с нулевым идентификатором IP в заголовке,
использование no-df лучше сочетать с опцией random-id.
- random-id
Заменяет поле идентификатора в IP пакете на случайное, так как
некоторые операционные системы назначают предсказуемые значения.
Применяется только к исходящим пакетам, которые не фрагментируются
после дополнительной сборки пакета.
- min-ttl num
Устанавливает минимальное значение Time To Live (TTL) в заголовке
пакета.
- max-mss num
Устанавливает Maximum Segment Size (MSS) в заголовке TCP пакета.
- fragment reassemble
Приходящие фрагменты накапливаются и собираются в законченый пакет,
который затем передается на механизм фильтрации. Преимущество -
возможность фильтрующему правилу игнорировать фрагменты.
Недостаток - увеличение расхода памяти. Это опция по умолчанию,
если никакая другая опция работы с фрагментами не установлена.
Также это единственная опция, с которой работает NAT.
- fragment crop
Все дублированные фрагменты будут отброшены и любые превышения -
обрезаны. В отличие от fragment reassemble, фрагменты не
буферизуются и прередаются на механизм фильтрации сразу.
- fragment drop-ovl
Работает подобно fragment crop, за исключением того, что все дубликаты
отбрасываются.
- reassemble tcp
Постоянно нормализует TCP пакеты. При использовании scrub пересобирает
пакет, может быть не определено направление (in/out). Может быть
выполнена следующая нормализация:
* Ни одной из сторон не позволяется уменьшить IP TTL. Это сделано
для того, чтобы защититься от посылок пакетов, запрашивающих
соединение, но исчезающих до достижения адресата. TTL всех пакетов
устанавливается в максимум.
* Модуляция временных меток RFC1323 в заголовке TCP пакета со
случайным числом. Это препятствует определению uptime хоста или
определению количества хостов за маршрутизатором, использующем
NAT.
Пример:
scrub in on fxp0 all fragment reassemble min-ttl 15 max-mss 1400
scrub in on fxp0 all no-df
scrub on fxp0 all reassemble tcp
Именованые (под)наборы правил и якоря
-------------------------------------
* Введение
* Именованные наборы правил
* Опции якоря
* Управление именоваными наборами
Введение
В дополнению к главному набору правил, PF может работать с
дополнительными наборами. С тех пор, как поднаборы могут динамически
изменяться используя pfctl(8), они стали удобным инструментом
изменения основного набора правил. Если таблица используется для
динамического хранения адресов, поднаборы используются для
динамического хранения правил фильтрации и правил nat, rdr, и binat.
Поднабор может быть добавлен в главный набор с помощью якоря. Есть
четыре типа якорных правил:
* anchor name - оценивает все правила фильтра по имени якоря
* binat-anchor name - оценивает правила binat фильтра по имени якоря
* nat-anchor name - оценивает правила nat фильтра по имени якоря
* rdr-anchor name - оценивает правила rdr фильтра по имени якоря
Только главный набор правил может содержать якорные правила.
Именованные наборы правил
Именованый набор - это группа правил фильтрации группа фильтра и/или
правил трансляции, которым было назначено имя. Точка привязки может
содержать больше чем один такой набор. Когда PF обнаруживает якорь в
главном наборе правил, он производит проверку всех поднабов правил,
привязанных к нему, в алфавитном порядке, согласно их именам. Проверка
правил в главном наборе начинается в том случае, когда пакет не
соответствует фильтру, если встречается опция quick или правило
трансляции пакет прекращает прохождение правил и основного и
поднабора.
Например:
ext_if = "fxp0"
block on $ext_if all
pass out on $ext_if all keep state
anchor goodguys
Этот набор правил устанавливает по умолчанию запретительную политику
на интерфейсе fxp0 для всего входящего и исходящего трафика. Stateful
трафик пропускается и создается якорное правило с именем goodguys.
Якоря могут быть связаны с правилами двумя методами: * Используя
загрузку правил * Используя pfctl(8) Загрузка правил указывает pfctl
загрузить правила из текстового файла. Например:
load anchor goodguys:ssh from "/etc/anchor-goodguys-ssh"
Когда будет загружен главный набор, правила, перечисленные в
файле/etc/anchor-goodguys-ssh будут загружены в именованый набор ssh,
приложенный к якорю goodguys.
Для добавления правил к якорю, используя pfctl, можно использовать
следующую конструкцию:
# echo "pass in proto tcp from 192.0.2.3 to any port 22" \
| pfctl -a goodguys:ssh -f -
Это добавит правило pass правила ssh к якорю goodguys. PF проверит эти
правила (а также любые другие правила фильтрации, которые будут
добавлены) когда дойдет до якоря goodguys в главном наборе правил.
Правила также могут быть сохранены и загружены из текстового файла:
# cat >> /etc/anchor-goodguys-www
pass in proto tcp from 192.0.2.3 to any port 80
pass in proto tcp from 192.0.2.4 to any port { 80 443 }
# pfctl -a goodguys:www -f /etc/anchor-goodguys-www
Эта операция загрузит правила из файла /etc/anchor-goodguys-www в
именованый набор правил www якоря goodguys.
Правила фильтрации и трансляции могут быть загружены в именованый
набор используя тотже синтаксис и опции, что и в главном наборе.
Единственным условием будет то, что все используемые макросы должны
быть определены в пределах этого же набора, макросы, определенные в
главном наборе не видны из именованного набора.
Каждый именованный набор существует обособленно от остальных.
Операции, проводимые над ним, такие как сброс правил, не имеют эффекта
над остальными. Кроме того, удаление казателя на якор не приводит к
удалению ни самого якоря ни именованных наборов правил, к нему
привязанных. Именованый набор существует до тех пор, пока все его
правила не будут сброшены, используя pfctl(8). Якорь уничтожается как
только не остается ни одного привязанного к нему набора правил.
Опции якоря
Дополнительно в якорном правиле может быть определен интерфейс,
протокол, адрес источника или назначения, тэг и т.д. Когда такой
дополнительный параметр встречается, то пакет попадает на проверку
только в случае соответсвия указанному условию. Например:
ext_if = "fxp0"
block on $ext_if all
pass out on $ext_if all keep state
anchor ssh in on $ext_if proto tcp from any to any port 22
Правила в якоре ssh будут оцениваться только в случае, если пришел TCP
пакет на 22 порт. Правила к якорю могут добавляться так:
# echo "pass in from 192.0.2.10 to any" | pfctl -a ssh:allowed -f -
В этом случае, когда не определен ни порт, ни протокол, ни интерфейс,
хосту 192.0.2.10 будет разрешена работа только по протоколу ssh.
Управление именоваными наборами
Управление осуществляется с помощью pfctl. Вы можете удалять и
добавлять правила из набора не перезаружая при этом главный набор.
Для примера, выведем список всех правил набора, добавленного к якорю
ssh:
# pfctl -a ssh:allowed -s rules
Сброс всех правил из этого набора:
# pfctl -a ssh:allowed -F rules
Если имя набора пропущено, то действие применяется ко всем наборам
якоря.
Для получения полного списка команд, пожалуйста см. pfctl(8).
Организация очередей и приоритетов
----------------------------------
* Очереди
* Планировщики
* Очереди, базирующиеся на классах
* Приоритетные очереди
* Случайное раннее обнаружение
* Явное уведомление о перегрузке
* Конфигурирование очереди
* Назначение трафика в очередь
* Пример #1: Маленькая домашняя сеть
* Пример #2: Сеть компании Очереди
Как известно, смысл очереди заключается в том, чтобы сохранить данные,
которые еще не прошли обработку. При работе в сети, данные получаемые
хостом поступают в очередь и ждут, когда они будут обработаны
операционной системой, при этом она решает, какие именно пакеты и в
каком порядке обрабатывать. Изменение этого порядка может оказать
влияние на производительность сети. Для примера, представьте себе
пользователя, работающего одновременно с приложениями по протоколам
ssh и ftp. В идеальном случае, пакеты ssh должны обрабатываться в
первую очередь, так как этот протокол очень чувствителен к задержкам.
При нажатии клавиши в ssh-клиенте ожидается немедленный ответ, но
идущая передача по ftp вызывает задержку в несколько секунд. Что может
произойти в случае, когда роутер обрабатывает большое количество ftp
пакетов? Пакеты ssh сохраняются в очереди, а то и просто отбрасываются
в случае малого буфера и в результате ssh сессия может вообще
прерваться. Модифицирование стратегии организации очередей может
позволить распределить пропускную способность между различными
приложениями, пользователями и хостами.
Обратите внимание, что организация очереди имеет смысл только для
исходящих соединений, потому что как только пакет попал на входящий
интерфейс с ним уже поздно что-либо делать, так как полоса пропускания
канала была уже использована. Единственным решением этой проблемы
может стать организация очереди на смежном маршрутизаторе или
позволять организацию очереди на внутреннем интерфейсе, если хост сам
является смежным маршрутизатором.
Планировщики
Планировщиком называется то, что организовывает очередь и определяет
порядок обработки пакетов. По умолчанию в OpenBSD в качестве
планировщика используется First In First Out (FIFO). Принцип его очень
просто - первый вошел - первый вышел. Новоприбывший пакет добавляется
в конец очереди. При превышении максимального размера очереди пакет
отбрасывается. Это явление известно как tail-drop.
OpenBSD поддерживает два планировщика:
* Очереди, базирующиеся на классах (Class Based Queueing)
* Приоритетные очереди (Priority Queueing)
Очереди, базирующиеся на классах
В очередях базирующиеся на классах (CBQ) алгоритм организации очереди
построен на разделении полосы пропускания между различными очередями
или классами. Трафик присоединяется к очереди на основании адреса
источника или отправителя, порта, протокола и т.д. и т.п. Очередь
может быть сконфигурирована на заимствование произвольной полосы
пропускания от родительской очереди, если та занимает свой канал
неполностью. Очереди также могут работать с системой приоритетов,
например, пропуская ssh трафик в первую очередь, по сравнению с
трафиком ftp/
В CBQ очереди размещаются иерархическим способом. В самом верху -
родительская очередь, определяющая общую пропускную способность.
Дочерним очередям назначается некоторая часть от полосы пропускания
родительской очереди. Например, очереди могут быть определены
следующим способом
Root Queue (2Mbps)
Queue A (1Mbps)
Queue B (500Kbps)
Queue C (500Kbps)
В этом случае общая пропускная способность составляет 2 МБ/с и эта
полоса пропускания делится между тремя дочерними очередями.
Иерархия может быть значительно расширена с использованием
вложенности. Для того, чтобы разделить полосу пропускания между
различными пользователями, при этом сделав некоторые виды трафика не
зависящим от полосы пропускания мы можем поступить следующим образом:
Root Queue (2Mbps)
UserA (1Mbps)
ssh (50Kbps)
bulk (950Kbps)
UserB (1Mbps)
audio (250Kbps)
bulk (750Kbps)
http (100Kbps)
other (650Kbps)
Обратите внимание, что на каждом уровне сумма полос пропускания не
может быть больше родительской.
Очередь может быть настроена таким образом, что будет заимствовать
полосу пропускания у родителя в случае неиспользования ее другими
очередями. Рассмотрим такую организацию очереди:
Root Queue (2Mbps)
UserA (1Mbps)
ssh (100Kbps)
ftp (900Kbps, borrow)
UserB (1Mbps)
Если трафик в ftp очереди превышает 900Kbps, и трафик в очереди UserA
- меньше чем 1Mbps (потому что ssh очередь использует меньше чем
100Kbps), ftp очередь заимствует дополнительную полосу пропускания от
UserA. Таким образом ftp очередь способна использовать больше чем ей
назначенные 900Kbps, но в случае увеличения очереди ssh заимствованая
полоса освобождается.
CBQ назначает каждой группе свой приоритетный уровень. В моменты
перегрузки предпочтение отдается очередям с более высоким приоритетом
в случае наличия у них одного родителя. При одинаковых приоритетах
очереди обслуживаются циклически. Для примера:
Root Queue (2Mbps)
UserA (1Mbps, priority 1)
ssh (100Kbps, priority 5)
ftp (900Kbps, priority 3)
UserB (1Mbps, priority 1)
CBQ будет обрабатывать очереди UserA и UserB циклическим способом, так
как их приоритеты равны. В ходе обработки очереди UserA также
обрабатываются ее дочерние очереди, где ssh имеет более высокий
приоритет и будет обрабатываться в первую очередь в случае перегрузки
сети. Обратите внимание, что очереди ssh и ftp е имеют приоритета по
сравнению с очередями UserA и UserB так как они находятся на более
низком уровне.
Для получения более детальной информации по CBQ обратитесь к
References on CBQ.
Приоритетные очереди
Приоритетные очереди (PRIQ) создаются на сетевом интерфейсе, причем
структура очередей является плоской - нельзя создавать дочерние
очереди. Сперва определяется корневая очередь с указанием общей
пропускной способности, а за ней все дочерние очереди. Рассмотрим
такой пример:
Root Queue (2Mbps)
Queue A (priority 1)
Queue B (priority 2)
Queue C (priority 3)
Корневая очередь определяется с пропускной спосоностью 2Mbps, следом
идут дочерние.
При использовании PRIQ Вы должны очень тщательно планировать очереди,
так как очереди обрабатываются строго по приоритетам и при полном
занятии канала трафиком с высоким приоритетом пакеты низжих
приоритетов будут отбрасываться.
Случайное раннее обнаружение
Случайное раннее обнаружение (RED) - это алгоритм определения
перегрузки канала. Его целью является предотвращение переполнения
очереди. Делается это путем непрерывного сравнения текущей длины
очереди к минимальному и максимальному порогам. Если минимальный порог
не достигнут - все пакеты пропускаются. Если достигнут максимальные
порог - все пакеты отбрасываются. В промежутке пакеты отбрасываются с
определенной вероятностью, зависящей от размера очереди. Чем ближе к
максимальному порогу - тем выше вероятность. Пакеты для отбрасывания
выбираются случайным образом из разных сессий. Чем большая полоса
пропускания занимается сессией, тем более выше вероятность сброса из
нее пакета.
RED весьма полезен, так как позволяет избежать ситуации, называемой
"глобальной синхронизацией", она проявляется в том, что связь
полностью прекращается из-за одновременно отбрасываемых пакетов с
разных сессий. Например, если перегрузка происходит на маршрутизаторе,
обслуживающем 10 одновременных сессий ftp и будут отброшены пакеты от
большинства или всех сессий, общая пропускная способность резко
понизится. RED позволяет избежать этого, выбирая сессии из которых
терять пакеты случайным образом. Поскольку сессии занимающие больше
полосы пропускания имеют больший шанс на потерю пакета, то возможность
возникновения перегрузки изчезнет и больших потерь трафика не
произойдет. Кроме того, RED позволяет обработать взрывной всплеск
трафика, так как начинает отбрасывать пакеты до переполнения очереди.
RED должен использоваться только тогда, когда транспортный протокол
способен реагировать на индикаторы перегрузки сети. В большинстве
случаев это означает, что RED должен использоваться только к очередям
TCP, а не к очередям UDP или ICMP.
Для получения дополнительной информации обратитесь к References on
RED.
Явное уведомление о перегрузке
Явное уведомление о перегрузке (ECN) работает совместно с RED и
применяется для уведомления двух связанных хостов о перегрузке сети.
Делается это разрешением RED установить флаг в заголовке пакета,
вместо его отброса. Если удаленный хост поддерживает ECN и читает этот
флаг, то он начинает снижать исходящий трафик.
Для получения более подробной информации обратитесь к RFC 3168.
Конфигурирование очереди
Начиная с OpenBSD 3.0 Alternate Queueing (ALTQ) стал частью основной
системы, а с версии OpenBSD 3.3 ALTQ был интегрирован в PF. Реализация
ALTQ в OpenBSD поддерживает планировщики Class Based Queueing (CBQ)
and Priority Queueing (PRIQ) и Random Early Detection (RED) вместе с
Explicit Congestion Notification (ECN)..
Поскольку ALTQ интегрирован в PF, то PF должен быть включен для
огранизации работы очередей. Руководство, как это сделать, можно найти
в разделе документации "Getting Started".
Очереди конфигурируются в файле pf.conf. Есть два типа директив,
которые используются для конфигурации очередей.
* altq on - определяет интерфейс, на котором будет организована
очередь, планировщик и создает корневую очередь.
* queue - определяет свойства дочерних очередей
Синтаксис кдирективы altq следующий:
altq on interface scheduler bandwidth bw qlimit qlim \
tbrsize size queue { queue_list }
* interface - сетевой интерфейс, на котором активируется очередь.
* scheduler - какой планировщик будет использован. Доступные
значения cbq и priq. На интерфейсе в один момент времени можно
установить только один планировщик.
* bw - общая пропускная способность, доступная планировщику. Может
содержать аббревиатуры b, Kb, Mb, Gb для обозначения bits,
kilobits, megabits, и gigabits в секунду, конкретное значение или
процент от общей пропускной способности.
* qlim - максимальное число пакетов в очереди. Необязательный
параметр. По умолчанию - 50
* size - размер token bucket regulator в байтах. Если не определен,
то устанавливается на основе ширины полосы пропускания.
* queue_list - список дочерних очередей, открываемых из под
родительской очереди.
Например:
altq on fxp0 cbq bandwidth 2Mb queue { std, ssh, ftp }
Эта команда запускает CBQ на интерфейсе fxp0, пропускная способность
канала 2Mb и создаются три дочерние очереди std, ssh, ftp.
Синтаксис директивы queue следующий:
queue name [on interface] bandwidth bw [priority pri] [qlimit qlim] \
scheduler ( sched_options ) { queue_list }
* name - имя очереди. Эта запись должна быть идентична определенной
в директиве altq опцией queue_list. Для cbq это также может быть
запись имени очереди в предыдущей директиве queue параметре
queue_list. Имя не должно быть длиннее 15 символов.
* interface - сетевой интерфейс, на котором запущена очередь. Это
значение опциональное и если не определено, то очередь будет
работать применительно ко всем интерфейсам.
* bw - общая пропускная способность, доступная планировщику. Может
содержать аббревиатуры b, Kb, Mb, Gb для обозначения bits,
kilobits, megabits, и gigabits в секунду, конкретное значение или
процент от общей пропускной способности. Указывается только при
использовании планировщика cbq.
* pri - приоритет очереди. Для cbq приоритет изменяется от 0 до 7,
для priq диапазон от 0 до 15. Приоритет 0 считается самым низким.
Если этот параметр не определен, ему назначается 1.
* qlim - максимальное число пакетов в очереди. Необязательный
параметр. По умолчанию - 50
* scheduler - используемый планировщик - cbq или priq. Должен быть
таким же, как и родительская очередь.
* sched_options - дополнительные опции для управления планировщиком:
* default - определить очередью по умолчанию, куда будут включаться
все пакеты не подходящие под остальные очереди. Может быть только
одна.
* red - включить Random Early Detection (RED) для этой очереди.
* rio - включить RED с IN/OUT. В этом режиме RED поддерживает
очереди различной длины и различные пороговые значения, по одному
на каждый уровень IP Quality of Service.
* ecn - включить Explicit Congestion Notification (ECN) для этой
очереди. Ecn работает совместно с red.
* borrow - эта очередь может заимствовать пропускную способность у
других очереде. Может быть определено только при использовании
cbq.
queue_list - список дочерних очередей. Может быть определено только
при использовании cbq. Продолжение примера выше:
queue std bandwidth 50% cbq(default)
queue ssh { ssh_login, ssh_bulk }
queue ssh_login priority 4 cbq(ecn)
queue ssh_bulk cbq(ecn)
queue ftp bandwidth 500Kb priority 3 cbq(borrow red)
Здесь определяются дочерние очереди. Очереди std назначается 50%
пропускной способности от материнской очереди и она назначается
дефолтной. Очередь ssh определяет две дочерних очереди, ssh_login и
ssh_bulk. Ssh_login дают более высокий приоритет чем ssh_bulk, и обе
работают с ECN. Ftp назначена полоса пропускания в 500Kbps и дан
приоритет 3. Эта очередь может арендовать свободную пропускную
способность других очередей и используется red.
Назначение трафика в очередь
Для направления трафика в очередь используется ключевое слово queue в
правилах PF. Для примера рассмотрим следующую строку:
pass out on fxp0 from any to any port 22
Пакеты из этого правила могут быть направлены в очередь таким
способом:
pass out on fxp0 from any to any port 22 queue ssh
Когда используется ключевое слово queue совместно с директивой block,
все пакеты TCP RST или ICMP Unreachable ставятся в указанную очередь.
Обратите внимание, что queue может приключиться для другого
интерфейся, чем было определено директивой altq:
altq on fxp0 cbq bandwidth 2Mb queue { std, ftp }
queue std cbq(default)
queue ftp bandwidth 1.5Mb
pass in on dc0 from any to any port 21 queue ftp
Очередь определяется на fxp0, но указание на нее встречается на dc0.
Если пакеты, соответствующие правилу выходит с интерфейса fxp0, то он
будет поставлен в очередь ftp. Этот тип очередей может быть очень
полезен на маршрутизаторах.
Обычно с ключевым словом queue используется только одно имя очереди,
но если определено и второе имя, то очередь будет использоваться для
пакетов с Type of Service(ToS) низкой задержки и для пакетов TCP ACK
без полезного груза данных. Хороший пример может быть найден при
использовании ssh: во время открытия сессии ToS устанавливается в
low-delay пока не откроется сессия SCP или SFTP. PF может использовать
информацию о находящихся в очереди пакетах для того. чтобы отличить
пакеты на ввод логина от остальных пакетов. Возможно будет полезным
разнести по приоритетам пакеты авторизации от пакетов данных:
pass out on fxp0 from any to any port 22 queue(ssh_bulk, ssh_login)
Это правило позволяет связать пакеты авторизации ssh с очередью
ssh_login, а пакеты SCP и SFTP с очередью ssh_bulk, при этом ssh_login
имеет приоритет выше и пакеты авторизации снуют во все стороны
значительно швыдче.
Повышение приоритета пакетов TCP ACK имеет смысл на асинхронных
соединениях, таких как ADSL, кде скорость входящего и исходящего
потока не равны между собой. На ADSL линии при польностью занятом
исходящем канале будет снижаться и полезное использование входящего
канала, так как пакеты TCP ACK будут теряться и задерживаться.
Тестирования показали, что для достижения наибольшей эффективности,
полоса пропускания должна быть немного меньше, чем способно
подключение. Например, если ADSL линия даем тмксимальную скорость в
640Kbps, установите значение пропускной способности для корневой линии
в 600Kb. Оптимальное значение находится путем проб и ошибок.
Когда ключевое слово queue используется с правилами keep state:
pass in on fxp0 proto tcp from any to any port 22 flags S/SA \
keep state queue ssh
PF будет делать запись очереди в таблице состояний таким образом, что
пакеты с fxp0 соответствующие образовавшемуся соединению будут
оказываться в очерези ssh.
Обратите внимание, что ключевое слово queue применяется к правилам,
обслуживающим входящий трафик.
Пример #1: Маленькая домашняя сеть
[ Alice ] [ Charlie ]
| | ADSL
---+-----+-------+------ dc0 [ OpenBSD ] fxp0 -------- ( Internet )
|
[ Bob ]
В этом примере OpenBSD используется как шлюз в Интернет для маленькой
домашней сети с тремя рабочими станциями. На шлюзе работает NAT и
фильтрация пакетов. Выход в Интернет осуществляется по ADSL с входящей
скоростью 2Mbps и исходящей 640Kbps.
Для очередей действуют следующие правила:
* Выделяем 80Kbps для игрулек Боба, чтобы не стонал, когда Алиса и
Чарли качают фильмы и музыку. Когда есть свободная полоса - пусть
берет.
* SSH трафик и трафик интернет - пейджеров имеет высший приоритет.
* DNS запросы-ответы имеют приоритет чуть ниже.
* Исходящие TCP ACK имеют высший приоритет по сравнению со всем
остальным исходящим траффиком.
Ниже представлены правила, реализующие эту политику. Обратите
внимание, что в pf.conf не представлены правила nat, rdr, options, и
т.д. непосредственно не имеющие отношения.
# enable queueing on the external interface to control traffic going to
# the Internet. use the priq scheduler to control only priorities. set
# the bandwidth to 610Kbps to get the best performance out of the TCP
# ACK queue.
altq on fxp0 priq bandwidth 610Kb queue { std_out, ssh_im_out, dns_out, \
tcp_ack_out }
# define the parameters for the child queues.
# std_out - the standard queue. any filter rule below that does not
# explicitly specify a queue will have its traffic added
# to this queue.
# ssh_im_out - interactive SSH and various instant message traffic.
# dns_out - DNS queries.
# tcp_ack_out - TCP ACK packets with no data payload.
queue std_out priq(default)
queue ssh_im_out priority 4 priq(red)
queue dns_out priority 5
queue tcp_ack_out priority 6
# enable queueing on the internal interface to control traffic coming in
# from the Internet. use the cbq scheduler to control bandwidth. max
# bandwidth is 2Mbps.
altq on dc0 cbq bandwidth 2Mb queue { std_in, ssh_im_in, dns_in, bob_in }
# define the parameters for the child queues.
# std_in - the standard queue. any filter rule below that does not
# explicitly specify a queue will have its traffic added
# to this queue.
# ssh_im_in - interactive SSH and various instant message traffic.
# dns_in - DNS replies.
# bob_in - bandwidth reserved for Bob's workstation. allow him to
# borrow.
queue std_in cbq(default)
queue ssh_im_in priority 4
queue dns_in priority 5
queue bob_in bandwidth 80Kb cbq(borrow)
# ... in the filtering section of pf.conf ...
alice = "192.168.0.2"
bob = "192.168.0.3"
charlie = "192.168.0.4"
local_net = "192.168.0.0/24"
ssh_ports = "{ 22 2022 }"
im_ports = "{ 1863 5190 5222 }"
# filter rules for fxp0 inbound
block in on fxp0 all
# filter rules for fxp0 outbound
block out on fxp0 all
pass out on fxp0 inet proto tcp from (fxp0) to any flags S/SA \
keep state queue(std_out, tcp_ack_out)
pass out on fxp0 inet proto { udp icmp } from (fxp0) to any keep state
pass out on fxp0 inet proto { tcp udp } from (fxp0) to any port domain \
keep state queue dns_out
pass out on fxp0 inet proto tcp from (fxp0) to any port $ssh_ports \
flags S/SA keep state queue(std_out, ssh_im_out)
pass out on fxp0 inet proto tcp from (fxp0) to any port $im_ports \
flags S/SA keep state queue(ssh_im_out, tcp_ack_out)
# filter rules for dc0 inbound
block in on dc0 all
pass in on dc0 from $local_net
# filter rules for dc0 outbound
block out on dc0 all
pass out on dc0 from any to $local_net
pass out on dc0 proto { tcp udp } from any port domain to $local_net \
queue dns_in
pass out on dc0 proto tcp from any port $ssh_ports to $local_net \
queue(std_in, ssh_im_in)
pass out on dc0 proto tcp from any port $im_ports to $local_net \
queue ssh_im_in
pass out on dc0 from any to $bob queue bob_in
Пример #2: Сеть компании
( IT Dept ) [ Boss's PC ]
| | T1
--+----+-----+---------- dc0 [ OpenBSD ] fxp0 -------- ( Internet )
| fxp1
[ COMP1 ] [ WWW ] /
| /
--+----------'
В этом примере OpenBSD выступает в роли системы сетевой защиты для
сети компании. В компании работает WWW сервер, установленный в DMZ.
Клиенты обновляют свои сайты через FTP. У IT одела имеется собственная
подсеть, соединенная с главной, босс использует свой компьютер для
почты и серфинга по сети. Соединение с Интернетом осуществляется на
скорости T1 (1.5Mbps) в обе стороны. Все прочие сетевые сегменты
используют Fast Ethernet (100Mbps).
Сетевой администратор выбрал следующую политику:
* Трафик между WWW сервером и Интернетом ограничивается 500Kbps на
одно соединение.
* Между WWW сервером и внутренней сетью ограничения по скорости нет.
* Трафик HTTP между WWW сервером и Интернет имеет высший приоритет
по сравнению со всем остальным трафиком от WWW в Internet (таким,
как доступ по FTP).
* 500Kbps резервируется для IT для того, чтобы качать последние
версии программного обеспечения. Если есть лишнее - забираем.
* Трафик между начальником и Интернетом имеет высший приоритет по
сравнению со сравнению со всем.
Ниже представлены правила, реализующие эту политику. Обратите
внимание, что в pf.conf не представлены правила nat, rdr, options, и
т.д. непосредственно не имеющие отношения.
# enable queueing on the external interface to queue packets going out
# to the Internet. use the cbq scheduler so that the bandwidth use of
# each queue can be controlled. the max outgoing bandwidth is 1.5Mbps.
altq on fxp0 cbq bandwidth 1.5Mb queue { std_ext, www_ext, boss_ext }
# define the parameters for the child queues.
# std_ext - the standard queue. also the default queue for
# outgoing traffic on fxp0.
# www_ext - container queue for WWW server queues. limit to
# 500Kbps.
# www_ext_http - http traffic from the WWW server
# www_ext_misc - all non-http traffic from the WWW server
# boss_ext - traffic coming from the boss's computer
queue std_ext cbq(default)
queue www_ext bandwidth 500Kb { www_ext_http, www_ext_misc }
queue www_ext_http priority 3 cbq(red)
queue www_ext_misc priority 1
queue boss_ext priority 3
# enable queueing on the internal interface to control traffic coming
# from the Internet or the DMZ. use the cbq scheduler to control the
# bandwidth of each queue. bandwidth on this interface is set to the
# maximum. traffic coming from the DMZ will be able to use all of this
# bandwidth while traffic coming from the Internet will be limited to
# 1.0Mbps (because 0.5Mbps (500Kbps) is being allocated to fxp1).
altq on dc0 cbq bandwidth 100% queue { net_int, www_int }
# define the parameters for the child queues.
# net_int - container queue for traffic from the Internet. bandwidth
# is 1.0Mbps.
# std_int - the standard queue. also the default queue for outgoing
# traffic on dc0.
# it_int - traffic to the IT Dept network.
# boss_int - traffic to the boss's PC.
# www_int - traffic from the WWW server in the DMZ.
queue net_int bandwidth 1.0Mb { std_int, it_int, boss_int }
queue std_int cbq(default)
queue it_int bandwidth 500Kb cbq(borrow)
queue boss_int priority 3
queue www_int cbq(red)
# enable queueing on the DMZ interface to control traffic destined for
# the WWW server. cbq will be used on this interface since detailed
# control of bandwidth is necessary. bandwidth on this interface is set
# to the maximum. traffic from the internal network will be able to use
# all of this bandwidth while traffic from the Internet will be limited
# to 500Kbps.
altq on fxp1 cbq bandwidth 100% queue { internal_dmz, net_dmz }
# define the parameters for the child queues.
# internal_dmz - traffic from the internal network.
# net_dmz - container queue for traffic from the Internet.
# net_dmz_http - http traffic.
# net_dmz_misc - all non-http traffic. this is also the default queue.
queue internal_dmz # no special settings needed
queue net_dmz bandwidth 500Kb { net_dmz_http, net_dmz_misc }
queue net_dmz_http priority 3 cbq(red)
queue net_dmz_misc priority 1 cbq(default)
# ... in the filtering section of pf.conf ...
main_net = "192.168.0.0/24"
it_net = "192.168.1.0/24"
int_nets = "{ 192.168.0.0/24, 192.168.1.0/24 }"
dmz_net = "10.0.0.0/24"
boss = "192.168.0.200"
wwwserv = "10.0.0.100"
# default deny
block on { fxp0, fxp1, dc0 } all
# filter rules for fxp0 inbound
pass in on fxp0 proto tcp from any to $wwwserv port { 21, \
> 49151 } flags S/SA keep state queue www_ext_misc
pass in on fxp0 proto tcp from any to $wwwserv port 80 \
flags S/SA keep state queue www_ext_http
# filter rules for fxp0 outbound
pass out on fxp0 from $int_nets to any keep state
pass out on fxp0 from $boss to any keep state queue boss_ext
# filter rules for dc0 inbound
pass in on dc0 from $int_nets to any keep state
pass in on dc0 from $it_net to any queue it_int
pass in on dc0 from $boss to any queue boss_int
pass in on dc0 proto tcp from $int_nets to $wwwserv port { 21, 80, \
> 49151 } flags S/SA keep state queue www_int
# filter rules for dc0 outbound
pass out on dc0 from dc0 to $int_nets
# filter rules for fxp1 inbound
pass in on fxp1 proto { tcp, udp } from $wwwserv to any port 53 \
keep state
# filter rules for fxp1 outbound
pass out on fxp1 proto tcp from any to $wwwserv port { 21, \
> 49151 } flags S/SA keep state queue net_dmz_misc
pass out on fxp1 proto tcp from any to $wwwserv port 80 \
flags S/SA keep state queue net_dmz_http
pass out on fxp1 proto tcp from $int_nets to $wwwserv port { 80, \
21, > 49151 } flags S/SA keep state queue internal_dmz
Адресные пулы и балансировка нагрузки
-------------------------------------
* Введение
* Адресные пулы NAT
* Балансировка нагрузки входящих подключений
* Балансировка нагрузки исходящего трафика
* Пример набора правил
Введение
Адресным пулом называется адресное пространство больше двух адресов
используемое группой пользователей. Адресный пул может быть указан в
правилах перенаправления, трансляции и указазан как адрес назначения в
опциях фильтрации route-to, reply-to и dup-to.
Есть четыре метода использования пулов адресов:
* bitmask - назначает адрес из пула согласно исходному сетевому
адресу (адрес источника для правил nat, адрес назначения для
правил rdr). Пример: если пул адресов включает 192.0.2.1/24 и
можифицируемый адрес 10.0.0.50, то результирующим адресом будет
192.0.2.50. Если пул адресов включает 192.0.2.1/25 и
модифицируемый адрес 10.0.0.130, результатом будет 192.0.2.2.
* random - случайный выбор адресов из пула.
* source-hash - использование хэша адреса источника для
распределения адресов из пула. Этот метод гаранитирует
соответствие адреса источника и адреса, взятого из пула. Ключ для
алгоритма хеширования может быть определен дополнительно после
ключевого слова source-hash в шестнадцетиричном формате или как
строка. По умолчанию, pfctl(8) генерирует случайный ключ при
каждой загрузке набора правил.
* round-robin - изпользование адресов пула по кругу. Это метод по
умолчанию и единственный метод, когда пул адресов определен из
таблицы.
За исключением метода round-robin, пул адресов должен быть опрелелен
как блок адресов CIDR (Classless Inter-Domain Routing). В методе
round-robin используется назначение адресов из таблицы.
Опция sticky-address может использоваться с пулами random и
round-robin для гарантии назаначения всегда одного и того же адреса
источника в адрес пула.
Адресные пулы NAT
Здесь пул адресов используется для трансляции адресов в правилах nat.
Соединение, которое имеет адрес источника транслируется в адрес пула,
при этом базируясь на определенном методе. Это может оказаться очень
полезным в случае. когда PF транслирует адреса для очень большой сети.
Так как число одновременных NAT соединений на один внешний адрес
ограниченно, выделение для этих целей пула адресов позволит
значительно увеличить число пользователей.
В этом примере пул из двух адресов используется для трансляции
исходящих пакетов. Для каждого исходящего соединения PF производит
ротацию адресов методом round-robin:
nat on $ext_if inet from any to any -> { 192.0.2.5, 192.0.2.10 }
Единственным недостатком этого метода будет то, что не всегда будет
соблюдатся соответствие между исходным адресом и адресом трансляции.
Это может вызвать проблему при заходе на web - узлы, проверяющих
валидность пользователя на основании IP адреса. Решением этой проблемы
может стать использование метода source-hash для привязки внутреннего
адреса к адресу трансляции. В этом случае адресный пул должен быть
определен как сетевой блок CIDR:
nat on $ext_if inet from any to any -> 192.0.2.4/31 source-hash
В этом правиле nat используется пул адресов 192.0.2.4/31 (192.0.2.4 -
192.0.2.5) как адреса трансляции для исходящих пакетов. Каждый
внутренний адрес будет всегда транслироваться в свой внешний адрес,
так как указано ключевое слово source-hash.
Балансировка нагрузки входящих подключений
Пулы адресов также могут использоваться для балансировки нагрузки
входящих подключений. Для примера, входящие подключения на web-сервер
могут быть распределены между серверной фермой:
web_servers = "{ 10.0.0.10, 10.0.0.11, 10.0.0.13 }"
rdr on $ext_if proto tcp from any to any port 80 -> $web_servers \
round-robin sticky-address
Все соединения циклически будут перенаправляться на серверы фермы
используя метод round-robin. Это "sticky connection" будет
существовать до тех пор, пока существуют постоянные соединения к этому
адресу. Следующий пользователь будет подключен иже с следующему
адресу.
Балансировка нагрузки исходящего трафика
Пулы адресов могут использоваться для балансировки нагрузки между
двумя и более внешними каналами с использованием опции route-to в
случае невозможности организовать динамическую маршрутизацию
(например, с использованием протокола BGP4). Совместное использование
route-to и пула адресов round-robin исходящие соединения могут быть
распределены между разными провайдерами.
В качестве дополнительной информации необходимо указать адреса
маршрутизаторов для каждого Интернет-соединения. Это необходимо для
опции route-to, дабы управлять исходящими пакетами.
Этот пример покажет нам балансировку нагрузки между двумя каналами:
lan_net = "192.168.0.0/24"
int_if = "dc0"
ext_if1 = "fxp0"
ext_if2 = "fxp1"
ext_gw1 = "68.146.224.1"
ext_gw2 = "142.59.76.1"
pass in on $int_if route-to \
{ ($ext_if1 $ext_gw1), ($ext_if2 $ext_gw2) } round-robin \
from $lan_net to any keep state
Опция route-to используется для приема трафика на внутреннем
интерфейсе и назначения ему внешнего сетевого интерфейса и шлюза,
таким образом обеспечивая балансировку. Обратите внимание, что опция
route-to должна быть указана в каждом правиле, предназначенном для
балансировки трафика. Ответные пакеты приходят на тот интерфейс, с
которого ушел запрос и они будут перенаправлены во внутрь как обычно.
Для гарантии того, что пакеты с $ext_if1 всегда направляются к
$ext_gw1 (и соответственно для $ext_if2 к $ext_gw2), в правилах можно
указать следующее:
pass out on $ext_if1 route-to ($ext_if2 $ext_gw2) from $ext_if2 \
to any
pass out on $ext_if2 route-to ($ext_if1 $ext_gw1) from $ext_if1 \
to any
В заключение хочу сказать, что NAT можно использовать на каждом из
внешних интерфейсов:
nat on $ext_if1 from $lan_net to any -> ($ext_if1)
nat on $ext_if2 from $lan_net to any -> ($ext_if2)
Полный пример балансировки исходящего трафика будет выглядеть так:
lan_net = "192.168.0.0/24"
int_if = "dc0"
ext_if1 = "fxp0"
ext_if2 = "fxp1"
ext_gw1 = "68.146.224.1"
ext_gw2 = "142.59.76.1"
# nat outgoing connections on each internet interface
nat on $ext_if1 from $lan_net to any -> ($ext_if1)
nat on $ext_if2 from $lan_net to any -> ($ext_if2)
# default deny
block in from any to any
block out from any to any
# pass all outgoing packets on internal interface
pass out on $int_if from any to $lan_net
# pass in quick any packets destined for the gateway itself
pass in quick on $int_if from $lan_net to $int_if
# load balance outgoing tcp traffic from internal network.
pass in on $int_if route-to \
{ ($ext_if1 $ext_gw1), ($ext_if2 $ext_gw2) } round-robin \
proto tcp from $lan_net to any flags S/SA modulate state
# load balance outgoing udp and icmp traffic from internal network
pass in on $int_if route-to \
{ ($ext_if1 $ext_gw1), ($ext_if2 $ext_gw2) } round-robin \
proto { udp, icmp } from $lan_net to any keep state
# general "pass out" rules for external interfaces
pass out on $ext_if1 proto tcp from any to any flags S/SA modulate state
pass out on $ext_if1 proto { udp, icmp } from any to any keep state
pass out on $ext_if2 proto tcp from any to any flags S/SA modulate state
pass out on $ext_if2 proto { udp, icmp } from any to any keep state
# route packets from any IPs on $ext_if1 to $ext_gw1 and the same for
# $ext_if2 and $ext_gw2
pass out on $ext_if1 route-to ($ext_if2 $ext_gw2) from $ext_if2 to any
pass out on $ext_if2 route-to ($ext_if1 $ext_gw1) from $ext_if1 to any
Тэгирование пакетов
-------------------
* Введение
* Назначение тэгов пакетам
* Проверка тэгов
* Политика фильтрации
* Тэггинг фреймов Ethernet
Введение
Тэггинг пакетов - способ пометить пакет внутренним идентификатором для
дальнейшего использования в качестве критерия в правилах трансляции и
фильтрации. С тэгированием становится возможным создание "доверия"
между интерфейсами и определение, был ли пакет обработан правилами
трансляции. Также становится возможным переход от фильтрации,
основанной на правилах, к фильтрации, основанной на политиках.
Назначение тэгов пакетам
Тэг назначается пакету с использованием ключевого слова tag:
pass in on $int_if all tag INTERNAL_NET keep state
Тэг INTERNAL_NET будет назначен любому пакету, проходящему через это
правило. Обратите внимание при использовании keep state; тегирование
пакетов должно происходить в правилах pass.
Использование тэгов также допустимо и в макросах:
name = "INTERNAL_NET"
pass in on $int_if all tag $name keep state
Также имеется набор предопределенных макросов, которые могут быть
использованы:
* $if - интерфейс
* $srcaddr - IP адрес источника
* $dstaddr - IP адрес назначения
* $srcport - порт источника
* $dstport - порт назначения
* $proto - The protocol
* $nr - номер правила
Эти макросы определяются во время загрузки, а не во время выполнения.
Тегирование подчиняется следующим правилам:
* Тэг "приклеивается". Только один тэг может быть прикреплен к
пакету и он не может быть удален. Хотя есть возможность изменить
его другим тэгом.
* См. правило выше, пакет может содержать тэг, даже если последнее
правило для пакета не содержало ключевое слово tag.
* Одновременно тэгу назначается тлолько один тэг.
* Тэг - это внутренний идентификатор. Он не может быть послан во
внешний мир.
Возьмем следующий набор правил для примера:
(1) pass in on $int_if tag INT_NET keep state
(2) pass in quick on $int_if proto tcp to port 80 tag \
INT_NET_HTTP keep state
(3) pass in quick on $int_if from 192.168.1.5 keep state
* Пакет поступает на интерфейс $int_if и ему назначается тэг INT_NET
в правиле #1.
* TCP пакет направляется с $int_if на порт 80 кудато-там в Интернет
и будет одарен тэгом INT_NET по правилу #1. Но правилом #2 ему
будет назначен тэг INT_NET_HTTP.
* Пакет, поступивший с $int_if от адреса 192.168.1.5 будет пропущен
правилом #3, так как это последнее соответствующее правило.
Однако, пакеты TCP port 80 будут отмечены тэгом INT_NET_HTTP, а
все остальные - тэгом INT_NET.
В дополнение к применению тэгов в правилах фильтрации, nat, rdr и
binat есть возможность выполнять проверку уже установленных тэгов.
Проверка тэгов
Для проверки уже установленных тэгов используется ключевое слово
tagged:
pass out on $ext_if tagged INT_NET keep state
Пакеты, уходящие на $ext_if должны быть помечены тэгом INT_NET, чтобы
соответствовать правилу, приведенному выше. Существует возможность
инверсного использования правила с помощью !:
pass out on $ext_if tagged ! WIFI_NET keep state
Политика фильтрации
Фильтрация пакетов на основе политик несколько отличается от
вильтрации на основе правил. В политиках устанавливаются правила, по
которым определенный вид трафика должен быть пропущен, а определенный
- запрещен. Пакеты классифицируются внутри политик на основе
традиционных критериев - IP адрес источника/назначения, протокола и
т.д.
Для примера, рассмотрим следующую политику:
* Трафик из внутренней сети в DMZ пропускается (LAN_DMZ)
* Трафик из Интернет к серверу в DMZ пропускается (INET_DMZ)
* Трафик из Интернет, переадресованный на spamd(8) пропускается
(SPAMD)
* Весь прочий трафик - блокируется
Обратите внимание, что политика охватывает весь трафик, который будет
проходить через систему сетевой защиты. В круглых скобках указан
назначаемый для данного пункта политики тэг.
Теперь необходимо написать правила фильтрации и трансляции,
реализующие данную политику:
rdr on $ext_if proto tcp from to port smtp \
tag SPAMD -> 127.0.0.1 port 8025
block all
pass in on $int_if from $int_net tag LAN_INET keep state
pass in on $int_if from $int_net to $dmz_net tag LAN_DMZ keep state
pass in on $ext_if proto tcp to $www_server port 80 tag INET_DMZ keep state
Теперь правила, определяющие политику установлены.
pass in quick on $ext_if tagged SPAMD keep state
pass out quick on $ext_if tagged LAN_INET keep state
pass out quick on $dmz_if tagged LAN_DMZ keep state
pass out quick on $dmz_if tagged INET_DMZ keep state
Теперь, когда правила политики определены, необходимо определить и
модифицировать классифицирующие правила. Для примера, если в DMZ
добавляется сервер POP3/SMTP, необходимо добавить классифицирующее
правило для POP3 и SMTP трафика, подобно нижеприведенному:
mail_server = "192.168.0.10"
...
pass in on $ext_if proto tcp to $mail_server port { smtp, pop3 } \
tag INET_DMZ keep state
Почтовый трафик теперь будет пропускаться, как соответствующий
политике INET_DMZ.
Полный набор теперь будет выглядеть так:
# macros
int_if = "dc0"
dmz_if = "dc1"
ext_if = "ep0"
int_net = "10.0.0.0/24"
dmz_net = "192.168.0.0/24"
www_server = "192.168.0.5"
mail_server = "192.168.0.10"
table persist file "/etc/spammers"
# classification -- classify packets based on the defined firewall
# policy.
rdr on $ext_if proto tcp from to port smtp \
tag SPAMD -> 127.0.0.1 port 8025
block all
pass in on $int_if from $int_net tag LAN_INET keep state
pass in on $int_if from $int_net to $dmz_net tag LAN_DMZ keep state
pass in on $ext_if proto tcp to $www_server port 80 tag INET_DMZ keep state
pass in on $ext_if proto tcp to $mail_server port { smtp, pop3 } \
tag INET_DMZ keep state
# policy enforcement -- pass/block based on the defined firewall policy.
pass in quick on $ext_if tagged SPAMD keep state
pass out quick on $ext_if tagged LAN_INET keep state
pass out quick on $dmz_if tagged LAN_DMZ keep state
pass out quick on $dmz_if tagged INET_DMZ keep state
Тэггинг фреймов Ethernet
Тэгирование может быть выполнено на уровне Ethernet, если машина,
осуществляющая тэггинг/фильтрацию, работает в режиме bridge(4). Для
создания bridge(4) правил фильтрации используется ключевое слово tag.
В PF существует возможность выполнять фильтрацию базируясь на mac
адресе источника/назначения. Bridge(4) правила могут быть созданы с
использованием команды brconfig(8):
# brconfig bridge0 rule pass in on fxp0 src 0:de:ad:be:ef:0 \
tag USER1
И затем в pf.conf указать:
pass in on fxp0 tagged USER1
Термины, используемые переводчиком.
При нахождении неточностей, ошибок или неправильном толковании
терминов прошу отметить это в форуме на http://dreamcatcher.ru
Таблица состояний - таблица, образованная правилами Keep State и
хранящая информацию о соединениях, образованных с помощью этого
правила.
скинут/отброшен - dropped/rejected
Поднабор - Sub ruleset
Якорь - anchor
У меня тоже такое впечатление :)
* 10.1.4.55 - не соответствует таблице и будет блокирован
- я тут или чего-то не понимаю, или у меня извращённые понятия о инвертировании :) Разве это не попадает под правило "!172.16.1.0/24"?? Если нет, то правило вида "!127.0.0.1" никем не удовлетворяется - 127.0.0.1 попадает под правило, но стоит знак !
и пакет будет блокирован, а все остальные не попадают под правило, потому что не являются 127.0.0.1, и тоже будут блокированы??
В /etc/pf.conf
#интерфейс смотрящий во внешнюю сеть например rl1
ext_if_1="rl1"
##интерфейс смотрящий во внутренюю сеть например rl0
int_if="rl0"
table <users> persist file "/etc/users"
nat on $ext_if_1 inet from <users> to any -> $ext_if_1
pass on $ext_if_1 all
pass on $int_if all
Добавить в /etc/users Внутрение адреса Например:
192.168.10.146/24