Процедура сетевой загрузки очень чувствительна к качеству сети. 20 клиентов грузят по сети с сервера ядро и стартовый диск, потом с сервера монтируют исошку и начинает по ней работать. И вот у нас их 20. Представим как будет рабоать такая схема, если сеть 10 мегабитная. Или если 100 мегабитная, но пакетлосс 1 промилле. Будет это работать хорошо или нет?
Поэтому лектор между двумя виртуалками вставил третью, на которой FreeBDS, два интерфеса собранные в бридж, а между ними dummynet, которая иммитирует сеть с возможностью задать пропускную способность, задержку, пакетлосс рейт. Для того чтобы оттестировать решение, которое идеально работает на двух виртуалках в условиях, приближенных к боевым, удобно иметь такую штуку.
История создания системы pf восходит к началу нынешнего тысячелетия и она такая. Существовал неплохой фаерволл под названием ipfilters и он очень неплохо работал, хотя это был древнючий проект. пока внезапно автор не обнаружил, что его прекрасный фаерволл включен в состав разных бзд систем, его модифицируют и распространяют, хотя в лицензии не было написано, что это можно. Автор заартачился, и втечение нескольких недель в исходных кодах опенбсд не было вообще никакого фаерволла. Нашелся швейцарец, который тогда экспериментировал с сетевым стеком бсд, и его код радостно влился в бзд и там радостно развивается.
Какие признаки межсетевого экрана на базе пф? Традиционная схема -- ядро + утилита pfctl. Это с точки зрения системного администратора. На самом деле между ядром и утилитой существует достаточно богатое апи. Вы можете взять и написать программу, которая пользуется ядерным апи для того чтобы туда ходить, модифицировать и работать с структурами фаерволла в ядре.
Главная идея -- принцип last wins. В чем привлекательны и пугающие особенности этого принципа?
Принцип, согласно которому, пришедший пакет прогонятеся сквозь все правила и применившееся последним выполняется, намекает, что если прогоняние пакета по таблице правил реализовано неэффективно -- оно будет работать медленно всегда.
Предположительно из принципа last wins следует медленная работа в случае неэффективной реализации или большого количества правил.
С другой стороны -- мы можем считать время обработки правил фаерволл если не фиксированным, то ограниченным сверху. В случае ипфв это не совсем верно. Там мы в действительности не знаем -- пакет не вылез потому что его жуют правилоа, или потому что его выбросили. В случае пф есть такая определенность, потому что пакет обрабатывается всегда одинаковое время и только иногда быстрее.
Нетрудно догадаться, что в случае быстрого спосоставления пакета правилам должно быть в таком фаерволле реализовано.
Из этого следует забавная вещь. В отличие от ипфв,мы с большей или меньшей степенью риска мы можем вставлять автоматически сгенерированые большие своды правил. Мы можем надеяться на то, что даже большой свод правил будет работать хорошо.
Что для этого сделано в пфе? Оптимизация. Свод правил в таблице пф это не программа, а такой фильтр. И там нету логики передачи потока -- call,return. Мы можем на этот свод поглядеть и, например, объединить несколько правил в одно, если это одно перекрывает все остальные.
Быстрый поиск в таблице. В пфе таблица ип адресов это структура данных, которую можно модифицировать из юзерспейса не трогая правил вообще. На эту таблицу можно ссылаться из правил.
Модификация правил из юзерспейса -- рисковая операция. Мы добавляем 10 правил. У нас добавилось одно -- хороший у нас фаерволл или плохой?
Если это таблица с айпи адресами, то нормальная ситуация, что мы потихоньку убираем или добавляем туда. Плюс эти таблицы имеют логарифмическую сложность поиска. Это приводит к тому, что в качестве айпи адресов мы тоже можем добавлять большие генераты.
Anchor -- подмножества правил, которые находятся отдельно от основного свода. Пока вы подготавливаете якорь к модификации, фаерволл работает по старому якорю. И, вообще, весь остальной фаерволл не меняется совсем.
Первый принцип, очень важный -- команда равняется задаче.
ipfw построен по следующему принципу -- правило построено так: что мы можем сделать с пакетом? Например, подменить сетевой адрес. Пусть будет такое правило. А потом мы придумаем, для чего оно применимо.
Подход от задачи и хорош, и плох. Хорош тем, что в результате своды правил для пф будут очень наглядны. По своду правил пф сразу видно, что хотел сделать администратор, составивший этот свод.
Комплексное правило. Помните правило keep state? Оно позволяло генерировать свод временных правил. Что же касается правила keep state в пф, то оно решает _задачу_. Оно означает не препятствовать конкретному тцп соединению. Например, временное правило будет удаляться по приезду пакета FIN в данном соединении. Ещё тцп соединения могут обслуживать ICMP трафиком дополнительным. Стейт в понимании пф -- всё, что относится к данному тцп соединению.
То же касается UDP.
Чем этот подход плох? Написав правило, надо отдавать себе отчет, что такое стейт. То есть, знать надо чуть больше.
Тоже самое относится к нату.
Мы помним, что недостаточно подменить ип-адрес, надо еще отслеживать, к какому именно потоку данных относится пакет.
НАТ можно делать на разных основаниях, вплоть до прикладного уровня. Например, в UDP. Или в случае ICMP.
Еще одна ситуация, когда надо сделать действия сразу над кучей объектов.
Например, сразу над списком ип-адресов. Или над пачкой интерфейсов.
Или сделать операцию по допущению или недопущению трафика сразу по несокльким портам.
Для этого есть понятие список. Использование вместо одного элемента сразу нескольких. При этом когда из одного правила генерируется свод.
Макрос -- это любая строка "переменная=значение".
Все это служит идее написания правил таким образом, что бы правило решало задачу.
Ещё один механизм, очень важный. Для того чтобы существенно уменьшить количество писанины при оформлении свода правил -- механизм разумныхз умолчаний.
Мы с ним уже встречались, когда говрили про файл rc.conf, он маленький. И есть файл rc.default.conf, он очень большой.
Поскольку большие своды правил и матчинг относительно состояний работают достаточно быстро, было принято поведение keep state для tcp соединений сделать по умолчанию.
Вы не поверите, но это в общем увеличивает читабельность кода.
Есть две проблемы, связанные с хранением состояния. Проблема первая -- использование портов.
Мы должны произвести допуск подключения, но выясняется, что на машине, которая генерит подключение рандом очень плохо работает.
Syn Proxy -- ретрансляция тцп соединения с нашей машины.
Всё это некоторая настройка по умолчанию, задача, которая решается одной командой.
Еще один важный момент, которые может показаться слегка излишним при изучении пфа в сухую. Поскольку одна команда решает целую подзадачу, может случиться так, что параметры могут задаваться по разному.
Допустим, у нас есть задача фильтрации по айпи-адресам. Это делается одной командой фаерволла. Но. давайте посмотрим на реальную задачу в реальной жизни. Если мы блокируем некоторые сайты, то фильтрация по афпи -- плохая задача, надо фильтровать по доменному имени. А как ? А хорошо было бы иметь возможность вписать в правило фильтрацию по доменному имени и каждый раз происходил бы днс резолвинг. Да, это задача сложнее, чем фильтрация по айпишнику, но она существует, и встречается едва ли не чаще.
Есть мнение, что это правило бесполезно из-за использования вхостов и того, что днсы отдают не все айпишники.
В пфе, если задача требует какой-то другой формы представления данных довольно часто такая задача тоже решается.
Атомарность задачи. Например -- нормализация проходящего трафика. Scramp. Это довольно большой список действие -- удаление лишних полей, добавление нужных полей. Приходит нехороший пакет, уходит облагороженный. Это много действий, но поскольку одна задача, то одна команда скрамп.
Задача ос-фингерпринитинга. По тому, как устанавливается тцп соединение можно определить какая ос с той стороны.
Под этот же список попадает работа с очередями и трафик-шейпингом. Вообще говоря, трафик шейпинг имеет мало общего с понятием межсетевой экран на уровне ядра. Потому что в нем отсутствует большая часть того, что мы считаем механизмом релаизации межсетевого экрана -- ни прогоняние через фильтр, ни матчинга, ни модификации соежимого. Надо идентифицировать пакет, как принадлежащий такому то потоку данных и организовать прокачку этого потока данных с нужными нами характеристиками, и в случае, если с ним возникают какие-то проблемы-- например, нас слишком заливают, что-то сделать с этим потоком данных, как-то им поуправлять, например послать ECN, или выбросить один пакет чтобы схлопнуть окно. Всё это не походе на то, как работает типичный фаерволл уровня ядра 20-30 летней давностти. Именно поэтому в линуксе шейпингом и фильтрацией занимаются разные подсистемы.
Но, как мы понимаем, задача фаерволла сформулированная в начале лекций, он включает и шейпинг. и матчинг, и их сочетания (засунуть в трубу только конкретные пакеты).
Именно поэтому существовавшая отдельно реализация очередей была включена в пф и теперь все водном флаконе. Хотя в принципе эти механизмы могут бть реализованы в разных кусках ядра.
Один из главных недостатков пфа -- то, что он работает только на уровне сетвом и выше, на интерфейсном уровне никакого анализа трафика не существует.
Именно поэтому в фрибз до сих пор существует ипфв, потому что он позволяет это делать на уровне езернета.
Что еще осталось рассказать из такового, вот, общего, чтобы потом привести пример и его чуть-чуть прокомментировать? С такой дисциплиной мы встретимся еще в iptables.
Строго определена последовательность в которой правила разного вила веключаются в свод. Сначала идут options. Например, optimiztion agressive. Или optimization low latency.
Следующее -- оптимизация и нормализация. Всего пять видов.
- настройка
- нормализация
- очереди
- преобразование
- фильтрация
Такая дисциплина нужна только для того, чтобы мы сами не запутались. На эффективность порядок правил не влияет.
Несмотря на то, что у нас общая стратегия last wins, мы можем поставить модификатор quick, что означает. что больше этот пакет мы не проверяем.
Управляется все это с помощью pfctl. Она умеет
- Включать/выключать пф.
- Флашить правила и якоря. -F
- Показывать
- Загрузить свод правил из файла -f
- Не делать всего того, что собирались, а толлько проверить. Частая ситуация - -vnf
ext_if = "fxp0" init_if = "dc0" lan_nat="192.168.0.0/24" table <firewalls, const, self> set skip on lo0 match in all scrub nat on $ext_if inet from !($ext_if) to any->($ext_if) rdr pass on $ext_if prototcp from any to any port 80 -> 127.0.0.1 port 80 80 block all antispoof for $ext_if block return in quick on $init_if prototcp from !192.168.0.1 to $int_if port ssh pass in on $int_if from $lan_net pzss out on int_if to $lan_net pass out on $ext_if