NetBSD NPF часть №1

Опубликовано – 16.09.2014

  Уважаемые читатели, выкладываю вольный перевод главы «Configuration» руководства пакетного фильтра NPF.

  NPF является пакетным фильтром 3-го уровня и поддерживает протоколы IPv4 и IPv6. Так же, есть поддержка таких протоколов 4-го уровня, как TCP, UDP и ICMP. NPF предлагает традиционный для большинства пакетных фильтров набор возможностей, таких как пакетная фильтрация с учетом стостояния сессии, NAT, таблицы (с чьей помощью реализуются различные структуры данных, такие как «контейнеры»), поддержка расширений, нормализация пакетов, журналирование и т.д. При написании NPF основной упор был на создание высокопроизводительного дизайна, возможность обслуживания большого количества клиентов и использование многопроцессорных систем. Он написан с нуля в 2009 и опубликован под лицензией BSD.

  NPF использует Berkeley Packet Filter (BPF) байт-код, который «на лету» (JIT) скомпилирован в машинный код. Каждое правило описывается последовательностью низкоуровневых операций, применяемых к пакету. Преимуществом такого подхода является независимость от протокола, что позволяет осуществить поддержку новых протоколов (например, 7-го уровня) или шаблонов фильтрования на пользовательском уровне, без необходимости модификации самого ядра.

  В качестве главного интерфейса для реализации пользовательских расширений в NPF используются процедуры правил. Синтаксис конфигурационного файла поддерживает указание произвольных процедур и их параметров, согласно описания расширения. Расширение состоит из двух частей: динамического модуля (файл .so), поддерживающего утилиту npfctl(8) и модуля ядра (файл .kmod). Интерфейс ядра доступен для использования и стоит воздержаться от модификации кода NPF.

  Внутри NPF абстрагируется в четко определенные модули и следует строгим принципам сопряжения, что необходимо для обеспечения расширяемости. Взаимодействие между пространством пользователя и ядром осуществляется через библиотеку libnpf(3). Это удобно использовать при разработке расширений или сторонних приложений. Шлюз уровня приложений (Application level gateways (ALGs)), такой, как поддержка, например, traceroute(8), также абстрагировано в отдельном модуле.

  NPF перехватывает пакеты 3-го уровня на входе в IP-стек. Пакет может быть отброшен до проверки NPF если он имеет ошибки в заголовке IPv4 или IPv6 или других полях. Входящий пакет проходит через NPF до сборки фрагментов. При желании, сборку пакетов в NPF можно отключить.

  Обработка пакета происходит для каждого интерфейса, как входящий так и исходящий, через который проходит пакет. Поддержка обработки в режиме forwarding path и оптимизации fast-forward планируется в следующих версиях.

  Порядок выполнения в NPF выглядит следующим образом:

проверка состояния → проверка правила (если нет состояния) → трансляция адреса → процедура правил

Конфигурирование

  Первым шагом необходимо настроить сетевые интерфейсы в самой операционной системе.Начинающие пользователи NetBSD могут обратиться к rc(8), rc.conf(5), ifconfig.if(5) и другим страницам руководства. Вторым шагом создается файл конфигурации NPF (по умолчанию, /etc/npf.conf). Ниже представлен простой файл конфигурации,содержащий две группы для двух интерфейсов и группу по умолчанию. Для реализации более сложных конструкций необходимо изучить страницу руководства npf.conf(5).

srv# cat /etc/npf.conf
$ext_if = inet4(wm0)
$int_if = inet4(wm1)
table  type hash file "/etc/npf_blacklist"
table  type tree dynamic
$services_tcp = { http, https, smtp, domain, 6000, 9022 }
$services_udp = { domain, ntp, 6000 }
$localnet = { 10.1.1.0/24 }
alg "icmp"
# Note: if $ext_if has multiple IP address (e.g. IPv6 as well),
# then the translation address has to be specified explicitly.
map $ext_if dynamic 10.1.1.0/24 -> $ext_if
map $ext_if dynamic 10.1.1.2 port 22 <- $ext_if port 9022
procedure "log" {
      # Note: npf_ext_log kernel module should be loaded, if not built-in.
      # Also, the interface created, e.g.: ifconfig npflog0 create
      log: npflog0
}
group "external" on $ext_if {
      pass stateful out final all
      block in final from 
      pass stateful in final family inet4 proto tcp to $ext_if port ssh apply "log"
      pass stateful in final proto tcp to $ext_if port $services_tcp
      pass stateful in final proto udp to $ext_if port $services_udp
      pass stateful in final proto tcp to $ext_if port 49151-65535    # Passive FTP
      pass stateful in final proto udp to $ext_if port 33434-33600    # Traceroute
}
group "internal" on $int_if {
      block in all
      block in final from 
      # Ingress filtering as per BCP 38 / RFC 2827.
      pass in final from $localnet
      pass out final all
}

Управление

  Управление NPF осуществляется с помощью утилиты npfctl(8) или через систему NetBSD rc.d, используемую на этапе начальной загрузки. Ниже приведен пример для запуска NPF и загрузки конфигурации через скрипт rc.d:

srv# echo 'npf=YES' >> /etc/rc.conf
srv# /etc/rc.d/npf reload
Reloading NPF ruleset.
srv# /etc/rc.d/npf start
Enabling NPF.
srv# npfctl
Usage:  npfctl start | stop | flush | show | stats
        npfctl save | load | { reload | validate } []
        npfctl rule "rule-name" { add | rem } 
        npfctl rule "rule-name" rem-id 
        npfctl rule "rule-name" { list | flush }
        npfctl table  { add | rem | test } 
npfctl table { list | flush }

  Любые модификации npf.conf требуют перезагрузки правил, осуществляемой командой reload. Отличие от других пакетных фильтров заключается в поведении команд start и stop. Фактически, при использовании этих команд не происходит изменения (выгрузки/загрузки) активной конфигурации. Подача команды start только направляет пакеты через NPF, а stop останавливает их прохождение. Таким образом, сперва изменяется набор правил командой reload, а затем включается фильтрация командой start. Подобным образом выполняется очистка конфигурации, для этого необходимо выполнить последовательность из команд stop и flush. Таким образом пользователь может легко отключать и включать фильтрацию без изменения активной конфигурации.

Переменные

  Для облегчения читаемости и гибкости набора правил можно использовать переменные. Чаще всего, переменные используются для определения IP-адреса, сети, порта или интерфейса. Переменные могут содержать несколько элементов.

  В примере выше с помощью переменных объявлены интерфейсы: $ext_if и $int_if (знак $ является индикатором объявления переменной) которые далее могут быть использованы в конфигурационном файле.

  Некоторые функции могут быть применены к интерфейсам inet4() and inet6(), а частности извлечение IPv4 или IPv6 адреса.

Группы

  Иметь один огромный набор правил для всех интерфейсов и направлений может быть довольно неудобным, поэтому NPF требует описания привил внутри групп. Группы можно рассматривать как более высокую иерархию правил, содержащие подправила, при этом основными свойствами группы является интерфейс и направление движения. Пакеты, соответствующие критериям группы, передаются в набор правил этой группы. Если пакет не соответствует ни одной группе, то он передается в группу по умолчанию. Группа по умолчанию всегда должна быть определена.

  В данном примере, пакеты, проходящие через сетевой интерфейс WM0, будет проверяться по правилам группы «external» и, если не произойдет совпадения, то пакет уйдет на проверку правил группы по умолчанию. Соответственно, если пакет проходит WM1, используются правила группы «internal» и т.д. Если пакет не проходит ни через WM0 ни WM1, то группа по умолчанию будет проверяться в первую очередь.

  Важным аспектом для понимания чем являются группы (и ключевое слово on для регулярных правил) является понимание символического представления интерфейса, так как группы могут ссылаться на интерфейсы, которые не существуют в момент загрузки конфигурации. Когда интерфейс с указанным именем появится, для негоо станут действовать группы / правила фильтрации; когда интерфейс удаляется, они становятся неактивными. Поэтому NPF выполняет динамическую отслеживание пояаления/удаления интерфейсов.

Правила

  Правила являются важной частью конфигурации NPF, описывая критерии соответствия пакета и предпинимаемое над ним действие. В настоящее время NPF поддерживает следующие коитерии: интерфейс, направление, протокол, IP-адрес или сеть, порт TCP/UDP или диапазон портов, флаг TCP и тип/код ICMP. В качестве действия, пакет может быть пропущен или заблокировн.

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

  Правила, блокирующие пакет дополнительно могут иметь опции return-rst, return-icmp или return. Эти опции указывают NPF генерировать ответный TCP RST для TCP пакета и/или ICMP destination unreachable (administratively prohibited; type 3, code 13) ответ для UDP пакета. Однако, разрешения для этого ответного пакета должны быть прописаны в наборе правил, так как NPF не пропускает их просто потому, что сам их и создал. Такое поведение позволяет пользователям использовать процедуры правил для таких ответных пакетов.

  Дополнительно, NPF поддерживает возможности и синтаксис pcap-filter(7), например:

block out final pcap-filter "tcp and dst 10.1.1.252"

  С помощью этого механизма можно описать практически либой трафик.

Таблицы

  Общей проблемой является добавление или удаление в правилах большого количества IP-адресов. Перезагрузка конфигурации является относительно дорогой операцией и не подходит для потоковых изменений. Такая же проблема встает перед набором из большого количества правил со схожей логикой, но разными адресами. В этом случае на помощь приходит высокоэффективный механизм таблиц.

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

  NPFподдерживает следующие виды таблиц:

  • hash — в среднем предоставляет O(1) время поиска. Хорошо подходит для редко изменяемых таблиц.
  • cdb — гарантированно предоставляет O(1) время поиска. Идеален для совсем-совсем редко изменяемых таблиц.
  • tree — дает O(k) время поиска и поддержку префиксов (маски). Хороший выбор для частоизменяемых таблиц и требующих поиска по префиксам.

  Ниже приведен фрагмент использования двух таблиц:

table  type hash file "/etc/npf_blacklist"
table  type tree dynamic
group "external" on $ext_if {
        block in final from 
        pass stateful out final from 
}

  Статическая таблица «blacklist» загружается из файла (в данном случае /etc/npf_blacklist). Динамическая таблица первоначально пуста и наполняется после загрузки конфигурации. Наполнение и управление таблицами осуществляется через утилиту npfctl(8). Ниже представлен пример сброса таблицы и добавления/удаления записи:

srv# npfctl table "blacklist" flush
srv# npfctl table "permitted" add 10.0.1.0/24
srv# npfctl table "permitted" rem 10.0.2.1

  Стандартный для приложений интерфейс ioctl(2) так же позволяет управлять таблицами NPF.

Процедуры правил

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

  Конфигурационный файл достаточно гибок для написания процедур с использованием аргументов. Помимо проверки синтаксиса, утилита npfctl(8) проводит дополнительные проверки при загрузке конфигурации на предмет регистрации процедуры в ядре и правильности указания аргументов. Так же, есть несколько встроенных процедур, например для журналирования или нормализации трафика:

procedure "log" {
        log: npflog0
}
procedure "norm" {
        normalize: "random-id", "min-ttl" 512, "max-mss" 1432
}

  Нормализация трафика является набором различных механихзмов. В примере выше, процедура нормализации включает в себя следующее: IPv4 ID randomisation, Don’t Fragment (DF) flag cleansing, minimum TTL enforcement and TCP MSS «clamping».

  Для выполнения процедуры в правиле необходимо использовать назначенное ключевое слово:

group "external" on $ext_if {
        block in final from  apply "log"
}

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

Application Level Gateways

  Некоторые протоколы не совместимы с NAT и требуют трансляции за пределами 3-го и 4-го уровней. Наиболее общими случаями приложений являются: traceroute и FTP. В случае таких приложений, трансляции выполняются с помощью расширений пакетного фильтра, называемых application level gateways (ALGs).

  Поддержка traceroute (в случае использования и ICMP и UDP) встроена в NPF и требует лишь загрузки модуля добавлением следующей директивы в npf.conf:

alg "icmp"

  Так же, модуль ядра ALG может быть загружен вручную:

modload npf_alg_icmp

Динамические правила

  NPF имеет поддержку динамических правил, что подразумевает добавление и удаление правил из набора без необходимости перезагрузки всей конфигурации.

  Рассмотрим следующий фрагмент:

group default {
        ruleset "test-set"
}

  Динамические правила управляются с помощью npfctl(8):

$ npfctl rule "test-set" add block proto icmp from 192.168.0.6
OK 1
$ npfctl rule "test-set" list
block proto icmp from 192.168.0.6
$ npfctl rule "test-set" add block from 192.168.0.7
OK 2
$ npfctl rule "test-set" list
block proto icmp from 192.168.0.6
block from 192.168.0.7
$ npfctl rule "test-set" rem block from 192.168.0.7
$ npfctl rule "test-set" rem-id 1
$ npfctl rule "test-set" list

  Каждое правило получает уникальный номер, который отображается при добавлении правила. Как показано в примере выше, есть два способа удаления правила: использовать ID правила (команда rem-id) и использовать хэш правила (команда rem). Хэш подсчитывается по алгоритму SHA1. Хотя вероятность весьма мала, но при использовании этого способа удаления правил, могут возникать коллизии, поэтому рекомендуется удалять правила используя ID.

Stateful фильтрация

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

  Продолжение следует.

  Список используемой литературы:

NPF documentation




 Уважайте труд автора, сохраняйте копирайты.
Реклама на сайте висит не просто так и если статья Вам понравилась, с ее помощью Вы можете отблагодарить автора за проделанную работу. Спасибо!

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *


*