Главная > Операционные системы > UNIX
Данный раздел на 80% состоит из перевода официальной документации по пакетному фильтру OpenBSD. Остальные 20% — мои добавления из других источников.
Пакетный фильтр OpenBSD при запуске считывает
правила из конфигурационного файла. По умолчанию это файл
Списки позволяют удобным образом задать несколько похожих критериев в одном правиле. Например: вместо того, чтобы писать по одному правилу на каждый 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 } списки могут быть вложенными: trusted = "{ 192.168.1.2 192.168.5.36 }" pass in inet proto tcp from { 10.10.0.0/24 $trusted } to port 22 Будьте осторожны с отрицаниями в списках. Следующий пример демонстрирует распространённую ошибку: pass in on fxp0 from { 10.0.0.0/8, !10.1.2.3 } Эта запись означает не «любой адрес из сети 10.0.0.0/8 кроме 10.1.2.3», а раскрывается в следующие два правила: pass in on fxp0 from 10.0.0.0/8 pass in on fxp0 from !10.1.2.3 Если больше нет никаких других ограничивающих правил, такое сочетание приведёт к тому, что будут пропущены вообще все пакеты кроме пакета с машины 10.1.2.3. Для решения такой задачи лучше применять таблицы (см. Раздел C.2.1.3, «Таблицы»).
Макросы, это определённые пользователем переменные, которые
могут содержать IP-адреса, номера портов, имена интерфейсов
и т.п. Имя макроса подчиняется традиционным для
большинства языков программирования правилам: начинаться оно
должно с буквы, а за ней должны идти буквы, цифры или
символы подчерка. Имя не должно быть зарезервированным
словом, таким как ext_if = "fxp0" block in on $ext_if from any to any
Здесь создан макрос Макросы могут раскрываться в списки: 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 "}"
Макрос
При помощи команды pfctl(8) можно
переопределять значения макросов с помощью опции
Таблицы используются для хранения адресов IPv4 и/или IPv6. Поиск в них осуществляется очень быстро, они расходуют значительно меньше памяти и процессорного времени, чем списки. Таблицы, таким образом, идеальны для хранения больших массивов адресов, поскольку поиск в таблице с 50 000 записей происходит не на много медленнее, чем в таблице с 50 адресами. Таблицы можно использовать следующим образом:
Таблицы можно создавать как в конфигурационном файле
В конфигуационном файле
Пример (имя таблицы указывается в угловых скобках 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 { <rfc1918>, <spammers> } to any pass in on fxp0 from <goodguys> to any
Адреса так же можно употреблять с отрицательным знаком ( table <goodguys> { 192.0.2.0/24, !192.0.2.5 }
Таблица Содержимое таблицы можно брать из файла: table <spammers> persist file "/etc/spammers" block in on fxp0 from <spammers> to any
Файл
Таблицей можно манипулировать при помощи утилиты
pfctl(8). Например, следующая команда
добавляет в таблицу
Кроме того, указанная команда создаст таблицу
Опция
Чтобы удалить адрес из таблицы можно использовать команду
См. так же Раздел C.3, «Управление пакетным фильтром OpenBSD при помощи утилиты pfctl(8)».
Хосты в таблицах можно указывать не только в виде
IP-адреса, но так же и по имени. В этом случае имена будут
разрешены и все адреса, соответствующие данному имени,
попадут в таблицу. Кроме того, можно указывать имя
интерфейса или ключевое слово Ограничение: адреса 0.0.0.0/0 и 0/0 в таблицах не работают. Используйте списки. При поиске в таблице находится «наиболее подходящий» адрес, т.е. сеть с самой большой маской. (Наибольшая маска у самой «узкой» сети.) Например: table <goodguys> { 172.16.0.0/16, !172.16.1.0/24, 172.16.1.100 } block in on dc0 all pass in on dc0 from <goodguys> to any
Адрес источника каждого пакета пришедшего через интерфейс
Фильтрация пакетов заключается в том, что пакеты пропускаются или отбрасываются при прохождении через сетевой интерфейс в соответствии с правилами. Правила основаны на заголовках пакетов сетевого и транспортного уровней модели OSI (см. OSI). Наиболее часто используемые критерии — адреса источника и назначения, номера портов источника и назначения, протоколы.
Правила фильтра состоят из критерия и действия, которое надо
предпринять, если пакет соответствует критерию. Действие
может быть или Ниже приведена упрощённая синтаксическая схема: action [direction] [log] [quick] [on interface] [af] [proto protocol] \ [from src_addr [port src_port]] [to dst_addr [port dst_port]] \ [flags tcp_flags] [state]
Полная синтаксическая схема приведена в справочной странице
man(1) по
Рекомендуемая практика при написании брандмауэров — делать политику «default deny», т.е. по умолчанию отбрасывать все пакеты, а потом пропускать некоторые разрешённые пакеты. Для создания политики «default deny» надо сделать следующие первые два правила: block in all block out all Эти правила блокируют весь трафик на всех интерфейсах вне зависимости от направления. Теперь нам надо явно разрешить прохождение трафика, чтобы он не был заблокирован политикой. Здесь понадобятся такие критерии как номера портов источника и назначения, адреса источника и назначения, протоколы. Правила должны быть настолько строгими, насколько это возможно, для того, чтобы через брандмауэр проходил только нужный трафик. Например: # Пропускать трафик из локальной сети (192.168.0.0/24) идущий через # интерфейс dc0 на машину OpenBSD (FreeBSD, NetBSD) с адресом 192.168.0.1, # а так же выпускать ответный трафик. pass in on dc0 from 192.168.0.0/24 to 192.168.0.1 pass out on dc0 from 192.168.0.1 to 192.168.0.0/24 # Пропускать TCP трафик на интерфейсе fxp0 направленный на web сервер # запущенный на нашей OpenBSD (FreeBSD, NetBSD) машине. Имя интерфейса # использовано в качестве адреса назначения, поэтому правилу # соответствуют только пакеты направленные к нам. pass in on fxp0 proto tcp from any to fxp0 port www
Как было замечено выше, пакеты проходят через все правила
от начала до конца. Пакет, который пометился правилом как
проходящий ( Вот пара примеров: Неправильный пример: 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
В данном примере трафик ssh будет отброшен немедленно,
так как встретилось ключевое слово Неоднозначный пример: pass in all block in on fxp0 proto tcp from any to any port ssh Такие два правила возможно приведут к блокированию трафика, а может быть и нет. В зависимости от того, какие правила окажутся ниже по тексту. Одна из важных возможностей пакетного фильтра — отслеживание состояния соединений («stateful inspection»). Stateful inspection возможна благодаря возможности пакетного фильтра отслеживать состояния сетевых соединений. Состояние каждого соединения хранится в таблице состояний. Пакетный фильтр может быстро определить принадлежит ли пакет уже открытому соединению. Если пакет принадлежит уже открытому соединению, то его пропускают, не направляя на другие фильтрующие правила. Использование таблицы состояний имеет много преимуществ: от упрощения правил фильтрации, до увеличения производительности брандмауэра. Пакетный фильтр может определить принадлежность пакета открытому соединению независимо от направления в котором идёт пакет. Это освобождает от необходимости написания правил пропускающих ответный трафик. А поскольку пакет не направляется ни на какие правила, время прохождения пакета через брандмауэр существенно уменьшается.
Если в правиле присутсвует опция pass out on fxp0 proto tcp from any to any keep state
Это правило разрешает любой исходящий трафик на
интерфейсе
Функция Сохранять состояние TCP, UDP и ICMP трафика, и рандомизировать ISN в TCP: pass out on fxp0 proto { tcp, udp, icmp } from any to any modulate state
Другое преимущество таблицы состояний состоит в том, что
трафик ICMP соответствующий открытому соединению тоже
пропускается через брандмауэр. Например: если
Область действия записи в таблице состояния ограничивается
задаваемой глобально опцией pass out on fxp0 proto { tcp, udp, icmp } from any \ to any modulate state (if-bound)
Это правило гласит, что пакет будет соответствовать записи в
таблице состояний только если он идёт через интерфейс
Правила
Кто-то может подумать, что для протокола UDP нельзя делать
записи в таблице состояний, так как это
«stateless» протокол. Однако пакетный фильтр
может отслеживать его состояния. Несмотря на отсутствие
«стартового» пакета, пакетный фильтр следит за
пакетами на основании таймаутов и номеров портов источника и
назначения. По достижении таймаута таблица состояний
очищается от соответствующей записи. Величину таймаута можно
задать в разделе «опции»
Можно использовать некоторые опции для управления поведением
записей в таблице состояний, созданных при помощи команд
Например: pass in on $ext_if proto tcp to $web_server \ port www flags S/SA keep state \ (max 200, source-track rule, max-src-nodes 100, max-src-states 3) Это правило означает следующее:
Отдельные ограничения можно ввести для TCP соединений прошедших тройное рукопожатие (см. Раздел B.1.4.3.2, «Открытие соединения TCP, тройное рукопожатие»):
Обе опции автоматически включают опцию В комбинации с данными опциями можно употреблять более агрессивные опции, для «наказания» «провинившихся».
Пример: table <abusive_hosts> persist block in quick from <abusive_hosts> pass in on $ext_if proto tcp to $web_server \ port www flags S/SA keep state \ (max-src-conn 100, max-src-conn-rate 15/5, overload <abusive_hosts> flush) Эти правила делают следующее:
При открытии соединения TCP пакетный фильтр должен изучить
флаги TCP выставленные в заголовке пакета. Описание флагов
дано в Таблица B.3, «Флаги TCP». Для изучения флагов
применяется ключевое слово pass in on fxp0 proto tcp from any to any port ssh flags S/SA Приведённое правило пропускает весь TCP трафик c установленным флагом SYN, при этом изучаются флаги SYN и ACK. Пакет с флагами SYN и ECE будет пропущен данным правилом, а пакет в котором выставлены флаги SYN и ACK или просто ACK не будет пропущен.
Часто флаги указываются вместе с правилом pass out on fxp0 proto tcp all flags S/SA keep state Данное правило разрешает исходящие соединения начинающиеся с пакета в котором выставленн флаг SYN, а флага ACK нет. Будьте внимательны при использовании флагов. Понимайте что вы делаете и зачем, особенно когда пользуетесь чьими-то советами. Некоторые люди предлагают открывать соединения если указан флаг SYN и никакой другой: . . . flags S/FSRPAUEW плохая идея!! Теоретически первый пакет должен содержать флаг SYN и никакой другой, однако некоторые хосты выставляют флаг ECN и будут отвергнуты данным правилом. Более разумным будет следующее правило: . . . flags S/SAFR Это практично и безопасно, однако в этом нет необходимости, если трафик был нормализован при помощи scrub. Процесс нормализации трафика заставляет пакетный фильтр отбрасывать пакеты с неправильными сочетаниями флагов (вроде SYN+RST) или подозрительным счетанием (SYN+FIN). Крайне желательно всегда подвергать трафик нормализации: scrub in on fxp0 . . . pass in on fxp0 proto tcp from any to any port ssh flags S/SA keep state В обычной ситуации клиент выполняет тройное рукопожатие с сервером (см. Раздел B.1.4.3.2, «Открытие соединения TCP, тройное рукопожатие»). Пакетный фильтр умеет выполнять в этой процедуре функцию посредника. При этом фильтр выполняет тройное рукопожатие с клиентом, затем проводит рукопожатие с сервером, и уже после этого начинает пробрасывать пакеты между клиентом и сервером. Этот метод позволяет избежать TCP SYN флуда (разновидность сетевой DOS атаки, когда клиент забрасывает сервер заявками на открытие соединения, но соединение не открывает. В результате у сервера могут исчерпаться сокеты. см. так же DOS атака).
Проксирование рукопожатия проводится при помощи ключевого
слова pass in on $ext_if proto tcp from any to $web_server port www \ flags S/SA synproxy state В этом примере проксируется входящее соединение к web-серверу.
IP-спуфинг — подделка исходящих адресов в заголовке IP пакета (см. spoofing).
Пакетный фильтр может осуществлять защиту от спуфинга при
помощи правил начинающихся с ключевого слова antispoof [log] [quick] for interface [af]
Пример: antispoof for fxp0 inet
Каждое правило block in on ! fxp0 inet from 10.0.0.0/24 to any block in inet from 10.0.0.1 to any Эти правила означают следующее:
Начиная с OpenBSD 4.0 в пакетном фильтре появилась возможность проверять исходящие адреса при помощи таблицы маршрутизации. Если пакет подвергается проверке uRPF, исходящий IP адрес разыскивается в таблице маршрутизации, и если указанный в ней интерфейс соответствует интерфейсу, через который пришёл пакет, то пакет проходит проверку, а если нет — то мы имеем дело с IP-спуфингом.
Проверка uRPF осуществляется при помощи ключевого слова
block in quick from urpf-failed label uRPF Проверка uRPF работает только при симметричной маршрутизации. В противном случае трафик будет заблокирован.
Если машина является конечной точкой IPSec туннеля, нельзя
включать проверку uRPF на интерфейсе enc0, иначе будет
заблокирован весь инкапсулированный трафик. Рекомендуется
пропускать все пакеты на интерфейсе enc0: block in quick on ! enc0 from urpf-failed label uRPF
Пакетный фильтр обладает возможностью определять из какой
операционной системы был отправлен SYN пакет. Делается это
на основе некоторых характерных особенностей работы TCP
стека разных систем. Так, известно, что операционные
системы Windows NT склонны
выставлять поле TTL в 128, тогда как
Linux,
FreeBSD — 64. Особенности
работы стека приведены в файле
В моей системе насчитывается 330 fingerprint'ов. Включая Zaurus (наладонник фирмы Sharp с Linux'ом) 2-х версий.
Фильтрацию можно осуществлять при помощи ключевого слова
pass in on $ext_if from any os OpenBSD keep state block in on $ext_if from any os "Windows 2000" block in on $ext_if from any os "Linux 2.4 ts" block in on $ext_if from any os unknown
Ключевое слово
По умолчанию пакетный фильтр отбрасывает IP пакеты с
выставленными опциями, для затруднения работы программ
пытающихся узнать тип нашей операционной системы, например
nmap(1). Если у вас есть приложения
нуждающиеся в прохождении такого трафика, например
multicast или IGMP, используйте опцию pass in quick on fxp0 all allow-opts Ниже приведён краткий пример конфигурационного файла для брандмауэра между небольшой внутренней сетью и Интернетом. Здесь приведены только правила фильтрации, nat, rdr, queue опущены. ext_if = "fxp0" int_if = "dc0" lan_net = "192.168.0.0/24" # Таблица содержит все IP адреса принадлежащие брандмауэру table <firewall> const { self } # Не фильтруем пакеты на кольцевом интерфейсе set skip on lo0 # Нормализуем входящий трафик scrub in all # Политика по умолчанию block all # Включаем защиту от спуфинга на внутреннем интерфейсе antispoof quick for $int_if inet # Разрешаем ssh соединения из локальной сети только с доверенного # компьютера 192.168.0.15. Использование "block return" приведёт тому, # что будет возвращаться пакет TCP RST для того, чтобы заблокированное # соединение закрывалось правильным образом. "quick" используется для # того, чтобы идущие ниже правила pass не переопределили данное # действие. block return in quick on $int_if proto tcp from ! 192.168.0.15 \ to $int_if port ssh flags S/SA # Разрешить весь трафик из локальной сети к брандмауэру и обратно pass in on $int_if from $lan_net to any pass out on $int_if from any to $lan_net # Пропустить исходящие tcp, udp и icmp пакеты на внешнем интерфейсе. # Сохранять состояния соединений. pass out on $ext_if proto tcp all modulate state flags S/SA pass out on $ext_if proto { udp, icmp } all keep state # Разрешить сединения ssh на внешнем интерфейсе если они направлены не # брандмауэру, а другой машине (во внутренней сети). Первый пакет # заносить в журнал, чтобы потом можно было сказать кто решил открыть # соединение. Использовать syn proxy для защиты от syn флуда. pass in log on $ext_if proto tcp from any to ! <firewall> \ port ssh flags S/SA synproxy state Суть трансляции адресов NAT описана в глоссарии: NAT. Когда клиент из внутренней сети пытается послать IP пакет в Интернет в этом пакете подменяется исходящий IP адрес на адрес шлюза, а так же, при необходимости, подменяется номер порта источника у пакетов TCP и UDP. Делается запись в таблице состояний. Обратные пакеты находятся в таблице состояний и с ними проделывается аналогичное обратное преобразование. Ни внутренняя машина, ни внешняя не знают о существовании NAT. Всё происходит прозрачно. Для внутренней машины NAT это просто шлюз, а внешняя машина ничего не знает о внутренней и считает, что соединение открыто шлюзом. Обнаружить NAT можно только по косвенным признакам.
Пакеты подвергаемые трансляции проходят через фильтр и
будут отброшены или пропущены в зависимости от правил
которые там встретятся. Единственное
исключение — если в правиле NAT встретится
ключевое слово Трансляция осуществляется до фильтрации. Правила фильтра увидят уже оттранслированные пакеты.
Поскольку NAT всегда используется на шлюзах и
маршрутизаторах, необходимо включить проброс пакетов. Для
этого надо выставить переменную ядра
Чтобы сделать эти изменения постоянными следует
добавить в файл net.inet.ip.forwarding=1 net.inet6.ip6.forwarding=1
Синтаксическая диаграмма правила NAT в
nat [pass [log]] on interface [af] from src_addr [port src_port] to \ dst_addr [port dst_port] -> ext_addr [pool_type] [static-port]
В большинстве случаев для NAT трансляции годится примерно такая строка: nat on tl0 from 192.168.1.0/24 to any -> 24.5.0.5 Это правило указывает, что надо осуществить NAT трансляцию на интерфейсе tl0 для каждого пакета пришедшего из сети 192.168.1.0/24 подменив адрес источника на 24.5.0.5. Предыдущая строка корректна, но рекомендуется для облегчения поддержки брандмауэра использовать другую форму записи (в примере dc0 внутренний интерфейс, а tl0 внешний): nat on tl0 from dc0:network to any -> tl0
При использовании имени интерфейса, как указано выше,
адрес будет определён и подставлен когда
nat on tl0 from dc0:network to any -> (tl0) Трансляция работает как для IPv4 так и для IPv6.
Соответствия между двумя хостами 1:1 можно достичь при
помощи правила Пример: 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 nat on tl0 from 192.168.1.208 to any nat on tl0 from 192.168.1.0/24 to any -> 24.2.74.79 Вся сеть 192.168.1.0/24 будет транслироваться к адресу 24.2.74.79, кроме пакетов идущих от хоста 192.168.1.208.
Здесь первое правило выигрывает. Если
есть ключевое слово
Чтобы увидеть состояния соединений подвергаемых NAT
трансляции можно воспользоваться командой
pfctl(8) с аргументом
Этот отчёт (первая срока) означает следующее:
Если у вас работает NAT, вам доступен весь Интернет, но как быть если за шлюзом с NAT, в приватной сети находится машина доступ к которой нужен снаружи? Здесь нам поможет проброс портов. С его помощью мы можем перенаправлять входящий трафик на машину расположенную за шлюзом с NAT. Пример: rdr on tl0 proto tcp from any to any port 80 -> 192.168.1.20 С помощью этого правила все обращения к 80 порту будут пробрасываться на машину 192.168.1.20. (В Linux netfilter это действие называется DNAT, так как у пакета подменяется не source IP, а destination IP.)
Директива rdr on tl0 proto tcp from 27.146.49.0/24 to any port 80 -> \ 192.168.1.20 Таким образом, можно пробросить только некоторую подсеть. Данный подход позволяет так же пробрасывать разные подсети на разные хосты. Мы можем использовать это свойство для того, чтобы давать пользователям доступ к их компьютерам в локальной сети на основании адреса с которого они обращаются к шлюзу: 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 Так же можно пробрасывать диапазоны портов: rdr on tl0 proto tcp from any to any port 5000:5500 -> \ 192.168.1.20 Транслированные пакеты, как и в случае с NAT, направляются на правила фильтра и могут быть как приняты, так и отброшены.
Единственное исключение: если в правиле Кроме того, имейте в виду, что на фильтр пакеты посылаются после трансляции. Рассмотрим следующий сценарий:
Правило перенаправления: rdr on tl0 proto tcp from 192.0.2.1 to 24.65.1.13 port 80 \ -> 192.168.1.5 port 8000 Вид пакета перед трансляцией:
Вид пакета после трансляции:
Правила фильтра увидят что пакет идёт на машину 192.168.1.5 на порт 8000 Создание подобных отверстий в брандмауэре связано с уменьшением безопасности системы. Например, если у вас во внутренней сети находится web-сервер и вы пропустили на него трафик из Интернет, то злоумышленник может используя уязвимости в работе web-сервера или CGI сценария, получить доступ к web-серверу и, таким образом, проникнет в защищённую вами локальную сеть. Снизить риск возникновения подобной ситуации можно путём построения демилитаризованной зоны DMZ (см. DMZ). Перенаправление часто используется для предоставления доступа внешним машинам к внутреннему серверу: server = 192.168.1.40 rdr on $ext_if proto tcp from any to $ext_if port 80 -> $server \ port 80
Однако при тестировании правил перенаправления из внутренней
сети они не работают. Дело в том, что пакеты из внутренней
сети не проходят через внешний интерфейс шлюза (
Добавление второго И всё таки желательно из внутренней сети видеть сервер так же как он виден из внешней и так, чтобы для клиента всё было прозрачно. Существует несколько способов решения этой проблемы. Сервер DNS можно настроить так, что он будет давать разные ответы в разные сети. Можно сделать так, чтобы локальные клиенты ходили на сервер непосредственно, без помощи шлюза. Такое решение, к тому же, снижает нагрузку на шлюз. Можно переместить сервер в отдельную сеть (см. так же DMZ) и добавить новый сетевой интерфейс в шлюз.
Можно произвести проксирование TCP соединений при помощи
приложений из userspace. Приложение перехватывает
соединение, устанавливает соединение с сервером и далее
пробрасывает данные через себя. Простейший пример можно
сделать при помощи inetd(8) (см. Раздел 5.17.2, «Суперсервер inetd(8)»)и nc(1) (см.
Раздел 6.4.4, «telnet(1), nc(1)»). Следующая строка в
127.0.0.1:5000 stream tcp nowait nobody /usr/bin/nc nc -w 20 192.168.1.10 80 Теперь на внутреннем интерфейсе мы можем связать 80-й порт с нашим proxy-сервером: rdr on $int_if proto tcp from $int_net to $ext_if port 80 -> \ 127.0.0.1 port 5000 И наконец, в комбинации с правилом 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 трансляциям и пакет отправится
к клиенту. Это достаточно сложный приём. Нужна
осторожность, чтобы не применить правила NAT к другому
трафику, например к внешним соединениям (прошедшим через
другие правила Авторы документации к пакетному фильтру рекомендуют в общем случае использовать какое-нибудь из предыдущих решений вместо последнего.
Пакетный фильтр предоставляет различные способы упрощения
конфигурационного файла. Хорошим примером является
использование списков и
макросов. Вдобавок язык и грамматика
Макросы полезны так как позволяют использовать понятные
имена вместо имён интерфейсов и адресов. Если меняется IP
адрес сервера можно просто переопределить макрос вместо
того, чтобы переписывать весь файл. Аналогичные соображения
касаются имён интерфейсов. Макросы позволяют упростить
модифицирование Пример: # 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" Если в LAN появятся новые сети, или сети будут перенумерованы, достаточно будет поменять одну строку: IntNet = "{ 192.168.0.0/24, 192.168.1.0/24 }" Теперь после перезагрузки правил всё будет работать по-прежнему. Следующие 8 строк служат для того, чтобы блокировать трафик связанный с приватными сетями, описаными в [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
Заметьте, что макросы и списки упрощают файл
Макросы можно использовать не только для хранения адресов, интерфейсов и портов, но вообще везде: 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 Пакетный фильтр обладает гибкой в тоже время человечной грамматикой. Нет необходимости строго помнить порядок ключевых слов и строго придерживаться какого-то определённого стиля. Для определения политики отбрасывающей по умолчанию пакеты, следует задать два правила: block in all block out all Это можно сократить до block 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 Первое правило блокирует все пакеты входящие через интерфейс rl0. Второе пропускает входящие пакеты, если они идут на 22-й порт. Правила блокирующие пакеты и отсылающие пакеты TCP RST и ICMP Unreachable должны выглядеть так: 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
Когда пакетный фильтр видит ключевое слово Порядок следования ключевых слов в большинстве случаев гибок. Например, следующее правило: 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 Другие похожие варианты тоже будут работать.
Опции в
Пример задания опций в пакетном фильтре: 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 skip on lo0 set state-policy if-bound
Нормализация трафика нужна для того, чтобы исключить неопределённость с тем куда направляется пакет. Кроме того, при нормализации собираются вместе фрагментированные пакеты, происходит защита операционных систем от некоторого вида атак и отбрасываются TCP пакеты с невозможным сочетанием флагов. Простейшая директива выглядит так: scrub in all Это приводит к нормализации всего входящего трафика на всех интерфейсах.
Одна из возможных причин для неиспользования
нормализации — использование NFS. Некоторые не
OpenBSD платформы используют странные
пакеты — фрагментированные, но с выставленным
битом «нефрагментировано», которые должны
отбрасываться пакетным фильтром при нормализации. Эту проблему
можно разрешить при использовании опции
Синтаксис директивы Scrub имеет следующие опции:
Пример: 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
Вдобавок к обычным наборам правил пакетный фильтр может использовать поднаборы. Если таблицы можно использовать для динамической смены на лету наборов IP-адресов, то поднаборы правил можно использовать для динамического переконфигурирования брандмауэра. С их помощью можно менять наборы правил фильтра, nat, binat и rdr. Поднаборы правил можно объявлять при помощи «якорей», — anchor Существует четыре типа anchor'ов:
Поднаборы могут быть вложенными и вызывать друг друга по
цепочке. Правила anchor обрабатываются в том месте, где они
вызываются. Например, правило Именованный набор — это группа правил фильтрации, и/или правил трансляции, которым было назначено имя. Когда пакетный фильтр обнаруживает якорь в главном наборе правил, он производит проверку всех поднаборов правил привязанных к нему. Пример: ext_if = "fxp0" block on $ext_if all pass out on $ext_if all keep state anchor goodguys
Этот набор правил устанавливает по умолчанию запретительную
политику на интерфейсе
Загрузка правил указывает pfctl(8) загрузить правила из текстового файла. Например: load anchor goodguys:ssh from "/etc/anchor-goodguys-ssh"
Когда будет загружен главный набор, правила, перечисленные в
файле Используя pfctl(8) можно добавить правило к якорю:
Таким образом, мы добавляем правило Правила также могут быть сохранены и загружены из текстового файла:
Эта операция загрузит правила из файла
Поскольку наборы правил могут быть вложенными, существует возможность вызвать все правила вложенные в некоторый набор: anchor "spam/*" Синтаксис правил в подгружаемых наборах правил такой же как и в главном наборе правил, но все используемые макросы должны быть определены в пределах этого же набора: макросы, определённые в главном наборе не видны из именованного набора. Каждый именованный набор существует обособленно от остальных. Операции, проводимые над ним, такие как сброс правил, не имеют эффекта над остальными. Кроме того, удаление указателя на якорь не приводит к удалению ни самого якоря ни привязанных к нему именованных наборов правил. Именованный набор существует до тех пор, пока все его правила не будут сброшены, используя 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
Правила из якоря
Несмотря на то, что в правиле не определён ни порт, ни протокол, ни интерфейс, хосту 192.0.2.10 будет разрешена работа только по протоколу ssh, в силу определений сделанных при объявлении якоря. Управление именованными наборами правил осуществляется при помощи утилиты pfctl(8). Она позволяет удалять и добавлять правила в набор без перезагрузки главного набора правил.
Вывести список правил из набора
Сбросить набор правил
Поставить что-то в очередь, значит сохранить это до обработки. При работе в сети, данные получаемые хостом поступают в очередь и ждут, когда они будут обработаны операционной системой, при этом она решает, какие именно пакеты и в каком порядке обрабатывать. Изменение порядка обработки пакетов может оказать влияние на производительность сети. В идеальном случае, пакеты ssh должны обрабатываться в первую очередь, так как этот протокол очень чувствителен к задержкам. При нажатии клавиши в ssh-клиенте ожидается немедленный ответ, но идущая передача по ftp вызывает задержку в несколько секунд. Что может произойти в случае, когда роутер обрабатывает большое количество ftp пакетов? Пакеты ssh сохраняются в очереди, а то и просто отбрасываются в случае малого буфера и в результате ssh сессия может вообще прерваться. Изменение стратегии организации очередей может позволить распределить пропускную способность между различными приложениями, пользователями и хостами. Обратите внимание, что организация очереди имеет смысл только для исходящих соединений, потому что как только пакет попал на входящий интерфейс с ним уже поздно что-либо делать, так как полоса пропускания канала была уже использована. Единственным решением этой проблемы может стать организация очереди на смежном маршрутизаторе или позволять организацию очереди на внутреннем интерфейсе, если хост сам является смежным маршрутизатором. Планировщиком называется то, что организовывает очередь и определяет порядок обработки пакетов. По умолчанию в OpenBSD в качестве планировщика используется FIFO, очередь. Принцип её работы очень прост — первый вошёл — первый вышел. Новоприбывший пакет добавляется в конец очереди. При превышении максимального размера очереди пакет отбрасывается. Это явление известно как tail-drop (отброс хвоста, как у ящерицы). Есть и другие планировщики. OpenBSD (и FreeBSD) поддерживает ещё три планировщика:
В очередях базирующиеся на классах (CBQ) алгоритм организации очереди построен на разделении полосы пропускания между различными очередями или классами. Трафик присоединяется к очереди на основании адреса источника или отправителя, порта, протокола и т.д. Очередь может быть сконфигурирована на заимствование произвольной полосы пропускания от родительской очереди, если та занимает свой канал не полностью. Очереди также могут работать с системой приоритетов, например, пропуская ssh трафик в первую очередь, по сравнению с трафиком ftp. В CBQ очереди размещаются иерархическим способом. В самом верху — родительская очередь, определяющая общую пропускную способность. Дочерним очередям назначается некоторая часть от полосы пропускания родительской очереди. Например, очереди могут быть определены следующим способом: Root Queue (2Mbps) Queue A (1Mbps) Queue B (500kbps) Queue C (500kbps) Здесь общая полоса пропускания — 2 мегабита в секунду (megabits per second — Mbps), Которая разделена на три подочереди.
Иерархия может быть расширена с использованием вложенности. Для того, чтобы разделить полосу пропускания между различными пользователями, при этом сделав так, что бы различные виды трафика не мешали друг другу, можно создать следующую структуру очередей: Root Queue (2Mbps) UserA (1Mbps) ssh (50kbps) bulk (950kbps) UserB (1Mbps) audio (250kbps) bulk (750kbps) http (100kbps) other (650kbps) Обратите внимание, что на каждом уровне сумма полос пропускания не может быть больше родительской. Очередь может быть настроена таким образом, что будет заимствовать (borrow) полосу пропускания у родителя в случае неиспользования ее другими очередями. Рассмотрим такую организацию очереди: Root Queue (2Mbps) UserA (1Mbps) ssh (100kbps) ftp (900kbps, borrow) UserB (1Mbps) Если трафик в очереди ftp превышает 900 kbps, а трафик в очереди UserA — меньше чем 1 Mbps (потому что очередь ssh использует меньше чем 100 kbps), полоса пропускания ftp может быть увеличена. Таким образом очередь ftp способна использовать больше чем назначенные ей 900 kbps, но в случае увеличения очереди ssh заимствованая полоса освобождается. CBQ может назначать каждой группе определённый приоритет. В моменты перегрузки предпочтение отдается очередям с более высоким приоритетом в случае наличия у них одного родителя. При одинаковых приоритетах очереди обслуживаются циклически. Для примера: Root Queue (2Mbps) UserA (1Mbps, priority 1) ssh (100kbps, priority 5) ftp (900kbps, priority 3) UserB (1Mbps, priority 1) Очереди UserA и UserB будут обрабатываться циклически, так как их приоритеты равны. В случае перегрузки в сети, предпочтение будет отдаваться ssh, так как её приоритет больше, чем у очереди ftp. При этом очереди ssh и ftp не имеют приоритета перед очередями UserA и UserB, так как они находятся на более низком уровне. Детальная информация о CBQ приведена в References on CBQ. Приоритетные очереди (PRIQ) создаются на сетевом интерфейсе, причем структура очередей является плоской — нельзя создавать дочерние очереди. Сперва определяется корневая очередь с указанием общей пропускной способности, а за ней все дочерние очереди. Например: Root Queue (2Mbps) Queue A (priority 1) Queue B (priority 2) Queue C (priority 3) Пропускная способность основной очереди определена как 2 Mbps, следом идут дочерние. При использовании PRIQ вы должны очень тщательно планировать очереди, так как они обрабатываются строго по приоритетам и если трафик с высоким приоритетом займёт весь канал, пакеты принадлежащие трафику с низким приоритетом будут отбрасываться. Очереди закреплённые за интерфейсом выстраиваются в иерархическое древо, каждая очередь может иметь потомка. Каждая очередь может иметь свой приоритет и свою полосу пропускания. Этот вид очереди похож на CBQ. Основное отличие HFSC от CBQ в том, что она позволяет отдельно регулировать задержки и пропускную способность очереди. В CBQ единственный параметр очереди это полоса пропускания. Если мы нарисуем «кривую сервиса» (прохождение пакетов по времени), то это будет некоторая прямая. Единственный способ снизить задержки при работе такой очереди состоит в том, чтобы увеличить полосу пропускания. В HFSC кривая сервиса состоит из двух линейных участков: ![]()
Кривая service curve. m1 — наклон первого
участка кривой, соответствует начальной полосе
пропускания и, в совокупности с параметром d, позволяет
регулировать задержки независимо от
полосы пропускания. d — время в течении
которого действует полоса пропускания m1.
m2 — конечная полоса пропускания.
Дополнительная информация о HFSC может быть найдена по адресу http://www.cs.cmu.edu/~hzhang/HFSC/main.html. Случайное раннее обнаружение (Random Early Detection, RED) — это алгоритм определения перегрузки канала. Его целью является предотвращение переполнения очереди. Делается это путём непрерывного сравнения текущей длины очереди с минимальным и максимальным порогами. Если минимальный порог не достигнут — все пакеты пропускаются. Если достигнут максимальные порог — все пакеты отбрасываются. В промежутке пакеты отбрасываются с определённой вероятностью, зависящей от размера очереди. Чем ближе к максимальному порогу — тем выше вероятность. Пакеты для отбрасывания выбираются случайным образом из разных сессий. Чем большая полоса пропускания занимается сессией, тем выше вероятность сброса из неё пакета. RED весьма полезен, так как позволяет избежать ситуации, называемой «глобальной синхронизацией», она проявляется в том, что связь полностью прекращается из-за одновременно отбрасываемых пакетов с разных сессий. Например, если перегрузка происходит на маршрутизаторе, обслуживающем 10 одновременных сессий ftp и будут отброшены пакеты от большинства или всех сессий, общая пропускная способность резко понизится. RED позволяет избежать этого, выбирая сессии из которых терять пакеты случайным образом. Поскольку сессии занимающие больше полосы пропускания имеют больший шанс на потерю пакета, то возможность возникновения перегрузки исчезнет и больших потерь трафика не произойдет. Кроме того, RED позволяет обработать взрывной всплеск трафика, так как начинает отбрасывать пакеты до переполнения очереди. RED должен использоваться только тогда, когда транспортный протокол способен реагировать на индикаторы перегрузки сети. В большинстве случаев это означает, что RED должен применяться только к очередям TCP, а не к очередям UDP или ICMP. Детальная информация о RED приведена в References on RED. Явное уведомление о перегрузке (ECN) работает совместно с RED и применяется для уведомления двух связанных хостов о перегрузке сети. Делается это разрешением RED установить флаг в заголовке пакета, вместо того, чтобы отбросить пакет. Если удалённый хост поддерживает ECN и читает флаги ECE и CWR, то он начинает снижать исходящий трафик (см. Таблица B.3, «Флаги TCP», [RFC-3168]). Начиная с OpenBSD 3.0 Alternate Queueing (ALTQ) стал частью основной системы, а с версии OpenBSD 3.3 ALTQ был интегрирован в пакетный фильтр и, следовательно, портирован вместе с ним и в FreeBSD. Реализация ALTQ в OpenBSD поддерживает планировщики CBQ PRIQ и RED вместе с ECN.
Очереди конфигурируются в файле
Синтаксис директивы altq on interface scheduler bandwidth bw qlimit qlim \ tbrsize size queue { queue_list }
Итак: altq on fxp0 cbq bandwidth 2Mb queue { std, ssh, ftp }
Это правило запускает CBQ на интерфейсе
Синтаксис директивы queue name [on interface] bandwidth bw [priority pri] [qlimit qlim] \ scheduler ( sched_options ) { queue_list }
Продолжение примера: 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 назначена полоса пропускания в 500 kbps и дан приоритет 3. Эта очередь может арендовать свободную пропускную способность других очередей и используется red. Для направления трафика в очередь используется ключевое слово queue в правилах пакетного фильтра. Для примера рассмотрим следующую строку: pass out on fxp0 from any to any port 22 Пакеты из этого правила можно направить в очередь следующим способом: pass out on fxp0 from any to any port 22 queue ssh
Если ключевое слово
Обратите внимание, что 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. Этот тип очередей может быть очень полезен на маршрутизаторах.
Обычно с ключевым словом pass out on fxp0 from any to any port 22 queue(ssh_bulk, ssh_login) Повышение приоритета пакетов TCP ACK имеет смысл на асинхронных соединениях, таких как ADSL, где скорость входящего и исходящего потока не равны между собой. На ADSL линии при полностью занятом исходящем канале будет снижаться и полезное использование входящего канала, так как пакеты TCP ACK будут теряться и задерживаться. Тестирования показали, что для достижения наибольшей эффективности, полоса пропускания должна быть немного меньше, возможностей канала. Например, если ADSL линия дает максимальную скорость в 640 kbps, установите значение пропускной способности для корневой линии в 600 kb. Оптимальное значение находится путем проб и ошибок.
Когда ключевое слово pass in on fxp0 proto tcp from any to any port 22 flags S/SA \ keep state queue ssh
Обратите внимание, что ключевое слово [ Alice ] [ Charlie ] | | ADSL ---+-----+-------+------ dc0 [ OpenBSD ] fxp0 -------- ( Internet ) | [ Bob ] В этом примере OpenBSD используется как шлюз в Интернет для маленькой домашней сети с тремя рабочими станциями. На шлюзе работает NAT и фильтрация пакетов. Выход в Интернет осуществляется по ADSL с входящей скоростью 2Mbps и исходящей 640Kbps. Для очередей действуют следующие правила:
Ниже представлены правила, реализующие эту политику.
Обратите внимание, что в # Включаем очереди на внешнем интерфейсе для контроля за трафиком # выходящим в Интернет. Используем планировщик priq для контроля только # по приоритетам. Устанавливает ширину пропускания 610 kbps для # оптимального пропускания очереди TCP ACK altq on fxp0 priq bandwidth 610Kb queue { std_out, ssh_im_out, dns_out, \ tcp_ack_out } # определяем параметры дочерних очередей # std_out - стандартная очередь. Любые правила ниже, в которых # очередь не указана явно, добавляют трафик к этой # очереди. # ssh_im_out - интерактивный SSH и интернет пейджеры # dns_out - запросы DNS # tcp_ack_out - пакеты TCP ACK не несущие полезной нагрузки queue std_out priq(default) queue ssh_im_out priority 4 priq(red) queue dns_out priority 5 queue tcp_ack_out priority 6 # Включаем очереди на внутреннем интерфейсе для контроля трафика # пришедшего из Интернет. Используем планировщик cbq для контроля полосы # пропускания. Максимальная полоса 2 Mbps. altq on dc0 cbq bandwidth 2Mb queue { std_in, ssh_im_in, dns_in, bob_in } # определяем параметры дочерних очередей # std_in - стандартная очередь. Любые правила ниже, в которых # очередь не указана явно, добавляют трафик к этой # очереди. # ssh_im_in - интерактивный SSH и интернет пейджеры # dns_in - ответы DNS # bob_in - Полоса зарезервированная для Боба, разрешаем ему # увеличивать полосу по мере возможности (borrow) queue std_in bandwidth 1.6Mb cbq(default) queue ssh_im_in bandwidth 200Kb priority 4 queue dns_in bandwidth 120Kb priority 5 queue bob_in bandwidth 80Kb cbq(borrow) # ... раздел фильтрации ... 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 }" # правила фильтрации входящего трафика на fxp0 block in on fxp0 all # правила фильтрации исходящего трафика на fxp0 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) # правила фильтрации входящего трафика на dc0 block in on dc0 all pass in on dc0 from $local_net # правила фильтрации исходящего трафика на dc0 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 ( IT Dept ) [ Boss's PC ] | | T1 --+----+-----+---------- dc0 [ OpenBSD ] fxp0 -------- ( Internet ) | fxp1 [ COMP1 ] [ WWW ] / | / --+----------' В этом примере OpenBSD выступает в роли системы сетевой защиты для корпоративной сети. В компании работает WWW сервер, установленный в DMZ. Клиенты обновляют свои сайты через FTP. У IT одела имеется собственная подсеть, соединённая с главной, босс использует свой компьютер для почты и серфинга по сети. Соединение с Интернетом осуществляется на скорости T1 (1.5 Mbps) в обе стороны. Все прочие сетевые сегменты используют Fast Ethernet (100 Mbps). Сетевой администратор выбрал следующую политику:
Ниже представлены правила, реализующие эту политику.
Обратите внимание, что в # Включаем очереди на внешнем интерфейсе для пакетов выходящих в # Интернет. Используем планировщик cbq чтобы контролировать полосу # пропускания каждой очереди. Максимальная исходящая полоса 1.5 Mbps altq on fxp0 cbq bandwidth 1.5Mb queue { std_ext, www_ext, boss_ext } # определяем параметры дочерних очередей # std_ext - стандартная очередь. Так же является очередью для # исходящего трафика на интерфейсе fxp0 # www_ext - контейнер для очередей WWW сервера. Размер 500 kbps # www_ext_http - http трафик WWW сервера - высший приоритет # www_ext_misc - не-http трафик WWW сервера # boss_ext - трафик пришедший с компьютера босса queue std_ext bandwidth 500Kb cbq(default borrow) queue www_ext bandwidth 500Kb { www_ext_http, www_ext_misc } queue www_ext_http bandwidth 50% priority 3 cbq(red borrow) queue www_ext_misc bandwidth 50% priority 1 cbq(borrow) queue boss_ext bandwidth 500Kb priority 3 cbq(borrow) # Включить очереди на внутреннем интерфейсе для контроля трафика # входящего из интернета или из DMZ. Используем планировщик cbq для # контроля за каждой очередью. Полоса пропускания устанавливается в # максимум. Трафику из DMZ позволяется использовать всю полосу, а # трафику пришедшему из Интернет рарешается использовать 1.0 Mbps # (поскольку 0.5 Mbps (500 kbps) зарезервировано на fxp1). altq on dc0 cbq bandwidth 100% queue { net_int, www_int } # определяем параметры дочерних очередей # net_int - Контейнер для трафика из Интернет. Полоса 1.0 Mbps # std_int - Стандартная очередь. Так же является оередью по умолчанию # для исходящего трафика на dc0 # it_int - Трафик IT-отдела. Им зарезервировано 500 kbps # boss_int - Трафик босса имеет высший приоритет # www_int - Трафик WWW сервера из DMZ не имеет ограничений queue net_int bandwidth 1.0Mb { std_int, it_int, boss_int } queue std_int bandwidth 250Kb cbq(default borrow) queue it_int bandwidth 500Kb cbq(borrow) queue boss_int bandwidth 250Kb priority 3 cbq(borrow) queue www_int bandwidth 99Mb cbq(red borrow) # Включаем очереди на интерфейсе DMZ для контроля трафика идущего к WWW # серверу. Используем планировщик cbq для контроля за полосой # пропускания. Полоса выставляется в максимум. Трафик из внутренней сети # может использовать всю полосу пропускания, а трафик из Интернет # ограничен 500 kbps. altq on fxp1 cbq bandwidth 100% queue { internal_dmz, net_dmz } # определяем параметры дочерних очередей # internal_dmz - трафик из внутренней сети # net_dmz - контейнер для очередей трафика идущего из Интернет # net_dmz_http - http трафик, наивысший приоритет # net_dmz_misc - не-http трафик. Это очередь по умолчанию queue internal_dmz bandwidth 99Mb cbq(borrow) queue net_dmz bandwidth 500Kb { net_dmz_http, net_dmz_misc } queue net_dmz_http bandwidth 50% priority 3 cbq(red borrow) queue net_dmz_misc bandwidth 50% priority 1 cbq(default borrow) # ... раздел фильтрации ... 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 # правила фильтрации входящего трафика на интерфейсе fxp0 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 # правила фильтрации исходящего трафика на интерфейсе fxp0 pass out on fxp0 from $int_nets to any keep state pass out on fxp0 from $boss to any keep state queue boss_ext # правила фильтрации входящего трафика на интерфейсе dc0 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 # правила фильтрации исходящего трафика на интерфейсе dc0 pass out on dc0 from dc0 to $int_nets # правила фильтрации входящего трафика на интерфейсе fxp1 pass in on fxp1 proto { tcp, udp } from $wwwserv to any port 53 \ keep state # правила фильтрации исходящего трафика на интерфейсе fxp1 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. Адрес источника транслируется не в отдельный адрес, а в адрес взятый при помощи одного из перечисленных выше методов из пула. Это может оказаться очень полезным в случае. когда пакетный фильтр транслирует адреса для очень большой сети. Так как число одновременных NAT соединений на один внешний адрес ограниченно, выделение для этих целей пула адресов позволит значительно увеличить число пользователей. В следующем примере пул из двух адресов используется для трансляции исходящих пакетов. Для каждого исходящего соединения пакетный фильтр производит ротацию адресов методом round-robin: nat on $ext_if inet from any to any -> { 192.0.2.5, 192.0.2.10 } Существенным недостатком этого метода будет то, что не всегда будет соблюдаться соответствие между исходным адресом и адресом трансляции. Это может вызвать проблему при заходе на web-узлы: они не смогут корректно обрабатывать информацию о сессиях. Решением этой проблемы может стать использование метода 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) как адреса трансляции
для исходящих пакетов. Каждый внутренний адрес будет всегда
транслироваться в свой внешний адрес, так как указано
ключевое слово Пулы адресов также могут использоваться для балансировки нагрузки входящих подключений. Для примера, входящие подключения на 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).
Совместное использование
В качестве дополнительной информации необходимо указать
адреса маршрутизаторов для каждого Интернет-соединения. Это
нужно для опции Следующий пример иллюстрирует балансировку нагрузки между двумя каналами: 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
Опция Для гарантии того, что пакеты с $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 для исходящих соединений на каждом внешнем интерфейсе 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 out on $int_if from any to $lan_net # пропускаем (quick) пакеты предназначенные самому шлюзу pass in quick on $int_if from $lan_net to $int_if # балансировка исходящего tcp трафика идущего из внутренней сети 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 # балансировка исходящего icmp и udp трафика идущего из внутренней сети 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 # основные "выпускаюшие" правила на внешнем интерфейсе 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 # маршрутизация пакетов идущих с любого IP на $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 Маркирование пакетов — способ пометить пакет внутренним идентификатором для дальнейшего использования в качестве критерия в правилах трансляции и фильтрации. Маркирование позволяет создать «доверие» между интерфейсами, а так же помогает оределить был ли пакет обработан правилами трансляции. Также становится возможным переход от фильтрации, основанной на правилах, к фильтрации, основанной на политиках.
Для присвоения маркера используется ключевое слово pass in on $int_if all tag INTERNAL_NET keep state
Маркер Маркер может быть присвоен с использованием макросов, например: name = "INTERNAL_NET" pass in on $int_if all tag $name keep state Существует набор предопределённых макросов, которые можно использовать для этих целей:
Эти макросы определяются во время загрузки, а не во время работы. Маркирование подчиняется следующим правилам:
Рассмотрим следующий пример: pass in on $int_if tag INT_NET keep state
В правилах nat, rdr и binat тоже можно метить пакеты при
помощи ключевого слова
Для проверки установленных маркеров используется ключевое
слово pass out on $ext_if tagged INT_NET keep state
Это правило соответствует исходящим пакетам на интерфейсе
pass out on $ext_if ! tagged WIFI_NET keep state
В правилах nat, rdr и binat так же допускается использование
ключевого слова Фильтрация пакетов на основе политик несколько отличается от фильтрации на основе правил. В политиках устанавливаются правила, по которым некоторый вид трафика должен быть пропущен, а некоторый запрещён. Пакеты классифицируются внутри политик на основе традиционных критериев — IP адреса источника/назначения, протокола и т.д. Рассмотрим следующий пример:
Заметьте, что политики охватывают весь трафик идущий через брандмауэр. В круглых скобках указаны маркеры используемые для данной политики. Правила фильтрации и трансляции: rdr on $ext_if proto tcp from <spamd> to port smtp \ tag SPAMD -> 127.0.0.1 port 8025 nat on $ext_if tag LAN_INET_NAT tagged LAN_INET -> ($ext_if) 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 Таким образом, мы установили какой трафик соответствует какой политике. Теперь разрешим проход трафика принадлежащего политикам SPAMD, LAN_INET_NAT, LAN_DMZ и INET_DMZ: pass in quick on $ext_if tagged SPAMD keep state pass out quick on $ext_if tagged LAN_INET_NAT keep state pass out quick on $dmz_if tagged LAN_DMZ keep state pass out quick on $dmz_if tagged INET_DMZ keep state
Теперь все политики определены. Если мы захотим добавить
POP3/SMTP сервер в DMZ, нам надо будет добавить следующие
строки в 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 <spamd> persist file "/etc/spammers" # классификация пакетов основанная на определённых в брандмауэре # политиках rdr on $ext_if proto tcp from <spamd> to port smtp \ tag SPAMD -> 127.0.0.1 port 8025 nat on $ext_if tag LAN_INET_NAT tagged LAN_INET -> ($ext_if) 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 # применение политик -- фильтрация на основе опрелелённых в брандмауэре # политиках pass in quick on $ext_if tagged SPAMD keep state pass out quick on $ext_if tagged LAN_INET_NAT keep state pass out quick on $dmz_if tagged LAN_DMZ keep state pass out quick on $dmz_if tagged INET_DMZ keep state
Если машина работает как мост на канальном уровне (в
OpenBSD см. bridge(4)
в FreeBSD
if_bridge(4)) пакетный фильтр может
маркировать кадры ethernet. При создании правил трансляции
при помощи команды brconfig(8)
(характерна для OpenBSD) можно установить
маркер с помощью опции
Далее можно ссылаться на этот маркер в
pass in on fxp0 tagged USER1
Журналирование в пакетном фильтре осуществляется при помощи
демона pflogd(8) слушающего сетевой
интерфейс
Для журналирования пакета надо поместить ключевое слово
Ключевому слову
Опции указываются в круглых скобках после ключевого слова
pass in log (all) on $ext_if inet proto tcp to $ext_if port 22 keep state Это правило помещает в журнал все входящие пакеты, идущие на 22-й порт. Журнальный файл записанный pflogd(8) имеет бинарный формат, его нельзя читать при помощи текстового редакора. Он предназначен для чтения утилитой tcpdump(1) (или другой программой скомпилированной с поддержкой библиотеки libpcap, например wireshark(1)). Для просмотра журнального файла выполните команду
Для просмотра журнала в режиме реального времени:
Поскольку pflogd(8) сохраняет данные в формате tcpdump(1), при просмотре данных журнала можно использовать правила фильтрации tcpdump(1). Например, для просмотра данных касающихся некоторого конкретного порта можно применять команду:
Этот пример можно слегка изменить, чтобы ограничить фильтрацию некоторым конкретным хостом:
Та же идея может использоваться при чтении данных из
устрйства
Правила фильтрации в tcpdump(1) специально расширены для взаимодействия с pflogd(8). В данной работе синтаксис правил tcpdump(1) подробно рассмотрен в Раздел 6.11, «Демонстрация основных навыков работы с утилитой tcpdump(1)», в том же разделе рассматриваются дополнительные правила tcpdump(1) для работы с журналом pflogd(8). Ещё один пример:
В этом примере мы в режиме реального времени следим за
входящими пакетами блокирующимися на интерфейсе Во многих случаях желательно вести журнал брандмауэра в текстовом формате и отправлять данные журнала на внешний журнальный сервер. Увы, никакого естественного метода для этого документация по пакетному фильтру нам не предлагает. Всё что нам предлагают, это раз в 5 минут пропускать бинарный файл через pipe:
Пошаговое HOWTO для направления данных журнала через logger(1) демону syslogd(8) можно найти на сайте разработчиков пакетого фильтра. Я не планирую пересказывать эту методику в данной книге, так как считаю её увечной. Насколько большую полосу пропускания может обслуживать пакетный фильтр? Насколько мощный компьютер мне нужен для обслуживания соединений с Интернет? Простого ответа на эти вопросы нет. Для некоторых целей будет достаточно процессора 486/66 с парой хороших сетевых карт ISA, при этом будет происходить трансляция NAT и обрабатываться трафик до 5 Mbps. Для других целей может не будет хватать даже более быстрой машины с более мощными сетевыми интерфейсами. Оценивать следует не количество байт в секунду, а количество пакетов в секунду. Производительность пакетного фильтра определяется несколькими величинами:
Часто люди спрашивают про benchmark для пакетного фильтра. Benchmark для вашей системы с вашим окружением можете сделать только вы. Пакетный фильтр используют на некоторых очень больших системах с очень большим трафиком. Разработчики — люди компетентные. Это не плохая для вас новость. Подробнее вопросы производительности пакетного фильтра изложены в работе Дэниэла Хартмайера [url://Hartmeier-2006-en], [url://Hartmeier-2006-ru]. FTP это протокол, который создавался, когда Интернет был маленький и все в нём друг друга знали. Нужды в фильтрации трафика в те годы не было, поэтому FTP сконструирован без оглядки на брандмауэры и трансляцию NAT. Существует два режима функционирования FTP: пассивный и активный. Выбор между ними, это выбор между тем, у кого будут проблемы с фильтрацией трафика. Если вы хотите, чтобы ваши пользователи были счастливы, вам придётся приспособиться к обоим режимам. Активный FTP. Когда клиент посылает серверу команду о передаче данных, сервер «ведёт себя активно» — открывает соединение к клиенту. Клиент выбирает случайный верхний порт и сообщает его серверу, последний открывает соединение на указанный порт клиента для передачи данных. Такое поведение затрудняет работу NAT: FTP сервер пытается открыть соединение с машиной, на которой работает NAT, а он просто не знает, что делать с этими соединениями. Пассивный FTP. При передаче данных сервер выбирает случайный верхний порт и сообщает его клиенту. Клиент открывает соединение на указанный верхний порт сервера, и по этому соединению передаются данные. Такая практика не всегда возможна и не всегда желательна, так как брандмауэр может блокировать трафик идущий на эти случайные порты. Зато с NAT с клиентской стороны, напротив нет никаких проблем, так как все соединения идут от клиента к серверу.
Для тестирования можно применять программу
ftp(1). В всех системах
BSD эта программа по умолчанию работает в
пассивном режиме. В активный режим её можно перевести с
использованием аргумента Как сказано выше протокол FTP плохо взаимодействует с брандмауэрами и NAT. Для решения этой проблемы пакетный фильтр предлагает воспользоваться прокси сервером FTP. Этот процесс пропускает FTP трафик добавляя и удаляя правила в пакетный фильтр при помощи системы якорей. Демон ftp-proxy(8) используется пакетным фильтром в OpenBSD 3.9. В старых версиях существовал другой демон с тем же названием.
Чтобы использовать его, поместите следующие строки в раздел
NAT файла nat-anchor "ftp-proxy/*" rdr-anchor "ftp-proxy/*" rdr on $int_if proto tcp from any to any port 21 -> 127.0.0.1 port 8021 Первые два правила создают пару якорей, которые будут использоваться демоном ftp-proxy(8) для добавления и удаления правил, т.е. для управления трафиком FTP. Последняя строка перенаправляет трафик FTP от клиентов программе ftp-poxy(8), которая будет слушать порт 8021. Вам так же понадобится якорь в области фильтрации: anchor "ftp-proxy/*"
Вам так же понадобится сделать так, чтобы демон
ftp-proxy(8) стартовал при запуске
системы. Для этого в OpenBSD надо в файл
ftpproxy_flags="" Можно запустить программу вручную, чтобы не перезагружать машину.
Для работы активного FTP вам понадобится ключ
В FreeBSD и NetBSD
функциональность ftp-proxy(8) ниже, чем
в OpenBSD. Фактически, в этих системах
пока применяется устаревшая версия
ftp-proxy(8).
Вместо ftp-proxy(8) в этих системах
можно использовать порт
В FreeBSD
ftp-proxy(8) запускается из
суперсервера inetd(8). Для этого в
int_if = "xl0" rdr pass on $int_if proto tcp from any to any port 21 -> 127.0.0.1 port 8021
Затем мы конфигурируем inetd(8) так,
чтобы он начал слушать порт 8021. Для этого в файл
ftp-proxy stream tcp nowait root /usr/libexec/ftp-proxy ftp-proxy Таким образом, ftp-proxy(8) пробрасывает командный канал FTP, а для передачи данных необходимо добавить разрешающие правила в брандмауэр: block in on $ext_if proto tcp all pass in on $ext_if inet proto tcp from any to $ext_if \ port > 49151 keep state
Это правило разрешает подключение к портам от 49151 до
65535. Программе ftp-proxy(8) можно
задавать диапазоны портов при помощи опций
Надо признать, что вариант с динамическими правилами,
работающий в OpenBSD выглядит разумнее.
Остаётся надеяться, что в будущем он будет портирован в
FreeBSD, а пока, повторюсь, мы можем
использовать порт
При функционировании пакетного фильтра и FTP сервера на
одной машине достаточно разрешить в обе стороны коннекты в
диапазоне портов от 49151 до 65535. Этот диапазон портов
используется по умолчанию системным FTP серврером
ftpd(8). Диапазон портов можно
регулировать в FreeBSD при помощи
переменных ядра pass in on $ext_if proto tcp from any to any port 21 keep state pass in on $ext_if proto tcp from any to any port > 49151 keep state В этом случае брандмауэр должен перенаправлять FTP трафик сервера и не блокировать нужные порты. Для этого мы вновь приходим к необходимости использовать ftp-proxy(8). ftp-proxy(8) может быть запущен в режиме перенаправления всего FTP трафика на один сервер FTP. Обычно мы настраиваем брандмауэр так, чтобы он слушал порт 21 и пробрасываем трафик на внутренний сервер FTP: ftpproxy_flags="-R 10.10.10.1 -p 21 -b 192.168.0.1" Здесь 10.10.10.1 — адрес сервера FTP, а работает он на порту 21. 192.168.0.1 — внешний адрес брандмауэра, к которому мы привязываем программу ftp-proxy(8).
Правила ext_ip = "192.168.0.1" ftp_ip = "10.10.10.1" nat-anchor "ftp-proxy/*" nat on $ext_if inet from $int_if -> ($ext_if) rdr-anchor "ftp-proxy/*" pass in on $ext_if inet proto tcp to $ext_ip port 21 \ flags S/SA keep state pass out on $int_if inet proto tcp to $ftp_ip port 21 \ user proxy flags S/SA keep state anchor "ftp-proxy/*"
Опция
authpf(8) — пользовательская
оболочка для авторизации на шлюзе. При использовании этой
программы шлюз работает как обычный маршрутизатор, но
пропускает пользовательский трафик только если пользователь
аутентифицировался на нём. Если пользовательская оболочка
выставлена в
authpf(8) загружает правила фильтрации и
трансляции используя уникальный для каждого пользователя
якорь. Название
якоря является комбинацией из имени пользователя и PID
экземпляра authpf(8) в формате main_ruleset/authpf/username(PID) Правила, которые может загрузить authpf(8) могут быть индивидуальными для каждого пользователя, а могут быть глобальными. Вот примеры использования authpf(8):
Информация об аутентифицировавшихся пользователях журналируется через syslogd(8). Это позволяет администратору вычислять кто когда пользовался Интернетом и вычислять какой пользователь использовал большее количество трафика. Полностью конфигурирование authpf(8) описано в man(1) странице по authpf(8).
В системе должен существовать конфигурационный файл
В этом файле могут присутствовать следующие две опции:
authpf(8) помещается в основной набор
правил при помощи правила nat-anchor "authpf/*" rdr-anchor "authpf/*" binat-anchor "authpf/*" anchor "authpf/*" Пакетный фильтр перейдёт к изучению правил входящих в эти якоря там, где они расположены. Нет нужды всегда использовать все якоря. Например, если authpf(8) не настроен на то, чтобы осуществлять трансляцию NAT, то соответствующий якорь не нужен. authpf(8) подгружает правила из файлов
Сперва ищется первый, потом второй, если существуют оба — используется только один. Таким образом, в первом файле должны находится правила характерные для конкретного пользователя и они будут перебивать глобальные правила заданные во втором файле. Как минимум один файл должен существовать, иначе authpf(8) не запустится. Правила трансляции такие же как и в основном файле, однако определены два макроса:
Рекомендуется использовать
В добавок к макросу table <authpf_users> persist pass in on $ext_if proto tcp from <authpf_users> \ to port smtp flags S/SA keep state Эта таблица должна использоваться в правилах, которые относятся ко всем аутентифицировавшимся пользователям.
Пользователям можно запретить пользоваться
authpf(8) для этого надо создать файл в
каталоге
Кроме того, в файле
Если authpf(8) не может определить
разрешено польователю входить или нет, он печатает
предупреждение и не пускает пользователя.
Когда пользователь успешно аутентифицируется в системе, ему будет напечатано приветственное сообщение Hello charlie. You are authenticated from host "64.59.56.140"
Это сообщение можно дополнить сообщением из файла
Чтобы authpf(8) заработал, его надо сделать оболочкой пользователя. Когда пользователь успешно зайдёт в систему через ssh(1), authpf(8) будет запущен в качестве оболочки. Он проверит можно ли пользователю его использовать, загрузит нужные правила и т.д. Есть два способа назначить пользователю authpf(8) в качестве оболочки:
В системе, где есть одновременно обычные пользователи и пользователи для authpf(8) удобно создать для последних специальный класс. Это позволит сделать разные политики для разных пользователей. Например, в классе можно указать:
Класс создаётся в файле
authpf:\ :welcome=/etc/motd.authpf:\ :shell=/usr/sbin/authpf:\ :tc=default:
После редактирования файла
Назначить пользователю класс можно разными способами. Например: chsh(1), pw(8), vipw(8). Чтобы просмотреть список пользователей из класса authpf, можно выполнить такую команду:
После того, как пользователь успешно вошёл в систему, authpf(8) меняет заголовок процесса, помещая в него имя пользователя и IP адрес с которого он пришёл:
Послав этому процессу сигнал
Пусть пакетный фильтр используется на машине
OpenBSD, которая является шлюзом для
беспроводной сети, которая является частью большой
университетской сети. Если пользователь аутентифицировался,
и он не находится в списке запрещённых пользователей
(
Файл wifi_if = "wi0" pass in quick on $wifi_if proto tcp from $user_ip to port { ssh, http, \ https } flags S/SA keep state Администратор charlie должен иметь доступ к SMTP, POP3, а так же web и SSH.
Файл
wifi_if = "wi0" smtp_server = "10.0.1.50" pop3_server = "10.0.1.51" pass in quick on $wifi_if proto tcp from $user_ip to $smtp_server \ port smtp flags S/SA keep state pass in quick on $wifi_if proto tcp from $user_ip to $pop3_server \ port pop3 flags S/SA keep state pass in quick on $wifi_if proto tcp from $user_ip to port { ssh, http, \ https } flags S/SA keep state
Основной файл с правилами, расположенный в
# macros wifi_if = "wi0" ext_if = "fxp0" dns_servers = "{ 10.0.1.56, 10.0.2.56 }" table <authpf_users> persist scrub in all # filter block drop all Правила очень просты. Вот, что они значат:
Основная идея в том, чтобы заблокировать всё и открыть
настолько мало, насколько это возможно. Трафик может
свободно покидать внешний интерфейс, однако он заблокирован
политикой default deny на внутреннем (wi0) интерфейсе. Когда
пользователь аутентифицируется, его трафик оказывается
разрешён для прохода на внутреннем интерфейсе и, таким
образом, проходит наружу. Ключевое слово CARP — Common Address Redundancy Protocol (Общий Протокол Избыточных Адресов, я не знаю есть ли общепринятый перевод на русский язык). Основная задача протокола — дать возможность различным хостам в локальной сети использовать общий IP адрес. CARP является свободной и безопасной альтернативой протоколам VRRP (Virtual Router Redundancy Protocol, см. [RFC-3768]) и HSRP (Hot Standby Router Protocol, см. [RFC-2281]). К сожалению, на этот протокол не опубликовано RFC. Кроме того, уже сужествует другой протокол с тем же названием: (Cache Array Routing Protocol, [RFC-3040] — протокол используемый Microsoft ISA).
CARP позволяет группе хостов использовать общий IP адрес. Эта группа хостов называется «избыточная группа» (redundancy group). Избыточной группе присваивается общий адрес, затем, среди её членов назначается «мастер» и запасные машины (backup). Мастер, это та машина, которой в данный момент принадлежит общий адрес IP. Он отвечает на ARP запросы, обращённые к этому адресу. Каждый хост может принадлежать более чем к одной «избыточной группе». Один из способов использования CARP — построение избыточных брандмауэров. Виртуальный IP адрес, принадлежащий группе, указывают клиентам в качестве маршрута по умолчанию. Если брандмауэр оказывается недоступен, IP адрес переходит к другой, запасной машине в группе и работа сети продолжается.
Мастер-хост группы регулярно посылает оповещения в локальную
сеть, чтобы запасные хосты знали, что он ещё жив. Если
запасной хост в течении некоторого времени не получает
уведомления от мастера, то он может принять на себя
обязанности мастера. (Какой именно — зависит от
значения В одной локальной сети может находиться несколько групп CARP, так как в оповещении, рассылаемом мастером присутствует Virtual Host ID, по которому запасные машины могут понять какой группе адресовано оповещение. Чтобы предотвратить рассылку поддельных оповещений CARP, каждая группа может быть сконфигурирована с паролем. Каждый пакет CARP снабжается этим паролем в виде SHA1 HMAC. CARP рассылается при помощи собственного протокола сетевого уровня (номер 112, официально не утверждён IANA) и ему нужно отдельное правило в брандмауэре: pass out on $carp_dev proto carp keep state
Здесь
Каждая группа CARP представлена
виртуальным сетевым интерфейсом
Поведением CARP пожно управлять через sysctl(8). Вот некоторые переменные ядра: Приведённые ниже переменные ядра работают как в OpenBSD, так и в FreeBSD:
Дополнительную информацию можно получить из руководства
man(1) по Пример конфигурации CARP:
Чтобы просмотреть состояние интерфейса
Интерфейс
По умолчанию
Когда
При обмене сообщениями pass on $sync_if proto pfsync
Здесь
Поскольку ifconfig <pfsyncN> syncdev <syncdev> [syncpeer <syncpeer>]
Пример:
Эта команда включает Совместное использование CARP и pfsync позволяет создать два и более брандмауэра и объединить их в устойчивый полнофункциональный кластер. При этом CARP реализует отказоустойчивую систему, а pfsync позволяет синхронизировать данные таблиц состояния, так, что при отказе мастера, запасная машина берёт на себя его функции и при этом не обрывает имеющиеся соединения. Например, пусть у нас имеется два брандмауэра — fw1 и fw2: +----| WAN/Internet |----+ | | | общий адрес | | +----192.0.2.100-----+ | | | | | 192.0.2.1:em2|/ \|em2:192.0.2.2 +-----+ 10.10.10.2+-----+ | fw1 |-em1----------em1-| fw2 | +-----+10.10.10.1 +-----+ 172.16.0.1:em0|\ /|em0:172.16.0.2 | | | | | +----172.16.0.100----+ | | общий адрес | | | ---+-------Shared LAN-------+--- Брандмауэры соединены «спина к спине» через кросс через интерфейсы em1. Оба соединены с локальной сетью при помощи интерфейсов em0 и с внешней сетью через интерфейсы em2. Адреса показаны на схеме. Мастер — fw1. Конфигурируем fw1: Выполняем необходимые настройки в ядре: Конфигурируем fw2: Выполняем необходимые настройки в ядре:
Некоторые распространённые проблемы при использовании
CARP и
И
Файл inet 172.16.0.100 255.255.255.0 172.16.0.255 vhid 1 carpdev em0 \ pass lanpasswd
Файл up syncdev em1
В FreeBSD вам понадобятся следующие
строки в ifconfig_carp1="172.16.0.100/24 vhid 1 carpdev em0 pass lanpasswd" ifconfig_pfsync0="up suncdev em1" Иногда бывает нужно перенести мастера с одного хоста на другой. Например, надо останивить мастера для профилактических работ или для отладки. Здесь обсуждается как это сделать не вредя имеющимся коннектам, чтобы пользователи ничего не заметили.
Для этих целей вы можете остановить интерфейс
Другой вариант состоит в том, что вы увеличиваете значение
Третий вариант состоит в том, чтобы управлять
CARP'ом при помощи переменной
Переменная
Здесь показано значение ассоциированное с группой Рассмотрим такой пример: имеется два брандмауэра с запущенным CARP со следующими CARP интерфейсами:
Задача состоит в том, чтобы перенести группы Для начала присвоим эти интерфейсы группе «internal»:
Теперь увеличим счётчик
Теперь CARP передаст полномочия по
интерфейсам Чтобы вернуть полномчия обратно надо выполнить команду:
Некоторые сетевые демоны, такие как
OpenBGPD и
sasyncd(8) используют счётчик
Фильтр должен работать на физическом
интерфейсе, не на виртуальном интерфейсе pass in on fxp0 inet proto tcp from any to carp0 port 22
Если вы замените
В данном примере пакетный фильтр используется как брандмауэр и NAT в домашней сети или небольшом офисе. Задача состоит в том, чтобы обеспечить доступ из внутренней сети в Интернет, обеспечить ограниченный доступ к брандмауэру из Интернета и обеспечить доступ к внутреннему web-серверу из Интернет. Топология сети: WWW-server [ COMP1 ] [ COMP3 ] | | ---+------+-----+------- xl0 [ BSD ] fxp0 -------- ( Internet ) | [ COMP2 ] Во внутренней сети есть некоторое количество компьютеров. Сколько именно — неважно. У нас на диаграмме показано три. На компьютере COMP3 работает небольшой web-сервер. Внутренняя сеть 192.168.0.0/24
Брандмауэр работает на OpenBSD (или любой
другой BSD) на процессоре
Celeron 300MHz с двумя сетевыми картами: 3com 3c905B
(
Мы считаем, что машина сконфигурирована как шлюз (т.е. включён проброс пакетов с интерфейса на интерфейс) и пакетный фильтр включён. О том как это делать написано в Раздел C.1, «Введение в работу с пакетным фильтром OpenBSD». Определяем макросы для облегчения управления брандмауэром и упрощения набора правил: ext_if="fxp0" int_if="xl0" tcp_services="{ 22, 113 }" icmp_types="echoreq" comp3="192.168.0.3" Первые две строки определяют сетевые интерфейсы брандмауэра. Если нам придётся перенести брандмауэр с машины на машину, нам надо будет изменить только эти две строчки, остальные правила будут по прежнему работоспособны. Третья и червёртая строки определяют номера открытых портов и типы ICMP сообщений принимаемых шлюзом. В последней строке задан адрес машины COMP3.
Следующие две опции определяют поведение системы при блокировании пакетов и включают журналирование для внешнего интерфейса: set block-policy return set loginterface $ext_if Каждая UNIX-система имеет кольцевой интерфейс, необходимый для того, чтобы программы могли общаться друг с другом при помощи сетевых протоколов внутри машины. Фильтрацию на кольцевом интерфейсе лучше отключить — чтобы не мешать работе программ. set skip on lo
Нет причин не использовать рекомендованные алгоритмы нормализации трафика. Поэтому нормализация, это всего одна строка: scrub in Следующее правило используется для трансляции NAT всей внутренней сети: nat on $ext_if from !($ext_if) to any -> ($ext_if)
Мы можем заменить Круглые скобки вокруг интерфейса стоят потому, что адрес ему назначается динамически. Для работы FTP-прокси, добавим якорь: nat-anchor "ftp-proxy/*" Во-первых, нам понадобится правило перенаправления для работы ftp-proxy(8), чтобы клиенты из локальной сети могли пользоваться FTP: rdr-anchor "ftp-proxy/*" rdr on $int_if proto tcp from any to any port 21 -> 127.0.0.1 port 8021
Заметим, что здесь будет перенаправляться только FTP с
управляющим каналом на порту 21. Если пользователи регулярно
используют другие порты, следует использовать список: Во-вторых, нам надо перенаправить все входящие соединения на 80-й порт машины COMP3. rdr on $ext_if proto tcp from any to any port 80 -> $comp3 Для начала определяем политику default deny: block in Здесь мы блокировали весь входящий трафик, даже из внутренней сети. Теперь мы будем по очереди описывать правила пропускающие нужный трафик. Имейте ввиду, что пакетный фильтр может блокировать отдельно входящий трафик и отдельно исходящий. Мы с вами блокировали весь входящий трафик, но если пакет уже вошёл (т.е. он соответствовал разрешающему правилу, которое будет записано ниже), то мы можем его выпустить: pass out keep state Нам понадобится якорь для ftp-proxy(8): anchor "ftp-proxy/*" Неполохо так же защититься от спуфинга: antispoof quick for { lo $int_if } Теперь откроем порты, используемые сервисами, котрые должны быть доступны из Интернет. Для начала, трафик предназначеный самому брандмауэру: pass in on $ext_if inet proto tcp from any to ($ext_if) \ port $tcp_services flags S/SA keep state
Благодаря указанию портов в макросе Далее надо пропустить трафик идущий на машину COMP3, который прошёл трансляцию rdr: pass in on $ext_if inet proto tcp from any to $comp3 port 80 \ flags S/SA synproxy state Чтобы немного увеличить безопасность web-сервера мы используем TCP SYN Proxy. Пропускаем трафик ICMP: pass in inet proto icmp all icmp-type $icmp_types keep state
Аналогично макросу Теперь мы пропускаем весь трафик из внутренней сети. Мы предполагаем, что пользователи во внутренней сети знают что они делают и это не вызовет проблем. Однако во многих случаях это не так и понадобятся более жёсткие ограничения. pass in quick on $int_if
Трафик TCP, UDP и ICMP может покидать машину благодаря
правилу « # макросы ext_if="fxp0" int_if="xl0" tcp_services="{ 22, 113 }" icmp_types="echoreq" comp3="192.168.0.3" # опции set block-policy return set loginterface $ext_if set skip on lo # нормализация трафика scrub in # nat/rdr nat on $ext_if from !($ext_if) -> ($ext_if:0) nat-anchor "ftp-proxy/*" rdr-anchor "ftp-proxy/*" rdr pass on $int_if proto tcp to port ftp -> 127.0.0.1 port 8021 rdr on $ext_if proto tcp from any to any port 80 -> $comp3 # Фильтрация block in pass out keep state anchor "ftp-proxy/*" antispoof quick for { lo $int_if } pass in on $ext_if inet proto tcp from any to ($ext_if) \ port $tcp_services flags S/SA keep state pass in on $ext_if inet proto tcp from any to $comp3 port 80 \ flags S/SA synproxy state pass in inet proto icmp all icmp-type $icmp_types keep state pass quick on $int_if
Главная > Операционные системы > UNIX |