---
description: 'В базовой системе FreeBSD встроено три межсетевых экрана: PF, IPFW и IPFILTER. В этой главе рассматривается, как определять правила фильтрации пакетов, различия между встроенными в FreeBSD межсетевыми экранами и способы их использования'
next: books/handbook/advanced-networking
params:
  path: /books/handbook/firewalls/
part: 'IV. Сетевое взаимодействие'
prev: books/handbook/network-servers
showBookMenu: 'true'
tags: ["firewall", "pf", "ipfw", "ipfilter", "blocklistd", "filtering"]
title: 'Глава 33. Межсетевые экраны'
weight: 38
---

[[firewalls]]
= Межсетевые экраны
:doctype: book
:toc: macro
:toclevels: 1
:icons: font
:sectnums:
:sectnumlevels: 6
:sectnumoffset: 33
:partnums:
:source-highlighter: rouge
:experimental:
:images-path: books/handbook/firewalls/

ifdef::env-beastie[]
ifdef::backend-html5[]
:imagesdir: ../../../../images/{images-path}
endif::[]
ifndef::book[]
include::shared/authors.adoc[]
include::shared/mirrors.adoc[]
include::shared/releases.adoc[]
include::shared/attributes/attributes-{{% lang %}}.adoc[]
include::shared/{{% lang %}}/teams.adoc[]
include::shared/{{% lang %}}/mailing-lists.adoc[]
include::shared/{{% lang %}}/urls.adoc[]
toc::[]
endif::[]
ifdef::backend-pdf,backend-epub3[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]
endif::[]

ifndef::env-beastie[]
toc::[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]

[[firewalls-intro]]
== Обзор

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

Межсетевые экраны могут повысить безопасность узла или сети. Они могут использоваться для выполнения одной или нескольких следующих функций:

* Защищать и изолировать приложения, сервисы и машины внутренней сети от нежелательного трафика из общедоступного Интернета.
* Ограничивать или отключать доступ с хостов внутренней сети к сервисам публичного Интернета.
* Поддерживать преобразования сетевых адресов (NAT), что позволяет внутренней сети использовать частные IP-адреса и совместно использовать одно подключение к публичному интернету с помощью одного IP-адреса или общего пула автоматически назначаемых публичных адресов.

В базовой системе FreeBSD встроено три межсетевых экрана: PF, IPFW и IPFILTER, также известный как IPF. FreeBSD также предоставляет два инструмента для управления использованием пропускной способности: man:altq[4] и man:dummynet[4]. ALTQ традиционно тесно связан с PF, а dummynet — с IPFW. Каждый межсетевой экран использует правила для контроля доступа пакетов к системе FreeBSD и из неё, хотя подходы у них различаются, и каждый имеет свой собственный синтаксис правил.

FreeBSD предоставляет несколько межсетевых экранов для удовлетворения различных требований и предпочтений широкого круга пользователей. Каждый пользователь должен оценить, какой межсетевой экран лучше всего соответствует его потребностям.

Прочтите эту главу, чтобы узнать:

* Как определить правила фильтрации пакетов.
* Различия между межсетевыми экранами, встроенными в FreeBSD.
* Как использовать и настроить межсетевой экран PF.
* Как использовать и настроить межсетевой экран IPFW.
* Как использовать и настроить межсетевой экран IPFILTER.

Прежде чем читать эту главу, необходимо:

* Понимать основы FreeBSD и принципы работы Интернета.

[NOTE]
====
Поскольку все межсетевые экраны основаны на проверке значений выбранных управляющих полей пакетов, создатель набора правил межсетевого экрана должен понимать, как работает TCP/IP, какие значения содержатся в управляющих полях пакетов и как эти значения используются в обычном сеансе связи. Хорошее введение можно найти в http://www.ipprimer.com[TCP/IP Primer от Daryl].
====

[[firewalls-concepts]]
== Концепции межсетевого экрана

Набор правил содержит группу правил, которые пропускают или блокируют пакеты на основе значений, содержащихся в пакете. Двунаправленный обмен пакетами между хостами составляет сеанс взаимодействия. Межсетевой экран обрабатывает как пакеты, поступающие из общедоступного Интернета, так и пакеты, генерируемые системой в ответ на них. Каждая служба TCP/IP имеет свой протокол и порт прослушивания. Пакеты, предназначенные для конкретной службы, идут из исходного адреса с использованием непривилегированного порта и направляются на конкретный порт службы на адресе назначения. Все вышеуказанные параметры могут быть использованы в качестве критериев выбора для создания правил, которые будут пропускать или блокировать службы.

Для поиска неизвестных номеров портов обратитесь к [.filename]#/etc/services#. Или посетите https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers[https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers] и выполните поиск по номеру порта, чтобы узнать его назначение.

Ознакомьтесь с этой ссылкой для http://web.archive.org/web/20150803024617/http://www.sans.org/security-resources/idfaq/oddports.php[номеров портов, используемых троянами].

FTP имеет два режима: активный режим и пассивный режим. Разница заключается в том, как устанавливается канал передачи данных. Пассивный режим более безопасен, так как канал передачи данных устанавливается инициатором сессии FTP. Для подробного объяснения работы FTP и различных режимов см. http://www.slacksite.com/other/ftp.html[http://www.slacksite.com/other/ftp.html].

Набор правил межсетевого экрана может быть "исключающим" или "включающим". Исключающий межсетевой экран пропускает весь трафик, кроме трафика, соответствующего набору правил. Включающий межсетевой экран действует наоборот, пропуская только трафик, соответствующий правилам, и блокируя всё остальное.

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

[NOTE]
====
Если не указано иное, все конфигурации и примеры наборов правил в этой главе создают включающие наборы правил для межсетевого экрана.
====

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

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

Когда сеанс завершается, он удаляется из динамической таблицы состояний.

Фильтрация с сохранением состояния позволяет сосредоточиться на блокировке/пропускании новых сеансов. Если новый сеанс пропущен, все последующие его пакеты автоматически разрешаются, а любые поддельные пакеты автоматически отвергаются. Если новый сеанс заблокирован, ни один из его последующих пакетов не разрешается. Фильтрация с сохранением состояния предоставляет расширенные возможности сопоставления, способные защитить от потока различных методов атак, используемых злоумышленниками.

NAT означает _Network Address Translation_ (Преобразование сетевых адресов). Функция NAT позволяет частной локальной сети за межсетевым экраном использовать один IP-адрес, назначенный провайдером, даже если этот адрес динамический. NAT даёт возможность каждому компьютеру в локальной сети выходить в Интернет без необходимости оплачивать провайдеру несколько интернет-аккаунтов или IP-адресов.

NAT автоматически преобразует частный IP-адрес LAN для каждой системы в локальной сети в единственный публичный IP-адрес, когда пакеты выходят за пределы межсетевого экрана, направляясь в публичный Интернет. Он также выполняет обратное преобразование для возвращающихся пакетов.

Согласно RFC 1918, следующие диапазоны IP-адресов зарезервированы для частных сетей, которые никогда не маршрутизируются напрямую в публичный Интернет и, следовательно, могут использоваться с NAT:

* `10.0.0.0/8`.
* `172.16.0.0/12`.
* `192.168.0.0/16`.

[WARNING]
====
При работе с правилами межсетевого экрана будьте _очень осторожны_. Некоторые конфигурации _могут заблокировать администратора_ на сервере. Для безопасности рекомендуется выполнять первоначальную настройку межсетевого экрана с локальной консоли, а не удалённо через ssh.
====

[[firewalls-pf]]
== PF

Начиная с FreeBSD 5.3, портированная версия межсетевого экрана PF из OpenBSD включена в базовую систему как её неотъемлемая часть. PF — это полноценный и многофункциональный межсетевой экран с опциональной поддержкой ALTQ (Alternate Queuing), которая обеспечивает качество обслуживания (QoS).

Проект OpenBSD содержит каноническое справочное издание по PF в http://www.openbsd.org/faq/pf/[FAQ по PF]. Питер Ханстин ведёт подробный учебник по PF на http://home.nuug.no/\~peter/pf/[http://home.nuug.no/~peter/pf/].

[WARNING]
====
При чтении http://www.openbsd.org/faq/pf/[FAQ по PF] учитывайте, что версия PF в FreeBSD значительно отличается от оригинальной версии OpenBSD за прошедшие годы. Не все функции работают одинаково в FreeBSD и OpenBSD, и наоборот.
====

`{freebsd-pf}` — хорошее место для вопросов о настройке и работе PF межсетевого экрана. Перед тем как задать вопрос, проверьте архивы списка рассылки — возможно, ответ уже был дан.

Эта часть Руководства посвящена PF в контексте FreeBSD. В ней показано, как включить PF и ALTQ, а также приведены несколько примеров создания наборов правил в системе FreeBSD.

=== Включение PF

Для использования PF необходимо сначала загрузить его модуль ядра. В этом разделе описаны записи, которые можно добавить в [.filename]#/etc/rc.conf# для включения PF.

Начните с добавления `pf_enable=yes` в [.filename]#/etc/rc.conf#:

[source, shell]
....
# sysrc pf_enable=yes
....

Дополнительные параметры, описанные в man:pfctl[8], могут быть переданы в PF при его запуске. Добавьте или измените эту запись в [.filename]#/etc/rc.conf# и укажите необходимые флаги между кавычками (`""`):

[.programlisting]
....
pf_flags=""                     # additional flags for pfctl startup
....

PF не запустится, если не сможет найти файл конфигурации набора правил. По умолчанию FreeBSD не поставляется с готовым набором правил, и файл [.filename]#/etc/pf.conf# отсутствует. Примеры наборов правил можно найти в [.filename]#/usr/share/examples/pf/#. Если пользовательский набор правил был сохранён в другом месте, добавьте строку в [.filename]#/etc/rc.conf#, указывающую полный путь к файлу:

[.programlisting]
....
pf_rules="/path/to/pf.conf"
....

Поддержка журналирования для PF предоставляется man:pflog[4]. Для включения поддержки журналирования добавьте `pflog_enable=yes` в [.filename]#/etc/rc.conf#:

[source, shell]
....
# sysrc pflog_enable=yes
....

Следующие строки также могут быть добавлены для изменения расположения файла журнала по умолчанию или для указания дополнительных флагов, передаваемых в man:pflog[4] при его запуске:

[.programlisting]
....
pflog_logfile="/var/log/pflog"  # where pflogd should store the logfile
pflog_flags=""                  # additional flags for pflogd startup
....

Наконец, если за межсетевым экраном есть локальная сеть (LAN) и пакеты необходимо перенаправлять для компьютеров в этой сети, или требуется NAT, включите следующую опцию:

[.programlisting]
....
gateway_enable="YES"            # Enable as LAN gateway
....

После сохранения необходимых изменений, PF можно запустить с поддержкой журналирования, набрав:

[source, shell]
....
# service pf start
# service pflog start
....

По умолчанию PF читает свои правила конфигурации из [.filename]#/etc/pf.conf# и изменяет, отбрасывает или пропускает пакеты в соответствии с правилами или определениями, указанными в этом файле. Установка FreeBSD включает несколько примеров файлов, расположенных в [.filename]#/usr/share/examples/pf/#. Полное описание наборов правил PF можно найти в http://www.openbsd.org/faq/pf/[PF FAQ].

Для управления PF используйте `pfctl`. crossref:firewalls[pfctl,Полезные опции `pfctl`] содержит сводку полезных опций этой команды. За подробным описанием всех доступных опций обратитесь к man:pfctl[8]:
[[pfctl]]
.Полезные параметры `pfctl`
[cols="1,1", frame="none", options="header"]
|===
| Команда
| Назначение

|`pfctl -e`
|Включить PF.

|`pfctl -d`
|Отключить PF.

|`pfctl -F all -f /etc/pf.conf`
|Очистить все правила NAT, фильтрации, состояний и таблиц и перезагрузить [.filename]#/etc/pf.conf#.

|`pfctl -s [ rules \| nat \| states ]`
|Отчёт о правилах фильтрации, правилах NAT или таблице состояний.

|`pfctl -vnf /etc/pf.conf`
|Проверить [.filename]#/etc/pf.conf# на ошибки, но не загружать набор правил.
|===

[TIP]
====
Пакет package:security/sudo[] полезен для выполнения команд, таких как `pfctl`, которые требуют повышенных привилегий. Он может быть установлен из Коллекции портов.
====

Для наблюдения за трафиком, проходящим через межсетевой экран PF, рекомендуется установить пакет package:sysutils/pftop[] или порт. После установки можно запустить pftop для просмотра текущего снимка трафика в формате, аналогичном man:top[1].

[[pf-tutorial]]
=== Наборы правил PF

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

Самый простой возможный набор правил предназначен для одиночной машины, которая не запускает никаких сервисов и которой требуется доступ к одной сети, возможно, к Интернету. Чтобы создать этот минимальный набор правил, отредактируйте файл [.filename]#/etc/pf.conf#, чтобы он выглядел следующим образом:

[.programlisting]
....
block in all
pass out all keep state
....

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

[source, shell]
....
# pfctl -e ; pfctl -f /etc/pf.conf
....

В дополнение к отслеживанию состояний, PF предоставляет _списки_ и _макросы_, которые можно определить для использования при создании правил. Макросы могут включать списки и должны быть определены до их использования. В качестве примера вставьте следующие строки в самое начало набора правил:

[.programlisting]
....
tcp_services = "{ ssh, smtp, domain, www, pop3, auth, pop3s }"
udp_services = "{ domain }"
....

PF понимает как имена портов, так и их номера, при условии, что имена перечислены в [.filename]#/etc/services#. В этом примере создаются два макроса. Первый — это список из семи имён TCP-портов, а второй — одно имя UDP-порта. После определения макросы можно использовать в правилах. В данном примере весь трафик блокируется, за исключением соединений, инициированных этой системой для семи указанных TCP-сервисов и одного указанного UDP-сервиса:

[.programlisting]
....
tcp_services = "{ ssh, smtp, domain, www, pop3, auth, pop3s }"
udp_services = "{ domain }"
block all
pass out proto tcp to any port $tcp_services keep state
pass proto udp to any port $udp_services keep state
....

Хотя UDP считается протоколом без сохранения состояния, PF способен отслеживать некоторую информацию о состоянии. Например, когда передаётся UDP-запрос к серверу имён с вопросом о доменном имени, PF будет ожидать ответ, чтобы пропустить его обратно.

Всякий раз при внесении изменений в набор правил, новые правила должны быть загружены для их использования:

[source, shell]
....
# pfctl -f /etc/pf.conf
....

Если нет синтаксических ошибок, `pfctl` не выводит сообщений во время загрузки правил. Правила также можно проверить перед попыткой их загрузки:

[source, shell]
....
# pfctl -nf /etc/pf.conf
....

Включение опции `-n` приводит к тому, что правила только интерпретируются, но не загружаются. Это даёт возможность исправить любые ошибки. В любое время будет применяться последний загруженный корректный набор правил, пока либо PF не будет отключён, либо не будет загружен новый набор правил.

[TIP]
====
Добавление `-v` к проверке или загрузке набора правил `pfctl` отобразит полностью разобранные правила именно так, как они будут загружены. Это крайне полезно при отладке правил межсетевого экрана.
====

[[pftut-gateway]]
==== Простой шлюз с NAT

В этом разделе показано, как настроить систему FreeBSD с PF для работы в качестве шлюза как минимум для одного другого компьютера. Шлюзу требуется как минимум два сетевых интерфейса, каждый из которых подключен к отдельной сети. В этом примере [.filename]#xl0# подключен к Интернету, а [.filename]#xl1# подключен к внутренней сети.

Включите шлюз, чтобы позволить машине перенаправлять сетевой трафик, полученный на одном интерфейсе, на другой интерфейс. Этот параметр sysctl перенаправляет IPv4-пакеты:

[source, shell]
....
# sysctl net.inet.ip.forwarding=1
....

Для переадресации IPv6-трафика используйте:

[source, shell]
....
# sysctl net.inet6.ip6.forwarding=1
....

Чтобы включить эти настройки при загрузке системы, используйте man:sysrc[8] для их добавления в [.filename]#/etc/rc.conf#:

[source, shell]
....
# sysrc gateway_enable=yes
# sysrc ipv6_gateway_enable=yes
....

Проверьте с помощью `ifconfig`, что оба интерфейса активны и работают.

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

[.programlisting]
....
pass in on xl1 from xl1:network to xl0:network port $ports keep state
....

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

[.programlisting]
....
pass out on xl0 from xl1:network to xl0:network port $ports keep state
....

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

[.programlisting]
....
pass from xl1:network to any port $ports keep state
....

Обозначение `interface:network` может быть заменено макросом для повышения читаемости набора правил. Например, можно определить макрос `$localnet` как сеть, непосредственно подключённую к внутреннему интерфейсу (`$xl1:network`). Или определение `$localnet` может быть изменено на _IP-адрес/маску сети_ для обозначения сети, например `192.168.100.1/24` для подсети частных адресов.

Если требуется, `$localnet` можно определить даже как список сетей. Независимо от конкретных потребностей, разумное определение `$localnet` может быть использовано в типичном правиле пропуска следующим образом:

[.programlisting]
....
pass from $localnet to any port $ports keep state
....

Следующий пример набора правил разрешает весь трафик, инициированный машинами во внутренней сети. Сначала определяются два макроса для представления внешнего и внутреннего интерфейсов 3COM настраиваемого шлюза.

[NOTE]
====
Для пользователей коммутируемого доступа внешний интерфейс будет использовать [.filename]#tun0#. Для ADSL-подключения, особенно тех, которые используют PPP через Ethernet (PPPoE), правильный внешний интерфейс — это [.filename]#tun0#, а не физический Ethernet-интерфейс.
====

[.programlisting]
....
ext_if = "xl0"	# macro for external interface - use tun0 for PPPoE
int_if = "xl1"	# macro for internal interface
localnet = $int_if:network
# ext_if IP address could be dynamic, hence ($ext_if)
nat on $ext_if from $localnet to any -> ($ext_if)
block all
pass from { lo0, $localnet } to any keep state
....

Этот набор правил вводит правило `nat`, которое используется для обработки преобразования сетевых адресов из не маршрутизируемых адресов внутри внутренней сети в IP-адрес, назначенный внешнему интерфейсу. Скобки вокруг последней части правила nat `($ext_if)` включаются, когда IP-адрес внешнего интерфейса назначается динамически. Это гарантирует, что сетевой трафик будет работать без серьёзных перебоев, даже если внешний IP-адрес изменится.

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

[.programlisting]
....
client_out = "{ ftp-data, ftp, ssh, domain, pop3, auth, nntp, http, \
    https, cvspserver, 2628, 5999, 8000, 8080 }"
....

для использования в основном правиле пропуска:

[.programlisting]
....
pass inet proto tcp from $localnet to any port $client_out \
    flags S/SA keep state
....

Еще могут понадобиться несколько других правил пропуска . Это правило включает SSH на внешнем интерфейсе:

[.programlisting]
....
pass in inet proto tcp to $ext_if port ssh
....

Это определение макроса и правило разрешают DNS и NTP для внутренних клиентов:

[.programlisting]
....
udp_services = "{ domain, ntp }"
pass quick inet proto { tcp, udp } to any port $udp_services keep state
....

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

[[pftut-ftp]]
==== Создание FTP-прокси

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

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

Все эти моменты представляют собой проблемы безопасности, даже без учёта потенциальных уязвимостей в клиентском или серверном программном обеспечении. Более безопасные альтернативы для передачи файлов существуют, например, man:sftp[1] или man:scp[1], которые обеспечивают аутентификацию и передачу данных через зашифрованные соединения.

Для случаев, когда требуется FTP, PF предоставляет возможность перенаправления FTP-трафика на небольшую прокси-программу под названием man:ftp-proxy[8], которая включена в базовую систему FreeBSD. Роль прокси заключается в динамическом добавлении и удалении правил в наборе правил, используя набор якорей, для корректной обработки FTP-трафика.

Чтобы включить FTP-прокси, добавьте следующую строку в [.filename]#/etc/rc.conf#:

[.programlisting]
....
ftpproxy_enable="YES"
....

Затем запустите прокси, выполнив:

[source, bash]
....
# service ftp-proxy start
....

Для базовой конфигурации необходимо добавить три элемента в [.filename]#/etc/pf.conf#. Во-первых, якоря, которые прокси будет использовать для вставки правил, генерируемых для FTP-сессий:

[.programlisting]
....
nat-anchor "ftp-proxy/*"
rdr-anchor "ftp-proxy/*"
....

Во-вторых, необходимо правило pass, чтобы разрешить FTP-трафик к прокси.

Третье, правила перенаправления и NAT должны быть определены до правил фильтрации. Вставьте это правило `rdr` сразу после правила `nat`:

[.programlisting]
....
rdr pass on $int_if proto tcp from any to any port ftp -> 127.0.0.1 port 8021
....

Наконец, разрешите перенаправленному трафику проходить:

[.programlisting]
....
pass out proto tcp from $proxy to any port ftp
....

где `$proxy` раскрывается в адрес, к которому привязан демон прокси.

Сохраните [.filename]#/etc/pf.conf#, загрузите новые правила и проверьте с клиента, что FTP-подключения работают:

[source, shell]
....
# pfctl -f /etc/pf.conf
....

Этот пример описывает базовую настройку, когда клиенты в локальной сети должны обращаться к FTP-серверам в других местах. Данная базовая конфигурация должна хорошо работать с большинством комбинаций FTP-клиентов и серверов. Как показано в man:ftp-proxy[8], поведение прокси можно изменить различными способами, добавляя параметры в строку `ftpproxy_flags=`. Некоторые клиенты или серверы могут иметь специфические особенности, которые необходимо учитывать в конфигурации, или может возникнуть необходимость интегрировать прокси определённым образом, например, назначить FTP-трафик в определённую очередь.

Для способов запуска FTP-сервера, защищённого PF и man:ftp-proxy[8], настройте отдельный `ftp-proxy` в обратном режиме с использованием `-R` на отдельном порту с собственным правилом перенаправления pass.

[[pftut-icmp]]
==== Управление ICMP

Многие инструменты, используемые для отладки или устранения неполадок в сети TCP/IP, основаны на протоколе ICMP (Internet Control Message Protocol), который был специально разработан для целей отладки.

Протокол ICMP отправляет и получает _управляющие сообщения_ между хостами и шлюзами, в основном для предоставления отправителю обратной связи о любых необычных или сложных условиях на пути к целевому хосту. Маршрутизаторы используют ICMP для согласования размеров пакетов и других параметров передачи в процессе, часто называемом _обнаружением MTU пути_.

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

[.programlisting]
....
pass inet proto icmp from any to any
....

Одно из решений — пропускать весь ICMP-трафик из локальной сети, блокируя все запросы извне:

[.programlisting]
....
pass inet proto icmp from $localnet to any keep state
pass inet proto icmp from any to $ext_if keep state
....

Дополнительные параметры демонстрируют гибкость PF. Например, вместо разрешения всех ICMP-сообщений можно указать сообщения, используемые man:ping[8] и man:traceroute[8]. Начните с определения макроса для этого типа сообщения:

[.programlisting]
....
icmp_types = "echoreq"
....

и правила, которое использует макрос:

[.programlisting]
....
pass inet proto icmp all icmp-type $icmp_types keep state
....

Если требуются другие типы ICMP-пакетов, расширьте `icmp_types` до списка этих типов пакетов. Введите `more /usr/src/sbin/pfctl/pfctl_parser.c`, чтобы увидеть список типов ICMP-сообщений, поддерживаемых PF. Дополнительные сведения о каждом типе сообщения можно найти по ссылке http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml[http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml].

Поскольку Unix `traceroute` по умолчанию использует UDP, требуется дополнительное правило для разрешения Unix `traceroute`:

[.programlisting]
....
# allow out the default range for traceroute(8):
pass out on $ext_if inet proto udp from any to any port 33433 >< 33626 keep state
....

Поскольку `TRACERT.EXE` в системах Microsoft Windows использует сообщения запроса эхо-ответа ICMP, достаточно только первого правила, чтобы разрешить трассировку сети с этих систем. В Unix `traceroute` можно настроить на использование других протоколов, и он будет использовать сообщения запроса эхо-ответа ICMP, если указан параметр `-I`. Подробности смотрите на man:traceroute[8].

[[pftut-pathmtudisc]]
===== Обнаружение MTU пути

Протоколы Интернета разработаны так, чтобы быть независимыми от устройств, и одним из следствий этой независимости является то, что оптимальный размер пакета для данного соединения не всегда может быть надёжно предсказан. Основное ограничение на размер пакета — это _Maximum Transmission Unit_ (MTU), который устанавливает верхний предел размера пакета для интерфейса. Введите `ifconfig`, чтобы просмотреть MTU для сетевых интерфейсов системы.

TCP/IP использует процесс, известный как обнаружение MTU пути, для определения правильного размера пакета для соединения. Этот процесс отправляет пакеты различного размера с установленным флагом "Не фрагментировать", ожидая возврата ICMP-пакета с "типом 3, кодом 4", когда достигнут верхний предел. Тип 3 означает "адресат недостижим", а код 4 является сокращением для "требуется фрагментация, но установлен флаг 'не фрагментировать'". Чтобы разрешить обнаружение MTU пути для поддержки соединений с другими MTU, добавьте тип `destination unreachable` в макрос `icmp_types`:

[.programlisting]
....
icmp_types = "{ echoreq, unreach }"
....

Поскольку правило `pass` уже использует этот макрос, его не нужно изменять для поддержки нового типа ICMP:

[.programlisting]
....
pass inet proto icmp all icmp-type $icmp_types keep state
....

PF позволяет фильтрацию по всем вариантам типов и кодов ICMP. Список возможных типов и кодов документирован в man:icmp[4] и man:icmp6[4].

[[pftut-tables]]
==== Использование таблиц

Некоторые типы данных важны для фильтрации и перенаправления в определённый момент времени, но их определение слишком длинное, чтобы включать его в файл набора правил. PF поддерживает использование таблиц — это определённые списки, которыми можно управлять без необходимости перезагружать весь набор правил, и которые обеспечивают быстрый поиск. Имена таблиц всегда заключаются в `< >`, например:

[.programlisting]
....
table <clients> { 192.168.2.0/24, !192.168.2.5 }
....

В этом примере сеть `192.168.2.0/24` является частью таблицы, за исключением адреса `192.168.2.5`, который исключен с помощью оператора `!`. Также можно загружать таблицы из файлов, где каждый элемент находится на отдельной строке, как показано в этом примере [.filename]#/etc/clients#:

[.programlisting]
....
192.168.2.0/24
!192.168.2.5
....

Для обращения к файлу определите таблицу следующим образом:

[.programlisting]
....
table <clients> persist file "/etc/clients"
....

После определения таблицы на неё можно ссылаться в правиле:

[.programlisting]
....
pass inet proto tcp from <clients> to any port $client_out flags S/SA keep state
....

Содержимое таблицы можно изменять в реальном времени с помощью `pfctl`. В этом примере добавляется ещё одна сеть в таблицу:

[source, shell]
....
# pfctl -t clients -T add 192.168.1.0/16
....

Обратите внимание, что любые изменения, внесенные таким образом, вступят в силу немедленно, что делает их идеальными для тестирования, но они не сохранятся после отключения питания или перезагрузки. Чтобы сделать изменения постоянными, необходимо изменить определение таблицы в наборе правил или отредактировать файл, на который ссылается таблица. Можно поддерживать копию таблицы на диске с помощью задания man:cron[8], которое периодически сохраняет содержимое таблицы на диск, используя команду вида `pfctl -t clients -T show >/etc/clients`. Или [.filename]#/etc/clients# можно обновить содержимым таблицы из памяти:

[source, shell]
....
# pfctl -t clients -T replace -f /etc/clients
....

[[pftut-overload]]
==== Использование таблиц превышения нагрузки для защиты SSH

Те, кто запускает SSH на внешнем интерфейсе, вероятно, видели что-то подобное в журналах аутентификации:

[.programlisting]
....
Sep 26 03:12:34 skapet sshd[25771]: Failed password for root from 200.72.41.31 port 40992 ssh2
Sep 26 03:12:34 skapet sshd[5279]: Failed password for root from 200.72.41.31 port 40992 ssh2
Sep 26 03:12:35 skapet sshd[5279]: Received disconnect from 200.72.41.31: 11: Bye Bye
Sep 26 03:12:44 skapet sshd[29635]: Invalid user admin from 200.72.41.31
Sep 26 03:12:44 skapet sshd[24703]: input_userauth_request: invalid user admin
Sep 26 03:12:44 skapet sshd[24703]: Failed password for invalid user admin from 200.72.41.31 port 41484 ssh2
....

Это указывает на атаку методом грубой силы, когда кто-то или какая-то программа пытается подобрать имя пользователя и пароль, чтобы получить доступ к системе.

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

Для настройки создайте следующую таблицу в разделе tables набора правил:

[.programlisting]
....
table <bruteforce> persist
....

Затем, где-то в начале набора правил добавьте правила для блокировки перебора, разрешая при этом законный доступ:

[.programlisting]
....
block quick from <bruteforce>
pass inet proto tcp from any to $localnet port $tcp_services \
    flags S/SA keep state \
    (max-src-conn 100, max-src-conn-rate 15/5, \
    overload <bruteforce> flush global)
....

Часть в скобках определяет ограничения, и числа должны быть изменены в соответствии с местными требованиями. Это можно прочитать следующим образом:

`max-src-conn` — это количество одновременных подключений, разрешённых с одного хоста.

`max-src-conn-rate` — это частота новых соединений, разрешённых с любого отдельного хоста (_15_) за указанное количество секунд (_5_).

`overload <bruteforce>` означает, что любой хост, превышающий эти ограничения, добавляет свой адрес в таблицу `bruteforce`. Набор правил блокирует весь трафик с адресов, находящихся в таблице `bruteforce`.

Наконец, `flush global` означает, что когда хост достигает лимита, все (`global`) соединения этого хоста будут разорваны (`flush`).

[NOTE]
====
Эти правила _не_ заблокируют медленные брутфорсеры, как описано в http://home.nuug.no/\~peter/hailmary2013/[http://home.nuug.no/~peter/hailmary2013/].
====

Этот пример набора правил предназначен в основном для иллюстрации. Например, если требуется разрешить большое количество соединений в целом, но необходимо быть более строгим в отношении ssh, дополните приведённое выше правило чем-то вроде следующего, в начале набора правил:

[.programlisting]
....
pass quick proto { tcp, udp } from any to any port ssh \
    flags S/SA keep state \
    (max-src-conn 15, max-src-conn-rate 5/3, \
    overload <bruteforce> flush global)
....

[NOTE]
====
*Не всегда необходимо блокировать всех превышающих нагрузки:* +

Стоит отметить, что механизм превышения нагрузки — это общий метод, который применяется не только к SSH, и не всегда оптимально полностью блокировать весь трафик от нарушителей.

Например, правило превышения нагрузки может использоваться для защиты почтовой службы или веб-сервиса, а таблица превышения нагрузки может применяться в правиле для назначения нарушителей в очередь с минимальным выделением пропускной способности или для перенаправления на определённую веб-страницу.
====

Со временем таблицы будут заполняться правилами превышения нагрузки, и их размер будет постепенно увеличиваться, занимая больше памяти. Иногда заблокированный IP-адрес оказывается динамически назначенным, который с тех пор был выделен хосту, имеющему законные основания для взаимодействия с хостами в локальной сети.

Для подобных ситуаций `pfctl` предоставляет возможность удалять записи из таблиц. Например, следующая команда удалит записи из таблицы `<bruteforce>`, к которым не было обращений в течение `86400` секунд:

[source, shell]
....
# pfctl -t bruteforce -T expire 86400
....

Аналогичную функциональность предоставляет пакет package:security/expiretable[], который удаляет записи таблицы, к которым не было обращений в течение указанного периода времени.

После установки `expiretable` можно запустить для удаления записей из таблицы `<bruteforce>`, которые старше указанного времени. В этом примере удаляются все записи старше 24 часов:

[.programlisting]
....
/usr/local/sbin/expiretable -v -d -t 24h bruteforce
....

[[pftut-spamd]]
==== Защита от SPAM

package:mail/spamd[] можно настроить совместно с PF для обеспечения внешней защиты от SPAM (Не путайте его с демоном spamd, который поставляется вместе с spamassassin). Этот spamd интегрируется в конфигурацию PF с помощью набора перенаправлений.

Спамеры обычно рассылают большое количество сообщений, и СПАМ в основном отправляется из нескольких дружественных к спамерам сетей и множества взломанных машин, которые быстро попадают в _списки блокировки_.

Когда SMTP-соединение с адреса из блок-листа получено, spamd отображает свой баннер и немедленно переключается в режим, где он отвечает на SMTP-трафик по одному байту за раз. Эта техника, предназначенная для максимально возможной потери времени на стороне спамера, называется _тарпиттинг_ (tarpitting, намеренное замедление). Конкретная реализация, использующая однобайтовые SMTP-ответы, часто упоминается как _прерывистые ответы_ (stuttering, метод заикания).

В этом примере показана базовая процедура настройки spamd с автоматически обновляемыми списками блокировки. Для получения дополнительной информации обратитесь к руководствам, которые устанавливаются вместе с пакетом package:mail/spamd[].

[.procedure]
****
.Процедура: Настройка spamd
. Установите пакет package:mail/spamd[] или порт. Для использования функции серого списка (greylisting) в spamd, необходимо подключить man:fdescfs[5] в [.filename]#/dev/fd#. Добавьте следующую строку в [.filename]#/etc/fstab#:
+
[.programlisting]
....
 fdescfs /dev/fd fdescfs rw 0 0
....
+
Затем смонтируйте файловую систему:
+
[.programlisting]
....
#  mount fdescfs
....

. Далее отредактируйте набор правил PF, включив:
+
[.programlisting]
....
table <spamd> persist
table <spamd-white> persist
rdr pass on $ext_if inet proto tcp from <spamd> to \
    { $ext_if, $localnet } port smtp -> 127.0.0.1 port 8025
rdr pass on $ext_if inet proto tcp from !<spamd-white> to \
    { $ext_if, $localnet } port smtp -> 127.0.0.1 port 8025
....
+
Две таблицы `<spamd>` и `<spamd-white>` являются обязательными. SMTP-трафик с адреса, указанного в `<spamd>`, но отсутствующего в `<spamd-white>`, перенаправляется к демону spamd, ожидающему на порту 8025.
. Следующий шаг — настроить spamd в [.filename]#/usr/local/etc/spamd.conf# и добавить параметры в [.filename]#rc.conf#.
+
Установка пакета package:mail/spamd[] включает образец файла конфигурации ([.filename]#/usr/local/etc/spamd.conf.sample#) и man-страницу для [.filename]#spamd.conf#. Обратитесь к ним для получения дополнительных параметров конфигурации, помимо показанных в этом примере.
+
Одна из первых строк в файле конфигурации, которая не начинается с символа комментария `+#+`, содержит блок, определяющий список `all`, который указывает списки для использования:
+
[.programlisting]
....
all:\
    :traplist:allowlist:
....
+
Эта запись добавляет желаемые списки блокировок, разделённые двоеточиями (`:`). Чтобы использовать список разрешений для исключения адресов из списка блокировок, добавьте имя списка разрешений _непосредственно_ после имени этого списка блокировок. Например: `:blocklist:allowlist:`.
+
За этим следует определение указанного списка блокировки:
+
[.programlisting]
....
traplist:\
    :black:\
    :msg="SPAM. Your address %A has sent spam within the last 24 hours":\
    :method=http:\
    :file=www.openbsd.org/spamd/traplist.gz
....
+
где первая строка — это имя списка блокировки, а вторая строка определяет тип списка. Поле `msg` содержит сообщение, которое будет отображаться заблокированным отправителям во время SMTP-диалога. Поле `method` указывает, каким образом spamd-setup получает данные списка; поддерживаемые методы — `http`, `ftp`, из `file` в смонтированной файловой системе и через `exec` внешней программы. Наконец, поле `file` определяет имя файла, который ожидает получить spamd.
+
Определение указанного разрешенного списка аналогично, но опускает поле `msg`, так как сообщение не требуется:
+
[.programlisting]
....
allowlist:\
    :white:\
    :method=file:\
    :file=/var/mail/allowlist.txt
....
+
[TIP]
====
*Выбирайте источники данных с осторожностью:* +

Использование всех блокирующих списков в примере [.filename]#spamd.conf# приведет к блокировке больших сегментов Интернета. Администраторам необходимо отредактировать файл, чтобы создать оптимальную конфигурацию, которая использует подходящие источники данных и, при необходимости, пользовательские списки.
====
+
Далее добавьте эту запись в [.filename]#/etc/rc.conf#. Дополнительные флаги описаны в man-странице, указанной в комментарии:
+
[.programlisting]
....
spamd_flags="-v" # use "" and see spamd-setup(8) for flags
....
+
После завершения перезагрузите набор правил, запустите spamd командой `service obspamd start` и завершите настройку с помощью `spamd-setup`. Наконец, создайте задание в man:cron[8], которое будет вызывать `spamd-setup` для обновления таблиц через разумные промежутки времени.
****

На типичном шлюзе перед почтовым сервером узлы начнут попадать в ловушку в течение от нескольких секунд до нескольких минут.

PF также поддерживает _greylisting_, который временно отклоняет сообщения от неизвестных хостов с кодами _45n_. Сообщения от хостов, находящихся в greylisting, которые повторяют попытку в разумные сроки, пропускаются. Трафик от отправителей, настроенных на работу в пределах, установленных RFC 1123 и RFC 2821, пропускается сразу.

Дополнительная информация о методе серых списков (greylisting) доступна на сайте http://www.greylisting.org/[greylisting.org]. Самое удивительное в серых списках, помимо их простоты, это то, что они до сих пор работают. Спамеры и авторы вредоносного ПО очень медленно адаптируются, чтобы обойти этот метод.

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

[.procedure]
.Процедура: Настройка серого списка
. Убедитесь, что man:fdescfs[5] смонтирован, как описано в Шаге 1 предыдущей процедуры.
. Для запуска spamd в режиме серого списка добавьте следующую строку в [.filename]#/etc/rc.conf#:
+
[.programlisting]
....
spamd_grey="YES"  # use spamd greylisting if YES
....
+
Обратитесь к man-странице spamd для описания дополнительных параметров, относящихся к серым спискам.
. Для завершения настройки серого списка:
+
[.programlisting]
....
#  service obspamd restart
#  service obspamlogd start
....

За кулисами инструмент базы данных spamdb и обновляющий белый список spamlogd выполняют важные функции для механизма отложенной доставки. spamdb — это основной интерфейс администратора для управления списками блокировки, серого списка и списка с разрешениями через содержимое базы данных [.filename]#/var/db/spamdb#.

[[pftut-hygiene]]
==== Сетевая гигиена

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

`block-policy` — это опция, которую можно установить в разделе `options` набора правил, предшествующем правилам перенаправления и фильтрации. Эта опция определяет, какую обратную связь (если таковая имеется) PF отправляет хостам, заблокированным правилом. Опция имеет два возможных значения: `drop` отбрасывает заблокированные пакеты без ответа, а `return` возвращает код состояния, например `Connection refused`.

Если не задано, политика по умолчанию — `drop`. Чтобы изменить `block-policy`, укажите желаемое значение:

[.programlisting]
....
set block-policy return
....

В PF `scrub` — это ключевое слово, которое включает нормализацию сетевых пакетов. Этот процесс пересобирает фрагментированные пакеты и отбрасывает TCP-пакеты с недопустимыми комбинациями флагов. Включение `scrub` обеспечивает защиту от определённых видов атак, основанных на некорректной обработке фрагментов пакетов. Доступно несколько опций, но простейшая форма подходит для большинства конфигураций:

[.programlisting]
....
scrub in all
....

Некоторые службы, такие как NFS, требуют специальных параметров обработки фрагментов. Дополнительную информацию можно найти по ссылке https://home.nuug.no/~peter/pf/en/scrub.html[https://home.nuug.no/~peter/pf/en/scrub.html].

Этот пример собирает фрагменты, сбрасывает бит "не фрагментировать" и устанавливает максимальный размер сегмента в 1440 байт:

[.programlisting]
....
scrub in all fragment reassemble no-df max-mss 1440
....

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

Эти правила отсеивают поддельный трафик, поступающий из остального мира, а также любые поддельные пакеты, исходящие из локальной сети:

[.programlisting]
....
antispoof for $ext_if
antispoof for $int_if
....

[[pftut-unrouteables]]
==== Обработка немаршрутизируемых адресов

Даже при правильно настроенном шлюзе для обработки преобразования сетевых адресов может потребоваться компенсировать ошибки в конфигурации, сделанные другими людьми. Распространённой ошибкой является пропуск трафика с не маршрутизируемыми адресами в Интернет. Поскольку трафик с не маршрутизируемых адресов может использоваться в нескольких методах DoS-атак, рекомендуется явно блокировать такой трафик от попадания в сеть через внешний интерфейс.

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

[.programlisting]
....
martians = "{ 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, \
	      10.0.0.0/8, 169.254.0.0/16, 192.0.2.0/24, \
	      0.0.0.0/8, 240.0.0.0/4 }"

block drop in quick on $ext_if from $martians to any
block drop out quick on $ext_if from any to $martians
....

=== Включение ALTQ

На FreeBSD ALTQ можно использовать с PF для обеспечения качества обслуживания (QOS — Quality of Service). После включения ALTQ в наборе правил можно определить очереди, которые определяют приоритет обработки исходящих пакетов.

Прежде чем включить ALTQ, обратитесь к man:altq[4], чтобы определить, поддерживают ли его драйверы сетевых карт, установленных в системе.

ALTQ недоступен в виде загружаемого модуля ядра. Если интерфейсы системы поддерживают ALTQ, создайте собственное ядро, следуя инструкциям в crossref:kernelconfig[kernelconfig,Настройка ядра FreeBSD]. Доступны следующие параметры ядра. Первый необходим для включения ALTQ. Хотя бы один из остальных параметров требуется для указания алгоритма планирования очередей:

[.programlisting]
....
options         ALTQ
options         ALTQ_CBQ        # Class Based Queuing (CBQ)
options         ALTQ_RED        # Random Early Detection (RED)
options         ALTQ_RIO        # RED In/Out
options         ALTQ_HFSC       # Hierarchical Packet Scheduler (HFSC)
options         ALTQ_PRIQ       # Priority Queuing (PRIQ)
....

Доступны следующие алгоритмы планировщика:

CBQ::
Очереди на основе классов (CBQ — Class Based Queuing) используется для разделения пропускной способности соединения на различные классы или очереди с целью приоритизации трафика на основе правил фильтрации.

RED::
Случайное раннее обнаружение (RED — Random Early Detection) используется для предотвращения перегрузки сети путем измерения длины очереди и сравнения её с минимальным и максимальным порогами для очереди. Когда очередь превышает максимальный порог, все новые пакеты случайным образом отбрасываются.

RIO::
В режиме Случайного раннего обнаружения для входящего и исходящего трафика (RIO) RED поддерживает несколько средних длин очередей и несколько пороговых значений, по одному для каждого уровня QOS.

HFSC::
Иерархический планировщик пакетов с гарантированной справедливой кривой обслуживания (HFSC) описан на http://www-2.cs.cmu.edu/\~hzhang/HFSC/main.html[http://www-2.cs.cmu.edu/~hzhang/HFSC/main.html].

PRIQ::
Очереди с приоритетом (PRIQ) всегда пропускают трафик из более высокоуровневой очереди первым.

Дополнительная информация о алгоритмах планирования и примеры наборов правил доступны на https://web.archive.org/web/20151109213426/http://www.openbsd.org/faq/pf/queueing.html[архивированной странице OpenBSD].

[[firewalls-ipfw]]
== IPFW

IPFW — это межсетевой экран с отслеживанием состояний, разработанный для FreeBSD, который поддерживает как IPv4, так и IPv6. Он состоит из нескольких компонентов: процессора правил фильтрации межсетевого экрана в ядре и встроенного механизма учёта пакетов, механизма журналирования, NAT, механизма управления трафиком man:dummynet[4], механизма переадресации, механизма моста и механизма ipstealth.

FreeBSD предоставляет пример набора правил в [.filename]#/etc/rc.firewall#, который определяет несколько типов межсетевых экранов для распространённых сценариев, чтобы помочь новичкам в создании подходящего набора правил. IPFW предлагает мощный синтаксис, с помощью которого опытные пользователи могут создавать настраиваемые наборы правил, соответствующие требованиям безопасности заданного окружения.

Этот раздел описывает, как включить IPFW, предоставляет обзор синтаксиса его правил и демонстрирует несколько наборов правил для распространённых сценариев настройки.

[[firewalls-ipfw-enable]]
=== Включение IPFW

IPFW включён в базовую установку FreeBSD в виде загружаемого модуля ядра, что означает, что для его включения не требуется собирать собственное ядро.

Для пользователей, которые хотят статически скомпилировать поддержку IPFW в собственном ядре, см. crossref:firewalls[firewalls-ipfw-kernelconfig, Параметры ядра IPFW].

Для настройки системы для включения IPFW при загрузке добавьте `firewall_enable="YES"` в файл [.filename]#/etc/rc.conf#:

[source, shell]
....
# sysrc firewall_enable="YES"
....

Чтобы использовать один из типов межсетевых экранов, предоставляемых FreeBSD, добавьте строку с указанием типа:

[source, shell]
....
# sysrc firewall_type="open"
....

Доступные типы:

* `open`: пропускает весь трафик.
* `client`: защищает только эту машину.
* `simple`: защищает всю сеть.
* `closed`: полностью отключает IP-трафик, за исключением интерфейса loopback.
* `workstation`: защищает только эту машину с использованием правил с отслеживанием состояния.
* `UNKNOWN`: отключает загрузку правил межсетевого экрана.
* [.filename]#filename#: полный путь к файлу, содержащему набор правил межсетевого экрана.

Если `firewall_type` установлен в значение `client` или `simple`, измените правила по умолчанию, указанные в [.filename]#/etc/rc.firewall#, чтобы они соответствовали конфигурации системы.

Обратите внимание, что тип `filename` используется для загрузки пользовательского набора правил.

Альтернативный способ загрузки пользовательского набора правил — присвойте переменной `firewall_script` значение, равное абсолютному пути к _исполняемому скрипту_, который включает команды IPFW. Примеры, используемые в этом разделе, предполагают, что `firewall_script` установлен в [.filename]#/etc/ipfw.rules#:

[source, shell]
....
# sysrc firewall_script="/etc/ipfw.rules"
....

Чтобы включить ведение журнала через man:syslogd[8], добавьте следующую строку:

[source, shell]
....
# sysrc firewall_logging="YES"
....

[WARNING]
====
В журнал будут записываться только правила межсетевого экрана с опцией `log`. Правила по умолчанию не включают эту опцию, и её необходимо добавить вручную. Поэтому рекомендуется отредактировать набор правил по умолчанию для ведения журнала. Кроме того, может потребоваться ротация журналов, если они сохраняются в отдельный файл.
====

В файле [.filename]#/etc/rc.conf# нет переменной для установки ограничений журналирования. Чтобы ограничить количество записей в журнале для каждого правила на одну попытку соединения, укажите число с помощью следующей строки в [.filename]#/etc/sysctl.conf#:

[source, shell]
....
# echo "net.inet.ip.fw.verbose_limit=5" >> /etc/sysctl.conf
....

Для включения журналирования через выделенный интерфейс с именем `ipfw0`, добавьте следующую строку в [.filename]#/etc/rc.conf# вместо этого:

[source, shell]
....
# sysrc firewall_logif="YES"
....

Затем используйте tcpdump для просмотра записываемых данных:

[source, shell]
....
# tcpdump -t -n -i ipfw0
....

[TIP]
====

Нет накладных расходов из-за ведения журнала, если не подключен tcpdump.
====

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

[source, shell]
....
# service ipfw start
# sysctl net.inet.ip.fw.verbose_limit=5
....

[[firewalls-ipfw-rules]]
=== Синтаксис правил IPFW

Когда пакет попадает в межсетевой экран IPFW, он сравнивается с первым правилом в наборе правил и последовательно обрабатывается сверху вниз, правило за правилом. Если пакет соответствует параметрам выбора правила, выполняется действие этого правила, и поиск в наборе правил для данного пакета прекращается. Это называется «первое совпадение побеждает». Если пакет не соответствует ни одному из правил, он попадает под действие обязательного правила IPFW с номером 65535, которое запрещает все пакеты и тихо их отбрасывает. Однако, если пакет соответствует правилу, содержащему ключевые слова `count`, `skipto` или `tee`, поиск продолжается. Подробнее о том, как эти ключевые слова влияют на обработку правил, см. в man:ipfw[8].

При создании правила IPFW ключевые слова должны быть записаны в следующем порядке. Некоторые ключевые слова являются обязательными, а другие — опциональными. Слова, написанные в верхнем регистре, обозначают переменную, а слова в нижнем регистре должны предшествовать следующей за ними переменной. Символ `+#+` используется для обозначения начала комментария и может находиться в конце правила или на отдельной строке. Пустые строки игнорируются.

`_CMD RULE_NUMBER set SET_NUMBER ACTION log LOG_AMOUNT PROTO from SRC SRC_PORT to DST DST_PORT OPTIONS_`

В этом разделе представлен обзор этих ключевых слов и их параметров. Это не исчерпывающий список всех возможных вариантов. Для полного описания синтаксиса правил, которые можно использовать при создании правил IPFW, обратитесь к man:ipfw[8].

CMD::
Каждое правило должно начинаться с `ipfw add`.

RULE_NUMBER::
Каждое правило связано с числом от `1` до `65534`. Этот номер используется для указания порядка обработки правил. Несколько правил могут иметь одинаковый номер, в таком случае они применяются в порядке их добавления.

SET_NUMBER::
Каждое правило связано с номером набора от `0` до `31`. Наборы можно отключать или включать по отдельности, что позволяет быстро добавлять или удалять набор правил. Если `SET_NUMBER` не указан, правило будет добавлено в набор `0`.

ACTION::
Правило может быть связано с одним из следующих действий. Указанное действие будет выполнено, когда пакет соответствует критерию выбора правила.
+
`allow | accept | pass | permit`: эти ключевые слова эквивалентны и разрешают пакеты, соответствующие правилу.
+
`check-state`: проверяет пакет по таблице динамических состояний. Если совпадение найдено, выполняется действие, связанное с правилом, которое создало это динамическое правило, в противном случае осуществляется переход к следующему правилу. Правило `check-state` не имеет критериев выбора. Если правило `check-state` отсутствует в наборе правил, таблица динамических правил проверяется при первом правиле `keep-state` или `limit`.
+
`count`: обновляет счетчики для всех пакетов, соответствующих правилу. Поиск продолжается со следующего правила.
+
`deny | drop`: любое из этих слов тихо отбрасывает пакеты, соответствующие этому правилу.
+
Доступны дополнительные действия. Подробности смотрите в man:ipfw[8].

LOG_AMOUNT::
Когда пакет соответствует правилу с ключевым словом `log`, сообщение будет записано в man:syslogd[8] с именем средства `SECURITY`. Запись в журнал происходит только в том случае, если количество пакетов, зарегистрированных для этого конкретного правила, не превышает указанного LOG_AMOUNT. Если LOG_AMOUNT не указан, лимит берется из значения `net.inet.ip.fw.verbose_limit`. Значение нуля снимает ограничение на запись в журнал. Как только лимит достигнут, запись в журнал можно снова включить, сбросив счетчик журналирования или счетчик пакетов для этого правила с помощью `ipfw resetlog`.
+
[NOTE]
====
Журналирование выполняется после того, как все остальные условия сопоставления пакета выполнены, и перед выполнением конечного действия с пакетом. Администратор решает, для каких правил включить журналирование.
====

PROTO::
Это необязательное значение может использоваться для указания любого имени протокола или номера, найденного в [.filename]#/etc/protocols#.

SRC::
Ключевое слово `from` должно сопровождаться исходным адресом или ключевым словом, представляющим исходный адрес. Адрес может быть представлен как `any`, `me` (любой адрес, настроенный на интерфейсе этой системы), `me6` (любой IPv6-адрес, настроенный на интерфейсе этой системы) или `table`, за которым следует номер таблицы поиска, содержащей список адресов. При указании IP-адреса, он может быть дополнительно указан с маской CIDR или маской подсети. Например, `1.2.3.4/25` или `1.2.3.4:255.255.255.128`.

SRC_PORT::
Необязательный порт источника может быть указан с использованием номера порта или имени из [.filename]#/etc/services#.

DST::
Ключевое слово `to` должно сопровождаться адресом назначения или ключевым словом, представляющим адрес назначения. Те же ключевые слова и адреса, которые описаны в разделе SRC, могут быть использованы для описания назначения.

DST_PORT::
Необязательный порт назначения может быть указан с использованием номера порта или имени из [.filename]#/etc/services#.

OPTIONS::
Несколько ключевых слов могут следовать за источником и назначением. Как следует из названия, OPTIONS являются необязательными. Часто используемые опции включают `in` или `out`, которые указывают направление потока пакетов, `icmptypes` с указанием типа ICMP-сообщения и `keep-state`.
+
Когда правило `keep-state` совпадает, межсетевой экран создаст динамическое правило, которое соответствует двунаправленному трафику между исходным и целевым адресами и портами, используя тот же протокол.
+
Функция динамических правил уязвима к истощению ресурсов из-за SYN-флуда, который может создать огромное количество динамических правил. Для защиты от такого типа атак в IPFW используйте параметр `limit`. Этот параметр ограничивает количество одновременных сессий, проверяя открытые динамические правила и подсчитывая, сколько раз встречалось сочетание данного правила и IP-адреса. Если это количество превышает значение, указанное в `limit`, пакет отбрасывается.
+
Доступны десятки параметров `OPTIONS`. Описание каждого доступного параметра можно найти в man:ipfw[8].

=== Пример набора правил

В этом разделе показано, как создать пример набора правил для статического межсетевого экрана в виде скрипта с именем [.filename]#/etc/ipfw.rules#. В данном примере все правила соединений используют `in` или `out` для указания направления. Они также используют `via` _имя-интерфейса_ для указания интерфейса, через который проходит пакет.

[NOTE]
====
При первоначальном создании или тестировании набора правил межсетевого экрана рекомендуется временно установить этот параметр:

[.programlisting]
....
net.inet.ip.fw.default_to_accept="1"
....

Это устанавливает политику по умолчанию для man:ipfw[8] более разрешительной, чем стандартная `deny ip from any to any`, что немного снижает вероятность блокировки системы сразу после перезагрузки.
====

Скрипт межсетевого экрана начинается с указания, что это скрипт Bourne shell, и очищает все существующие правила. Затем он создает переменную `cmd`, чтобы не приходилось вводить `ipfw add` в начале каждого правила. Также определяется переменная `pif`, которая представляет имя интерфейса, подключенного к Интернету.

[.programlisting]
....
#!/bin/sh
# Flush out the list before we begin.
ipfw -q -f flush

# Set rules command prefix
cmd="ipfw -q add"
pif="dc0"     # interface name of NIC attached to Internet
....

Первые два правила разрешают весь трафик на доверенном внутреннем интерфейсе и на loopback-интерфейсе:

[.programlisting]
....
# Change xl0 to LAN NIC interface name
$cmd 00005 allow all from any to any via xl0

# No restrictions on Loopback Interface
$cmd 00010 allow all from any to any via lo0
....

Следующее правило пропускает пакет, если он соответствует существующей записи в таблице динамических правил:

[.programlisting]
....
$cmd 00101 check-state
....

Следующий набор правил определяет, какие состояния соединений внутренние системы могут устанавливать с узлами в Интернете:

[.programlisting]
....
# Allow access to public DNS
# Replace x.x.x.x with the IP address of a public DNS server
# and repeat for each DNS server in /etc/resolv.conf
$cmd 00110 allow tcp from any to x.x.x.x 53 out via $pif setup keep-state
$cmd 00111 allow udp from any to x.x.x.x 53 out via $pif keep-state

# Allow access to ISP's DHCP server for cable/DSL configurations.
# Use the first rule and check log for IP address.
# Then, uncomment the second rule, input the IP address, and delete the first rule
$cmd 00120 allow log udp from any to any 67 out via $pif keep-state
#$cmd 00120 allow udp from any to x.x.x.x 67 out via $pif keep-state

# Allow outbound HTTP and HTTPS connections
$cmd 00200 allow tcp from any to any 80 out via $pif setup keep-state
$cmd 00220 allow tcp from any to any 443 out via $pif setup keep-state

# Allow outbound email connections
$cmd 00230 allow tcp from any to any 25 out via $pif setup keep-state
$cmd 00231 allow tcp from any to any 110 out via $pif setup keep-state

# Allow outbound ping
$cmd 00250 allow icmp from any to any out via $pif keep-state

# Allow outbound NTP
$cmd 00260 allow udp from any to any 123 out via $pif keep-state

# Allow outbound SSH
$cmd 00280 allow tcp from any to any 22 out via $pif setup keep-state

# deny and log all other outbound connections
$cmd 00299 deny log all from any to any out via $pif
....

Следующий набор правил управляет соединениями от хостов Интернета к внутренней сети. Он начинается с запрета пакетов, обычно связанных с атаками, а затем явно разрешает определённые типы соединений. Все авторизованные сервисы, поступающие из Интернета, используют `limit` для предотвращения перегрузки.

[.programlisting]
....
# Deny all inbound traffic from non-routable reserved address spaces
$cmd 00300 deny all from 192.168.0.0/16 to any in via $pif     #RFC 1918 private IP
$cmd 00301 deny all from 172.16.0.0/12 to any in via $pif      #RFC 1918 private IP
$cmd 00302 deny all from 10.0.0.0/8 to any in via $pif         #RFC 1918 private IP
$cmd 00303 deny all from 127.0.0.0/8 to any in via $pif        #loopback
$cmd 00304 deny all from 0.0.0.0/8 to any in via $pif          #loopback
$cmd 00305 deny all from 169.254.0.0/16 to any in via $pif     #DHCP auto-config
$cmd 00306 deny all from 192.0.2.0/24 to any in via $pif       #reserved for docs
$cmd 00307 deny all from 204.152.64.0/23 to any in via $pif    #Sun cluster interconnect
$cmd 00308 deny all from 224.0.0.0/3 to any in via $pif        #Class D & E multicast

# Deny public pings
$cmd 00310 deny icmp from any to any in via $pif

# Deny ident
$cmd 00315 deny tcp from any to any 113 in via $pif

# Deny all Netbios services.
$cmd 00320 deny tcp from any to any 137 in via $pif
$cmd 00321 deny tcp from any to any 138 in via $pif
$cmd 00322 deny tcp from any to any 139 in via $pif
$cmd 00323 deny tcp from any to any 81 in via $pif

# Deny fragments
$cmd 00330 deny all from any to any frag in via $pif

# Deny ACK packets that did not match the dynamic rule table
$cmd 00332 deny tcp from any to any established in via $pif

# Allow traffic from ISP's DHCP server.
# Replace x.x.x.x with the same IP address used in rule 00120.
#$cmd 00360 allow udp from any to x.x.x.x 67 in via $pif keep-state

# Allow HTTP connections to internal web server
$cmd 00400 allow tcp from any to me 80 in via $pif setup limit src-addr 2

# Allow inbound SSH connections
$cmd 00410 allow tcp from any to me 22 in via $pif setup limit src-addr 2

# Reject and log all other incoming connections
$cmd 00499 deny log all from any to any in via $pif
....

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

[.programlisting]
....
# Everything else is denied and logged
$cmd 00999 deny log all from any to any
....

[[in-kernel-nat]]
=== NAT в ядре системы

Межсетевой экран IPFW в FreeBSD имеет две реализации NAT: реализацию в пользовательском пространстве man:natd[8] и более новую реализацию NAT в ядре. Обе работают совместно с IPFW для преобразования сетевых адресов. Это можно использовать для организации общего доступа в Интернет, чтобы несколько внутренних компьютеров могли подключаться к Интернету, используя один публичный IP-адрес.

Для этого машина FreeBSD, подключённая к Интернету, должна выступать в роли шлюза. Эта система должна иметь две сетевые карты (NIC), где одна подключена к Интернету, а другая — к внутренней локальной сети (LAN). Каждой машине в LAN должен быть назначен IP-адрес из частного адресного пространства, как определено в https://www.ietf.org/rfc/rfc1918.txt[RFC 1918].

Для включения встроенной в ядро функции NAT в IPFW требуется дополнительная настройка. Чтобы поддержка NAT на уровне ядра включалась при загрузке, необходимо добавить следующие параметры в [.filename]#/etc/rc.conf#:

[.programlisting]
....
gateway_enable="YES"
firewall_enable="YES"
firewall_nat_enable="YES"
....

[NOTE]
====
Когда `firewall_nat_enable` установлен, но `firewall_enable` нет, это не будет иметь эффекта и ничего не сделает. Это связано с тем, что реализация NAT в ядре совместима только с IPFW.
====

Когда набор правил содержит правила с отслеживанием состояния, позиционирование правила NAT критически важно, и используется действие `skipto`. Действие `skipto` требует указания номера правила, чтобы знать, к какому правилу перейти. В приведённом ниже примере расширяется набор правил межсетевого экрана, показанный в предыдущем разделе. В него добавлены некоторые новые записи и изменены существующие правила для настройки межсетевого экрана с поддержкой NAT, встроенного в ядро. Начинается он с добавления дополнительных переменных, представляющих номер правила для перехода, опцию `keep-state` и список TCP-портов, который будет использоваться для сокращения количества правил.

[.programlisting]
....
#!/bin/sh
ipfw -q -f flush
cmd="ipfw -q add"
skip="skipto 1000"
pif=dc0
ks="keep-state"
good_tcpo="22,25,37,53,80,443,110"
....

При использовании NAT в ядре необходимо отключить аппаратную сегментацию TCP (TSO) из-за архитектуры man:libalias[3] — библиотеки, реализованной в виде модуля ядра для обеспечения функциональности NAT в IPFW. TSO можно отключить для каждого сетевого интерфейса с помощью man:ifconfig[8] или для всей системы с помощью man:sysctl[8]. Чтобы отключить TSO для всей системы, необходимо добавить следующие настройки в [.filename]#/etc/sysctl.conf#:

[.programlisting]
....
net.inet.tcp.tso="0"
....

Будет также настроен экземпляр NAT. Возможно иметь несколько экземпляров NAT, каждый со своей конфигурацией. Для этого примера нужен только один экземпляр NAT — экземпляр NAT номер 1. Конфигурация может принимать несколько опций, таких как: `if`, указывающий публичный интерфейс, `same_ports`, обеспечивающий одинаковое отображение алиасов портов и локальных номеров портов, `unreg_only`, приводящий к обработке только незарегистрированных (частных) адресных пространств экземпляром NAT, и `reset`, который помогает поддерживать работоспособность экземпляра NAT даже при изменении публичного IP-адреса машины с IPFW. Для всех возможных опций, которые могут быть переданы в конфигурацию отдельного экземпляра NAT, обратитесь к man:ipfw[8]. При настройке межсетевого экрана с NAT и отслеживанием состояний необходимо разрешить повторное внедрение преобразованных пакетов в межсетевой экран для дальнейшей обработки. Это можно достичь, отключив поведение `one_pass` в начале скрипта межсетевого экрана.

[.programlisting]
....
ipfw disable one_pass
ipfw -q nat 1 config if $pif same_ports unreg_only reset
....

Правило входящего NAT вставляется _после_ двух правил, которые разрешают весь трафик на доверенных интерфейсах и интерфейсе loopback, а также после правила пересборки, но _до_ правила `check-state`. Важно, чтобы номер правила, выбранный для этого NAT-правила (в данном примере `100`), был больше, чем первые три правила, и меньше, чем правило `check-state`. Кроме того, из-за особенностей работы встроенного NAT рекомендуется размещать правило пересборки непосредственно перед первым NAT-правилом и после правил, разрешающих трафик на доверенных интерфейсах. Обычно фрагментация IP-пакетов не должна происходить, но при работе с туннелированным трафиком IPSEC/ESP/GRE это возможно, и пересборка фрагментов необходима перед передачей полного пакета встроенному механизму NAT.

[NOTE]
====
Правило пересборки не требовалось при использовании пользовательского демона man:natd[8], поскольку внутренняя работа действия `divert` в man:ipfw[8] уже обеспечивает пересборку пакетов перед их передачей в сокет, как также указано в man:ipfw[8].

Экземпляр NAT и номер правила, используемые в этом примере, не совпадают с экземпляром NAT и номером правила, созданными по умолчанию в [.filename]#rc.firewall#. [.filename]#rc.firewall# — это скрипт, который настраивает правила межсетевого экрана по умолчанию в FreeBSD.
====

[.programlisting]
....
$cmd 005 allow all from any to any via xl0  # exclude LAN traffic
$cmd 010 allow all from any to any via lo0  # exclude loopback traffic
$cmd 099 reass all from any to any in       # reassemble inbound packets
$cmd 100 nat 1 ip from any to any in via $pif # NAT any inbound packets
# Allow the packet through if it has an existing entry in the dynamic rules table
$cmd 101 check-state
....

Правила для исходящего трафика изменены, чтобы заменить действие `allow` на переменную `$skip`, указывая, что обработка правил продолжится с правила `1000`. Семь правил для `tcp` заменены правилом `125`, так как переменная `$good_tcpo` содержит семь разрешённых исходящих портов.

[NOTE]
====
Помните, что производительность IPFW в значительной степени определяется количеством правил в наборе правил межсетевого экрана.
====

[.programlisting]
....
# Authorized outbound packets
$cmd 120 $skip udp from any to x.x.x.x 53 out via $pif $ks
$cmd 121 $skip udp from any to x.x.x.x 67 out via $pif $ks
$cmd 125 $skip tcp from any to any $good_tcpo out via $pif setup $ks
$cmd 130 $skip icmp from any to any out via $pif $ks
....

Правила для входящего трафика остаются такими же, за исключением самого последнего правила, в котором удаляется `via $pif`, чтобы охватить как входящие, так и исходящие правила. Правило NAT должно следовать за этим последним исходящим правилом, иметь номер выше, чем у последнего правила, и номер правила должен быть указан в действии `skipto`. В этом наборе правил правило номер `1000` обрабатывает передачу всех пакетов в настроенный экземпляр для обработки NAT. Следующее правило разрешает прохождение любого пакета, прошедшего обработку NAT.

[.programlisting]
....
$cmd 999 deny log all from any to any
$cmd 1000 nat 1 ip from any to any out via $pif # skipto location for outbound stateful rules
$cmd 1001 allow ip from any to any
....

В этом примере правила `100`, `101`, `125`, `1000` и `1001` управляют преобразованием адресов исходящих и входящих пакетов, чтобы записи в таблице динамического состояния всегда указывали на частный LANIP-адрес.

Рассмотрим внутренний веб-браузер, который инициирует новый исходящий HTTP-сеанс через порт 80. Когда первый исходящий пакет попадает в межсетевой экран, он не соответствует правилу `100`, так как направлен наружу, а не внутрь. Он проходит правило `101`, так как это первый пакет и он ещё не был добавлен в таблицу динамического состояния. В итоге пакет соответствует правилу `125`, так как он исходящий на разрешённом порту и имеет исходный IP-адрес из внутренней LAN. При соответствии этому правилу выполняются два действия. Во-первых, действие `keep-state` добавляет запись в таблицу динамического состояния, и выполняется указанное действие `skipto rule 1000`. Затем пакет проходит NAT и отправляется в Интернет. Этот пакет достигает целевого веб-сервера, где генерируется и отправляется обратно ответный пакет. Этот новый пакет попадает в начало набора правил. Он соответствует правилу `100`, и его IP-адрес назначения преобразуется обратно в исходный внутренний адрес. Затем он обрабатывается правилом `check-state`, обнаруживается в таблице как существующий сеанс и передаётся в LAN.

На входящей стороне набор правил должен блокировать плохие пакеты и разрешать только авторизованные сервисы. Пакет, соответствующий входящему правилу, помещается в таблицу динамического состояния и выпускается в локальную сеть. Пакет, сгенерированный в ответ, распознаётся правилом `check-state` как принадлежащий существующему сеансу. Затем он отправляется к правилу `1000` для выполнения NAT перед выпуском на исходящий интерфейс.

[NOTE]
====
Переход от пользовательской реализации man:natd[8] к NAT в ядре может сначала показаться простым, но есть небольшой нюанс. При использовании GENERIC ядра IPFW загрузит модуль ядра [.filename]#libalias.ko#, когда в [.filename]#/etc/rc.conf# включена опция `firewall_nat_enable`. Модуль ядра [.filename]#libalias.ko# предоставляет только базовую функциональность NAT, в то время как пользовательская реализация man:natd[8] имеет все функции NAT в своей пользовательской библиотеке без дополнительной настройки. Под всей функциональностью подразумеваются следующие модули ядра, которые могут быть дополнительно загружены при необходимости, помимо стандартного модуля [.filename]#libalias.ko#: [.filename]#alias_ftp.ko#, [.filename]#alias_bbt.ko#, [.filename]#skinny.ko#, [.filename]#irc.ko#, [.filename]#alias_pptp.ko# и [.filename]#alias_smedia.ko# с использованием директивы `kld_list` в [.filename]#/etc/rc.conf#. Если используется собственное ядро, полная функциональность пользовательской библиотеки может быть встроена в ядро с помощью опции `options LIBALIAS`.
====

==== Перенаправление портов

Недостаток NAT в целом заключается в том, что клиенты в локальной сети недоступны из Интернета. Клиенты в локальной сети могут устанавливать исходящие соединения с внешним миром, но не могут принимать входящие. Это создаёт проблему, если необходимо запустить интернет-сервисы на одной из машин-клиентов локальной сети. Простое решение этой проблемы — перенаправление определённых портов из Интернета на машине, предоставляющей NAT, к клиенту в локальной сети.

Например, сервер IRC работает на клиенте `A`, а веб-сервер — на клиенте `B`. Для правильной работы соединения, полученные на портах 6667 (IRC) и 80 (HTTP), должны быть перенаправлены на соответствующие машины.

С встроенным в ядро NAT вся конфигурация выполняется в настройках экземпляра NAT. Полный список параметров, которые может использовать экземпляр встроенного в ядро NAT, приведен в man:ipfw[8]. Синтаксис IPFW соответствует синтаксису natd. Синтаксис для `redirect_port` выглядит следующим образом:

[.programlisting]
....
redirect_port proto targetIP:targetPORT[-targetPORT]
  [aliasIP:]aliasPORT[-aliasPORT]
  [remoteIP[:remotePORT[-remotePORT]]]
....

Для настройки приведённой выше конфигурации аргументы должны быть:

[.programlisting]
....
redirect_port tcp 192.168.0.2:6667 6667
redirect_port tcp 192.168.0.3:80 80
....

После добавления этих аргументов в конфигурацию NAT-экземпляра 1 в приведённом выше наборе правил, TCP-порты будут проброшены на клиентские машины в локальной сети, на которых работают службы IRC и HTTP.

[.programlisting]
....
ipfw -q nat 1 config if $pif same_ports unreg_only reset \
  redirect_port tcp 192.168.0.2:6667 6667 \
  redirect_port tcp 192.168.0.3:80 80
....

Диапазоны портов вместо отдельных портов могут быть указаны с помощью `redirect_port`. Например, _tcp 192.168.0.2:2000-3000 2000-3000_ перенаправит все соединения, полученные на портах с 2000 по 3000, на порты с 2000 по 3000 клиента `A`.

==== Перенаправление адресов

Перенаправление адресов полезно, если доступно более одного IP-адреса. Каждому клиенту в локальной сети может быть назначен собственный внешний IP-адрес с помощью man:ipfw[8], который затем перезаписывает исходящие пакеты от клиентов локальной сети с соответствующим внешним IP-адресом и перенаправляет весь входящий трафик для этого конкретного IP-адреса обратно к определённому клиенту локальной сети. Это также известно как статический NAT. Например, если доступны IP-адреса `128.1.1.1`, `128.1.1.2` и `128.1.1.3`, то `128.1.1.1` может использоваться как внешний IP-адрес машины с man:ipfw[8], а `128.1.1.2` и `128.1.1.3` перенаправляются обратно клиентам `A` и `B` локальной сети.

Синтаксис `redirect_addr` приведён ниже, где `localIP` — это внутренний IP-адрес клиента в локальной сети, а `publicIP` — внешний IP-адрес, соответствующий клиенту в локальной сети.

[.programlisting]
....
redirect_addr localIP publicIP
....

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

[.programlisting]
....
redirect_addr 192.168.0.2 128.1.1.2
redirect_addr 192.168.0.3 128.1.1.3
....

Как и `redirect_port`, эти аргументы размещаются в конфигурации экземпляра NAT. При перенаправлении адреса нет необходимости в перенаправлении портов, так как перенаправляются все данные, полученные на определённый IP-адрес.

Внешние IP-адреса на машине с man:ipfw[8] должны быть активны и назначены как псевдонимы внешнему интерфейсу. Подробности см. в man:rc.conf[5].

==== Пользовательский NAT

Начнем с утверждения: реализация NAT в пользовательском пространстве: man:natd[8], имеет больше накладных расходов, чем NAT в ядре. Для работы man:natd[8] по преобразованию пакетов, пакеты должны копироваться из ядра в пользовательское пространство и обратно, что создает дополнительные накладные расходы, отсутствующие при использовании NAT в ядре.

Для включения демона NAT в пользовательском пространстве man:natd[8] при загрузке, следующая минимальная конфигурация должна быть добавлена в [.filename]#/etc/rc.conf#. Параметр `natd_interface` должен быть установлен равным имени сетевого интерфейса, подключенного к Интернету. Скрипт man:rc[8] для man:natd[8] автоматически проверит, используется ли динамический IP-адрес, и настроит себя соответствующим образом.

[.programlisting]
....
gateway_enable="YES"
natd_enable="YES"
natd_interface="rl0"
....

В общем случае, приведённый выше набор правил, описанный для NAT в ядре, также может использоваться совместно с man:natd[8]. Исключениями являются настройка экземпляра NAT в ядре `(ipfw -q nat 1 config ...)`, которая не требуется вместе с правилом пересборки 99, так как его функциональность включена в действие `divert`. Правила 100 и 1000 необходимо немного изменить, как показано ниже.

[.programlisting]
....
$cmd 100 divert natd ip from any to any in via $pif
$cmd 1000 divert natd ip from any to any out via $pif
....

Для настройки перенаправления портов или адресов используется синтаксис, аналогичный NAT в ядре. Однако, в отличие от настройки в скрипте набора правил, как в случае с NAT в ядре, конфигурацию man:natd[8] лучше выполнять в конфигурационном файле. Для этого необходимо передать дополнительный флаг через [.filename]#/etc/rc.conf#, который указывает путь к конфигурационному файлу.

[.programlisting]
....
natd_flags="-f /etc/natd.conf"
....

[NOTE]
====
Указанный файл должен содержать список параметров конфигурации, по одному на строку. Для получения дополнительной информации о файле конфигурации и возможных переменных обратитесь к man:natd[8]. Ниже приведены два примера записей, по одному на строку:

[.programlisting]
....
redirect_port tcp 192.168.0.2:6667 6667
redirect_addr 192.168.0.3 128.1.1.3
....

====

[[firewalls-ipfw-cmd]]
=== Команда IPFW

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

`ipfw` — это полезный способ отображения текущих правил межсетевого экрана на экране консоли. Возможность учёта IPFW динамически создаёт счётчик для каждого правила, который подсчитывает каждый пакет, соответствующий правилу. В процессе тестирования правила, вывод правила с его счётчиком — это один из способов определить, работает ли правило так, как ожидается.

Чтобы вывести список всех активных правил в порядке их применения:

[source, shell]
....
# ipfw list
....

Для вывода всех активных правил с отметкой времени последнего совпадения правила:

[source, shell]
....
# ipfw -t list
....

Следующий пример выводит информацию о сборе статистики и количество пакетов для совпавших правил вместе с самими правилами. Первый столбец — это номер правила, за которым следует количество совпавших пакетов и байтов, а затем само правило.

[source, shell]
....
# ipfw -a list
....

Для отображения динамических правил вместе со статическими:

[source, shell]
....
# ipfw -d list
....

Чтобы также показать истекшие динамические правила:

[source, shell]
....
# ipfw -d -e list
....

Для обнуления счетчиков:

[source, shell]
....
# ipfw zero
....

Обнулить счетчики только для правила с номером _NUM_:

[source, shell]
....
# ipfw zero NUM
....

==== Журналирование сообщений межсетевого экрана

Даже при включенной функции журналирования, IPFW не будет самостоятельно генерировать записи о правилах. Администратор межсетевого экрана решает, какие правила в наборе правил будут записываться в журнал, и добавляет ключевое слово `log` к этим правилам. Обычно журналируются только правила с действием deny. Принято дублировать правило "ipfw default deny everything" с включенным ключевым словом `log` в качестве последнего правила в наборе. Таким образом, можно увидеть все пакеты, которые не соответствуют ни одному из правил в наборе.

Журналирование — это обоюдоострый меч. Если не быть осторожным, переизбыток данных журналирования или атака типа DoS могут заполнить диск файлами журналов. Сообщения журнала записываются не только в syslogd, но и выводятся на корневую консоль, что быстро становится раздражающим.

Опция ядра `IPFIREWALL_VERBOSE_LIMIT=5` ограничивает количество последовательных сообщений, отправляемых в man:syslogd[8], касающихся совпадения пакетов с заданным правилом. Когда эта опция включена в ядре, количество последовательных сообщений, касающихся определённого правила, ограничивается указанным числом. Нет никакой пользы от 200 одинаковых сообщений в журнале. При установке этого параметра в пять, пять последовательных сообщений, касающихся определённого правила, будут записаны в syslogd, а оставшиеся идентичные последовательные сообщения будут подсчитаны и отправлены в syslogd с фразой, подобной следующей:

[.programlisting]
....
last message repeated 45 times
....

Все сообщения о зарегистрированных пакетах по умолчанию записываются в [.filename]#/var/log/security#, что определено в [.filename]#/etc/syslog.conf#.

[[firewalls-ipfw-rules-script]]
==== Построение скрипта правил

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

Синтаксис примера, приведённого ниже, совместим с синтаксисом, используемым оболочками man:sh[1], man:csh[1] и man:tcsh[1]. Поля символьной подстановки (переменные — один из видов полей подстановки, прим. перев.) начинаются со знака доллара ($). Символьные поля не имеют префикса $. Значение для заполнения символьного поля должно быть заключено в двойные кавычки ("").

Начните файл правил следующим образом:

[.programlisting]
....
############### start of example ipfw rules script #############
#
ipfw -q -f flush       # Delete all rules
# Set defaults
oif="tun0"             # out interface
odns="192.0.2.11"      # ISP's DNS server IP address
cmd="ipfw -q add "     # build rule prefix
ks="keep-state"        # just too lazy to key this each time
$cmd 00500 check-state
$cmd 00502 deny all from any to any frag
$cmd 00501 deny tcp from any to any established
$cmd 00600 allow tcp from any to any 80 out via $oif setup $ks
$cmd 00610 allow tcp from any to $odns 53 out via $oif setup $ks
$cmd 00611 allow udp from any to $odns 53 out via $oif $ks
################### End of example ipfw rules script ############
....

Правила не важны, так как цель этого примера — показать, как заполняются поля символьной подстановки.

Если приведённый выше пример находится в [.filename]#/etc/ipfw.rules#, правила можно перезагрузить следующей командой:

[source, shell]
....
# sh /etc/ipfw.rules
....

[.filename]#/etc/ipfw.rules# может находиться в любом месте, и файл может иметь любое имя.

То же самое можно выполнить вручную, запустив следующие команды:

[source, shell]
....
# ipfw -q -f flush
# ipfw -q add check-state
# ipfw -q add deny all from any to any frag
# ipfw -q add deny tcp from any to any established
# ipfw -q add allow tcp from any to any 80 out via tun0 setup keep-state
# ipfw -q add allow tcp from any to 192.0.2.11 53 out via tun0 setup keep-state
# ipfw -q add 00611 allow udp from any to 192.0.2.11 53 out via tun0 keep-state
....

[[firewalls-ipfw-kernelconfig]]
=== Опции ядра для IPFW

Для статической компиляции поддержки IPFW в собственное ядро обратитесь к инструкциям в crossref:kernelconfig[kernelconfig,Настройка ядра FreeBSD]. В файле конфигурации собственного ядра доступны следующие параметры:

[.programlisting]
....
options    IPFIREWALL			# enables IPFW
options    IPFIREWALL_VERBOSE		# enables logging for rules with log keyword to syslogd(8)
options    IPFIREWALL_VERBOSE_LIMIT=5	# limits number of logged packets per-entry
options    IPFIREWALL_DEFAULT_TO_ACCEPT # sets default policy to pass what is not explicitly denied
options    IPFIREWALL_NAT		# enables basic in-kernel NAT support
options    LIBALIAS			# enables full in-kernel NAT support
options    IPFIREWALL_NAT64		# enables in-kernel NAT64 support
options    IPFIREWALL_NPTV6		# enables in-kernel IPv6 NPT support
options    IPFIREWALL_PMOD		# enables protocols modification module support
options    IPDIVERT			# enables NAT through natd(8)
....

[NOTE]
====
IPFW может быть загружен как модуль ядра: указанные выше параметры по умолчанию собираются как модули или могут быть установлены во время выполнения с помощью tunables.
====

[[firewalls-ipf]]
== IPFILTER (IPF)

IPFILTER, также известный как IPF, представляет собой кроссплатформенный межсетевой экран с открытым исходным кодом, который был портирован на несколько операционных систем, включая FreeBSD, NetBSD, OpenBSD и Solaris(TM).

IPFILTER — это механизм межсетевого экрана и NAT на уровне ядра, которым можно управлять и отслеживать его работу с помощью пользовательских программ. Правила межсетевого экрана можно устанавливать или удалять с помощью `ipf`, правила NAT — с помощью `ipnat`, статистику работы ядерной части IPFILTER в реальном времени можно выводить с помощью `ipfstat`, а `ipmon` позволяет записывать действия IPFILTER в системные журналы.

IPF изначально был написан с использованием логики обработки правил "последнее совпавшее правило побеждает" и использовал только правила без состояния. С тех пор IPF был улучшен и теперь включает опции `quick` и `keep state`.

Часто задаваемые вопросы по IPF находятся на сайте http://www.phildev.net/ipf/index.html[http://www.phildev.net/ipf/index.html]. Доступен поисковый архив списка рассылки IPFilter по адресу http://marc.info/?l=ipfilter[http://marc.info/?l=ipfilter].

Этот раздел Руководства посвящен IPF в контексте FreeBSD. В нём приведены примеры правил, содержащих опции `quick` и `keep state`.

=== Включение IPF

IPF включён в базовую установку FreeBSD в виде загружаемого модуля ядра, что означает, что для включения IPF не требуется создавать собственное ядро.

Для пользователей, которые предпочитают статически компилировать поддержку IPF в пользовательское ядро, обратитесь к инструкциям в crossref:kernelconfig[kernelconfig,Настройка ядра FreeBSD]. Доступны следующие параметры ядра:

[.programlisting]
....
options IPFILTER
options IPFILTER_LOG
options IPFILTER_LOOKUP
options IPFILTER_DEFAULT_BLOCK
....

где `options IPFILTER` включает поддержку IPFILTER, `options IPFILTER_LOG` включает журналирование IPF с использованием псевдоустройства [.filename]#ipl# для записи пакетов для каждого правила, содержащего ключевое слово `log`, `IPFILTER_LOOKUP` включает пулы IP для ускорения поиска IP, а `options IPFILTER_DEFAULT_BLOCK` изменяет поведение по умолчанию так, что любой пакет, не соответствующий правилу `pass` межсетевого экрана, блокируется.

Для настройки системы на включение IPF при загрузке добавьте следующие записи в [.filename]#/etc/rc.conf#. Эти записи также включат журналирование и политику `default pass all`. Чтобы изменить политику по умолчанию на `block all` без компиляции пользовательского ядра, не забудьте добавить правило `block all` в конец набора правил.

[.programlisting]
....
ipfilter_enable="YES"             # Start ipf firewall
ipfilter_rules="/etc/ipf.rules"   # loads rules definition text file
ipv6_ipfilter_rules="/etc/ipf6.rules" # loads rules definition text file for IPv6
ipmon_enable="YES"                # Start IP monitor log
ipmon_flags="-Ds"                 # D = start as daemon
                                  # s = log to syslog
                                  # v = log tcp window, ack, seq
                                  # n = map IP & port to names
....

Если требуется функциональность NAT, также добавьте следующие строки:

[.programlisting]
....
gateway_enable="YES"              # Enable as LAN gateway
ipnat_enable="YES"                # Start ipnat function
ipnat_rules="/etc/ipnat.rules"    # rules definition file for ipnat
....

Затем, чтобы запустить IPF сейчас:

[.programlisting]
....
#  service ipfilter start
....

Для загрузки правил межсетевого экрана укажите имя файла набора правил, используя `ipf`. Следующая команда может быть использована для замены текущих работающих правил межсетевого экрана:

[source, shell]
....
# ipf -Fa -f /etc/ipf.rules
....

где `-Fa` очищает все внутренние таблицы правил, а `-f` указывает файл, содержащий правила для загрузки.

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

Обратитесь к man:ipf[8] для получения подробностей о других флагах, доступных с этой командой.

=== Синтаксис правил IPF

В этом разделе описывается синтаксис правил IPF, используемый для создания правил с состояниями. При создании правил следует помнить, что если в правиле не указано ключевое слово `quick`, каждое правило обрабатывается по порядку, и _последнее совпавшее правило_ будет применено. Это означает, что даже если первое совпавшее правило разрешает (`pass`), но далее есть совпадающее правило, которое запрещает (`block`), пакет будет отброшен. Примеры наборов правил можно найти в [.filename]#/usr/share/examples/ipfilter#.

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

Ключевые слова, используемые в правилах, должны быть записаны в определённом порядке, слева направо. Некоторые ключевые слова являются обязательными, а другие — опциональными. Некоторые ключевые слова имеют подопции, которые могут сами быть ключевыми словами и включать дополнительные подопции. Порядок ключевых слов следующий, где слова, написанные в ВЕРХНЕМ РЕГИСТРЕ, представляют переменную, а слова, написанные в нижнем регистре, должны предшествовать следующей за ними переменной:

`_ACTION DIRECTION OPTIONS proto PROTO_TYPE from SRC_ADDR SRC_PORT to DST_ADDR DST_PORT TCP_FLAG|ICMP_TYPE keep state STATE_`

В этом разделе описываются ключевые слова и их параметры. Это не исчерпывающий список всех возможных параметров. Полное описание синтаксиса правил, который можно использовать при создании правил IPF, а также примеры использования каждого ключевого слова приведены в man:ipf[5].

ACTION::
Ключевое слово `action` указывает действие — что делать с пакетом, если он соответствует данному правилу. Каждое правило _должно_ иметь действие. Поддерживаются следующие действия:
+
`block`: отбрасывает пакет.
+
`pass`: разрешает пакет.
+
`log`: создает запись в журнале.
+
`count`: подсчитывает количество пакетов и байтов, что может дать представление о том, как часто используется правило.
+
`auth`: ставит пакет в очередь для дальнейшей обработки другой программой.
+
`call`: предоставляет доступ к функциям, встроенным в IPF, которые позволяют выполнять более сложные действия.
+
`decapsulate`: удаляет любые заголовки для обработки содержимого пакета.

DIRECTION::
Далее, каждое правило должно явно указывать `direction ` — направление трафика с использованием одного из следующих ключевых слов:
+
`in`: правило применяется к входящему пакету.
+
`out`: правило применяется к исходящему пакету.
+
`all`: правило применяется в обоих направлениях.
+
Если в системе несколько интерфейсов, можно указать интерфейс вместе с направлением. Примером может быть `in on fxp0`.

OPTIONS::
`options` — параметры необязательны. Однако, если указано несколько параметров, они должны использоваться в порядке, указанном здесь.
+
`log`: при выполнении указанного ДЕЙСТВИЯ содержимое заголовков пакета будет записано в псевдоустройство журнала пакетов man:ipl[4].
+
`quick`: если пакет соответствует этому правилу, происходит ДЕЙСТВИЕ, указанное в правиле, и дальнейшая обработка последующих правил для этого пакета не выполняется.
+
`on`: должен сопровождаться именем интерфейса, как отображается в man:ifconfig[8]. Правило будет применяться только в том случае, если пакет проходит через указанный интерфейс в указанном направлении.
+
При использовании ключевого слова `log` следующие квалификаторы могут быть указаны в таком порядке:
+
`body`: указывает, что первые 128 байт содержимого пакета будут записаны в журнал после заголовков.
+
`first`: если ключевое слово `log` используется вместе с опцией `keep state`, рекомендуется использовать эту опцию, чтобы регистрировался только инициирующий пакет, а не каждый пакет, соответствующий соединению с отслеживанием состояний.
+
Дополнительные параметры доступны для указания сообщений об ошибках. Подробности смотрите в man:ipf[5].

PROTO_TYPE::
Тип протокола является необязательным. Однако он обязателен, если в правиле необходимо указать SRC_PORT или DST_PORT, так как он определяет тип протокола. При указании типа протокола используйте ключевое слово `proto`, за которым следует номер протокола или его имя из [.filename]#/etc/protocols#. Примеры названий протоколов: `tcp`, `udp` или `icmp`. Если указан PROTO_TYPE, но не указаны SRC_PORT или DST_PORT, все номера портов для этого протокола будут соответствовать этому правилу.

SRC_ADDR::
Ключевое слово `from` является обязательным и сопровождается ключевым словом, обозначающим источник пакета. Источником может быть имя хоста, IP-адрес с указанием маски CIDR, пул адресов или ключевое слово `all`. Примеры можно найти в man:ipf[5].
+
Единственный способ задать диапазоны IP-адресов, это представить их в нотации — числа, разделенные точками / длина маски. Пакет или порт package:net-mgmt/ipcalc[] может быть использован для упрощения расчета маски CIDR. Дополнительная информация доступна на веб-странице утилиты: http://jodies.de/ipcalc[http://jodies.de/ipcalc].

SRC_PORT::
Номер порта источника является необязательным. Однако, если он используется, требуется, чтобы PROTO_TYPE был сначала определён в правиле. Номер порта также должен предваряться ключевым словом `proto`.
+
Поддерживается несколько различных операторов сравнения: `=` (равно), `!=` (не равно), `<` (меньше), `>` (больше), `<=` (меньше или равно) и `>=` (больше или равно).
+
Для указания диапазона портов поместите два номера порта между `<>` (меньше и больше), `><` (больше и меньше) или `:` (больше или равно и меньше или равно).

DST_ADDR::
Ключевое слово `to` является обязательным и за ним следует ключевое слово, обозначающее назначение пакета. Аналогично SRC_ADDR, это может быть имя хоста, IP-адрес с маской CIDR, пул адресов или ключевое слово `all`.

DST_PORT::
Аналогично SRC_PORT, номер порта назначения является необязательным. Однако, если он используется, требуется, чтобы PROTO_TYPE был сначала определён в правиле. Номер порта также должен предваряться ключевым словом `proto`.

TCP_FLAG|ICMP_TYPE::
Если в качестве `PROTO_TYPE` указан `tcp`, флаги могут быть заданы в виде букв, где каждая буква представляет один из возможных флагов TCP, используемых для определения состояния соединения. Возможные значения: `S` (SYN), `A` (ACK), `P` (PSH), `F` (FIN), `U` (URG), `R` (RST), `C` (CWN) и `E` (ECN).
+
Если указан `icmp` в качестве PROTO_TYPE, можно указать тип ICMP для сопоставления. Допустимые типы приведены в man:ipf[5].

STATE::
Если правило `pass` содержит `keep state`, IPF добавит запись в свою таблицу динамического состояния и разрешит последующие пакеты, соответствующие соединению. IPF может отслеживать состояние сеансов TCP, UDP и ICMP. Любой пакет, который IPF может однозначно идентифицировать как часть активного сеанса, даже если это другой протокол, будет разрешён.
+
В IPF пакеты, предназначенные для выхода через интерфейс, подключенный к публичному интернету, сначала проверяются в динамической таблице состояний. Если пакет соответствует следующему ожидаемому пакету активного сеанса связи, он проходит через межсетевой экран, и состояние потока сеанса обновляется в динамической таблице состояний. Пакеты, не принадлежащие к уже активному сеансу, проверяются в соответствии с набором правил для исходящего трафика. Пакеты, поступающие через интерфейс, подключенный к публичному интернету, сначала проверяются в динамической таблице состояний. Если пакет соответствует следующему ожидаемому пакету активного сеанса, он проходит через межсетевой экран, и состояние потока сеанса обновляется в динамической таблице состояний. Пакеты, не принадлежащие к уже активному сеансу, проверяются в соответствии с набором правил для входящего трафика.
+
Несколько ключевых слов могут быть добавлены после `keep state`. Если они используются, эти ключевые слова устанавливают различные параметры, управляющие фильтрацией с отслеживанием статуса, такие как установка ограничений на соединения или времени жизни соединения. Подробный список доступных параметров и их описания можно найти в man:ipf[5].

=== Пример набора правил

Этот раздел демонстрирует, как создать пример набора правил, который разрешает только сервисы, соответствующие правилам `pass`, и блокирует все остальные.

FreeBSD использует loopback-интерфейс (интерфейс обратной петли) ([.filename]#lo0#) и IP-адрес `127.0.0.1` для внутреннего взаимодействия. Набор правил межсетевого экрана должен включать правила, разрешающие свободное перемещение этих внутренних пакетов:

[.programlisting]
....
# no restrictions on loopback interface
pass in quick on lo0 all
pass out quick on lo0 all
....

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

Эти два правила разрешают весь трафик через доверенный интерфейс LAN с именем [.filename]#xl0#:

[.programlisting]
....
# no restrictions on inside LAN interface for private network
pass out quick on xl0 all
pass in quick on xl0 all
....

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

Этот набор правил определяет исходящий раздел общедоступного интерфейса с именем [.filename]#dc0#. Эти правила сохраняют состояние и определяют конкретные службы, для которых внутренние системы авторизованы для доступа к общедоступному Интернету. Все правила используют `quick` и указывают соответствующие номера портов и, где применимо, адреса назначения.

[.programlisting]
....
# interface facing Internet (outbound)
# Matches session start requests originating from or behind the
# firewall, destined for the Internet.

# Allow outbound access to public DNS servers.
# Replace x.x.x.x with address listed in /etc/resolv.conf.
# Repeat for each DNS server.
pass out quick on dc0 proto tcp from any to x.x.x.x port = 53 flags S keep state
pass out quick on dc0 proto udp from any to x.x.x.x port = 53 keep state

# Allow access to ISP's specified DHCP server for cable or DSL networks.
# Use the first rule, then check log for the IP address of DHCP server.
# Then, uncomment the second rule, replace z.z.z.z with the IP address,
# and comment out the first rule
pass out log quick on dc0 proto udp from any to any port = 67 keep state
#pass out quick on dc0 proto udp from any to z.z.z.z port = 67 keep state

# Allow HTTP and HTTPS
pass out quick on dc0 proto tcp from any to any port = 80 flags S keep state
pass out quick on dc0 proto tcp from any to any port = 443 flags S keep state

# Allow email
pass out quick on dc0 proto tcp from any to any port = 110 flags S keep state
pass out quick on dc0 proto tcp from any to any port = 25 flags S keep state

# Allow NTP
pass out quick on dc0 proto tcp from any to any port = 37 flags S keep state

# Allow FTP
pass out quick on dc0 proto tcp from any to any port = 21 flags S keep state

# Allow SSH
pass out quick on dc0 proto tcp from any to any port = 22 flags S keep state

# Allow ping
pass out quick on dc0 proto icmp from any to any icmp-type 8 keep state

# Block and log everything else
block out log first quick on dc0 all
....

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

[.programlisting]
....
# interface facing Internet (inbound)
# Block all inbound traffic from non-routable or reserved address spaces
block in quick on dc0 from 192.168.0.0/16 to any    #RFC 1918 private IP
block in quick on dc0 from 172.16.0.0/12 to any     #RFC 1918 private IP
block in quick on dc0 from 10.0.0.0/8 to any        #RFC 1918 private IP
block in quick on dc0 from 127.0.0.0/8 to any       #loopback
block in quick on dc0 from 0.0.0.0/8 to any         #loopback
block in quick on dc0 from 169.254.0.0/16 to any    #DHCP auto-config
block in quick on dc0 from 192.0.2.0/24 to any      #reserved for docs
block in quick on dc0 from 204.152.64.0/23 to any   #Sun cluster interconnect
block in quick on dc0 from 224.0.0.0/3 to any       #Class D & E multicast

# Block fragments and too short tcp packets
block in quick on dc0 all with frags
block in quick on dc0 proto tcp all with short

# block source routed packets
block in quick on dc0 all with opt lsrr
block in quick on dc0 all with opt ssrr

# Block OS fingerprint attempts and log first occurrence
block in log first quick on dc0 proto tcp from any to any flags FUP

# Block anything with special options
block in quick on dc0 all with ipopts

# Block public pings and ident
block in quick on dc0 proto icmp all icmp-type 8
block in quick on dc0 proto tcp from any to any port = 113

# Block incoming Netbios services
block in log first quick on dc0 proto tcp/udp from any to any port = 137
block in log first quick on dc0 proto tcp/udp from any to any port = 138
block in log first quick on dc0 proto tcp/udp from any to any port = 139
block in log first quick on dc0 proto tcp/udp from any to any port = 81
....

Всякий раз, когда регистрируются сообщения о правиле с опцией `log first`, выполните команду `ipfstat -hio`, чтобы оценить, сколько раз правило сработало. Большое количество срабатываний может указывать на то, что система подвергается атаке.

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

[.programlisting]
....
# Allow traffic in from ISP's DHCP server. Replace z.z.z.z with
# the same IP address used in the outbound section.
pass in quick on dc0 proto udp from z.z.z.z to any port = 68 keep state

# Allow public connections to specified internal web server
pass in quick on dc0 proto tcp from any to x.x.x.x port = 80 flags S keep state

# Block and log only first occurrence of all remaining traffic.
block in log first quick on dc0 all
....

=== Настройка NAT

Чтобы включить NAT, добавьте следующие выражения в [.filename]#/etc/rc.conf# и укажите имя файла, содержащего правила NAT:

[.programlisting]
....
gateway_enable="YES"
ipnat_enable="YES"
ipnat_rules="/etc/ipnat.rules"
....

Правила NAT гибки и могут выполнять множество различных задач, подходящих как для коммерческих, так и для домашних пользователей. Синтаксис правил, представленный здесь, упрощен для демонстрации типичного использования. Полное описание синтаксиса правил можно найти в man:ipnat[5].

Базовая синтаксическая структура правила NAT выглядит следующим образом, где `map` начинает правило, а _IF_ следует заменить на имя внешнего интерфейса:

[.programlisting]
....
map IF LAN_IP_RANGE -> PUBLIC_ADDRESS
....

_LAN_IP_RANGE_ — это диапазон IP-адресов, используемых внутренними клиентами. Обычно это частный диапазон адресов, например `192.168.1.0/24`. _PUBLIC_ADDRESS_ может быть либо статическим внешним IP-адресом, либо ключевым словом `0/32`, которое представляет IP-адрес, назначенный _IF_.

В IPF, когда пакет прибывает на межсетевой экран из локальной сети с публичным адресом назначения, он сначала проходит через исходящие правила набора правил межсетевого экрана. Затем пакет передаётся в набор правил NAT, который читается сверху вниз, и первое совпадающее правило применяется. IPF проверяет каждое правило NAT на соответствие имени интерфейса и исходному IP-адресу пакета. Когда имя интерфейса пакета совпадает с правилом NAT, исходный IP-адрес пакета в частной локальной сети проверяется на вхождение в диапазон IP-адресов, указанный в _LAN_IP_RANGE_. При совпадении исходный IP-адрес пакета перезаписывается публичным IP-адресом, указанным в _PUBLIC_ADDRESS_. IPF добавляет запись в свою внутреннюю таблицу NAT, чтобы при возврате пакета из Интернета он мог быть сопоставлен с исходным частным IP-адресом перед передачей в набор правил межсетевого экрана для дальнейшей обработки.

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

Первый метод заключается в назначении диапазона портов для использования в качестве исходных портов. Добавление ключевого слова `portmap` позволяет указать NAT использовать только исходные порты из заданного диапазона:

[.programlisting]
....
map dc0 192.168.1.0/24 -> 0/32 portmap tcp/udp 20000:60000
....

Или используйте ключевое слово `auto`, которое указывает NAT определить порты, доступные для использования:

[.programlisting]
....
map dc0 192.168.1.0/24 -> 0/32 portmap tcp/udp auto
....

Второй метод заключается в использовании пула публичных адресов. Это полезно, когда количество локальных адресов слишком велико, чтобы уместиться в один публичный адрес, и доступен блок публичных IP-адресов. Эти публичные адреса могут использоваться как пул, из которого NAT выбирает IP-адрес для отображения адреса пакета при его выходе.

Диапазон публичных IP-адресов может быть указан с использованием маски сети или нотации CIDR. Эти два правила эквивалентны:

[.programlisting]
....
map dc0 192.168.1.0/24 -> 204.134.75.0/255.255.255.0
map dc0 192.168.1.0/24 -> 204.134.75.0/24
....

Распространённой практикой является размещение общедоступного веб-сервера или почтового сервера в изолированном сегменте внутренней сети. Трафик с этих серверов всё равно проходит через NAT, но требуется перенаправление портов для направления входящего трафика на нужный сервер. Например, чтобы сопоставить веб-сервер с внутренним адресом `10.0.10.25` с его публичным IP-адресом `20.20.20.5`, используйте следующее правило:

[.programlisting]
....
rdr dc0 20.20.20.5/32 port 80 -> 10.0.10.25 port 80
....

Если это единственный веб-сервер, это правило также будет работать, так как оно перенаправляет все внешние HTTP-запросы на `10.0.10.25`:

[.programlisting]
....
rdr dc0 0.0.0.0/0 port 80 -> 10.0.10.25 port 80
....

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

В этом примере первое правило вызывает прокси для исходящего FTP-трафика из внутренней локальной сети. Второе правило пропускает FTP-трафик из межсетевого экрана в Интернет, а третье правило обрабатывает весь не-FTP трафик из внутренней локальной сети:

[.programlisting]
....
map dc0 10.0.10.0/29 -> 0/32 proxy port 21 ftp/tcp
map dc0 0.0.0.0/0 -> 0/32 proxy port 21 ftp/tcp
map dc0 10.0.10.0/29 -> 0/32
....

Правила FTP `map` размещаются перед правилом NAT, так что при совпадении пакета с правилом FTP прокси-сервер FTP создает временные правила фильтрации, позволяющие пакетам FTP-сессии проходить и подвергаться NAT. Все пакеты из локальной сети, не относящиеся к FTP, не будут соответствовать правилам FTP, но подвергнутся NAT, если они соответствуют третьему правилу.

Без FTP-прокси потребуются следующие правила межсетевого экрана. Обратите внимание, что без прокси необходимо разрешить все порты выше `1024`:

[.programlisting]
....
# Allow out LAN PC client FTP to public Internet
# Active and passive modes
pass out quick on rl0 proto tcp from any to any port = 21 flags S keep state

# Allow out passive mode data channel high order port numbers
pass out quick on rl0 proto tcp from any to any port > 1024 flags S keep state

# Active mode let data channel in from FTP server
pass in quick on rl0 proto tcp from any to any port = 20 flags S keep state
....

Всякий раз, когда редактируется файл, содержащий правила NAT, выполните `ipnat` с `-CF`, чтобы удалить текущие правила NAT и очистить содержимое таблицы динамической трансляции. Используйте `-f` и укажите имя набора правил NAT для загрузки:

[source, shell]
....
# ipnat -CF -f /etc/ipnat.rules
....

Для отображения статистики NAT:

[source, shell]
....
# ipnat -s
....

Для отображения текущих сопоставлений таблицы NAT:

[source, shell]
....
# ipnat -l
....

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

[source, shell]
....
# ipnat -v
....

=== Просмотр статистики IPF

В пакет IPF входит программа man:ipfstat[8], которую можно использовать для получения и отображения статистики, собираемой при совпадении пакетов с правилами при прохождении через межсетевой экран. Статистика накапливается с момента последнего запуска межсетевого экрана или с момента последнего сброса статистики в ноль с помощью команды `ipf -Z`.

Вывод `ipfstat` по умолчанию выглядит следующим образом:

[source, shell]
....
input packets: blocked 99286 passed 1255609 nomatch 14686 counted 0
 output packets: blocked 4200 passed 1284345 nomatch 14687 counted 0
 input packets logged: blocked 99286 passed 0
 output packets logged: blocked 0 passed 0
 packets logged: input 0 output 0
 log failures: input 3898 output 0
 fragment state(in): kept 0 lost 0
 fragment state(out): kept 0 lost 0
 packet state(in): kept 169364 lost 0
 packet state(out): kept 431395 lost 0
 ICMP replies: 0 TCP RSTs sent: 0
 Result cache hits(in): 1215208 (out): 1098963
 IN Pullups succeeded: 2 failed: 0
 OUT Pullups succeeded: 0 failed: 0
 Fastroute successes: 0 failures: 0
 TCP cksum fails(in): 0 (out): 0
 Packet log flags set: (0)
....

Доступно несколько вариантов. При указании `-i` для входящего или `-o` для исходящего трафика команда получит и отобразит соответствующий список правил фильтрации, установленных и используемых ядром. Чтобы также увидеть номера правил, добавьте `-n`. Например, `ipfstat -on` выводит таблицу исходящих правил с номерами:

[source, shell]
....
@1 pass out on xl0 from any to any
@2 block out on dc0 from any to any
@3 pass out quick on dc0 proto tcp/udp from any to any keep state
....

Включите `-h`, чтобы добавить перед каждым правилом количество его совпадений. Например, `ipfstat -oh` выводит таблицу внутренних правил для исходящего трафика, добавляя перед каждым правилом количество его использований:

[source, shell]
....
2451423 pass out on xl0 from any to any
354727 block out on dc0 from any to any
430918 pass out quick on dc0 proto tcp/udp from any to any keep state
....

Для отображения таблицы состояний в формате, аналогичном man:top[1], используйте `ipfstat -t`. Когда межсетевой экран подвергается атаке, эта опция позволяет идентифицировать и просматривать атакующие пакеты. Дополнительные подфлаги дают возможность выбора IP-адреса назначения или источника, порта или протокола для мониторинга в реальном времени. Подробности смотрите в man:ipfstat[8].

=== Журналирование IPF

IPF предоставляет `ipmon`, который может использоваться для записи информации журналирования межсетевого экрана в удобочитаемом формате. Для этого требуется сначала добавить `options IPFILTER_LOG` в собственное ядро, следуя инструкциям в crossref:kernelconfig[kernelconfig,Настройка ядра FreeBSD].

Эта команда обычно запускается в режиме демона для обеспечения непрерывного ведения системного журнала, чтобы можно было просматривать записи о прошлых событиях. Поскольку FreeBSD имеет встроенное средство man:syslogd[8] для автоматической ротации системных журналов, параметр `ipmon_flags` по умолчанию в [.filename]#rc.conf# использует `-Ds`:

[.programlisting]
....
ipmon_flags="-Ds" # D = start as daemon
                  # s = log to syslog
                  # v = log tcp window, ack, seq
                  # n = map IP & port to names
....

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

После включения системы журналирования в [.filename]#rc.conf# и запуска с помощью `service ipmon start`, IPF будет записывать в журнал только те правила, которые содержат ключевое слово `log`. Администратор межсетевого экрана решает, какие правила из набора должны записываться в журнал, и обычно регистрируются только правила с deny. Обычно ключевое слово `log` включают в последнее правило набора. Это позволяет увидеть все пакеты, которые не соответствуют ни одному из правил набора.

По умолчанию режим `ipmon -Ds` использует `local0` как средство ведения журнала. Для дальнейшего разделения регистрируемых данных можно использовать следующие уровни ведения журнала:

[source, shell]
....
LOG_INFO - packets logged using the "log" keyword as the action rather than pass or block.
LOG_NOTICE - packets logged which are also passed
LOG_WARNING - packets logged which are also blocked
LOG_ERR - packets which have been logged and which can be considered short due to an incomplete header
....

Чтобы настроить IPF для записи всех данных в [.filename]#/var/log/ipfilter.log#, сначала создайте пустой файл:

[source, shell]
....
# touch /var/log/ipfilter.log
....

Затем, чтобы записывать все журналируемые сообщения в указанный файл, добавьте следующую строку в [.filename]#/etc/syslog.conf#:

[.programlisting]
....
local0.* /var/log/ipfilter.log
....

Для активации изменений и указания man:syslogd[8] прочитать изменённый файл [.filename]#/etc/syslog.conf#, выполните команду `service syslogd reload`.

Не забудьте отредактировать [.filename]#/etc/newsyslog.conf# для ротации нового файла журнала.

Сообщения, генерируемые `ipmon`, состоят из полей данных, разделённых пробелами. Общие для всех сообщений поля:

. The date of packet receipt.
. Время получения пакета. Указывается в формате ЧЧ:ММ:СС.Д, где ЧЧ - часы, ММ - минуты, СС - секунды, а Д - доли секунды.
. Имя интерфейса, обработавшего пакет.
. Группа и номер правила в формате `@0:17`.
. Действие: `p` — пропущено, `b` — заблокировано, `S` — короткий пакет, `n` — не совпало ни с одним правилом, `L` — правило для журналирования.
. Адреса записываются в виде трёх полей: исходный адрес и порт, разделённые запятой, символ `->` и адрес назначения с портом. Например: `209.53.17.22,80 -> 198.73.220.17,1722`.
. `PR` с указанием имени или номера протокола: например, `PR tcp`.
. `len`, за которым следует длина заголовка и общая длина пакета: например, `len 20 40`.

Если пакет является TCP-пакетом, будет дополнительное поле, начинающееся с дефиса, за которым следуют буквы, соответствующие установленным флагам. Список букв и соответствующих флагов приведен в man:ipf[5].

Если пакет является ICMP-пакетом, в конце будут два поля: первое всегда `icmp`, а следующее — тип ICMP-сообщения и подтип, разделённые косой чертой. Например: `icmp 3/3` для сообщения о недоступности порта.

[[firewalls-blocklistd]]
== Blocklistd

Blocklistd — это демон, который прослушивает сокеты, ожидая получения уведомлений от других демонов о неудачных или успешных попытках подключения. Он наиболее широко используется для блокировки чрезмерного количества попыток подключения к открытым портам. Типичный пример — SSH, работающий в интернете и получающий множество запросов от ботов или скриптов, пытающихся угадать пароли и получить доступ. Используя blocklistd, демон может уведомить межсетевой экран о необходимости создания правила фильтрации для блокировки чрезмерных попыток подключения с одного источника после нескольких попыток. Blocklistd был первоначально разработан в NetBSD и появился там в версии 7. FreeBSD 11 импортировал blocklistd из NetBSD (ранее он назывался blacklist).

В этой главе описывается, как настроить blocklistd, его конфигурация, а также приводятся примеры использования. Читатели должны быть знакомы с основными концепциями межсетевого экрана, такими как правила. Подробности см. в главе о межсетевом экране. В примерах используется PF, но другие межсетевые экраны, доступные в FreeBSD, также должны работать с blocklistd.

=== Включение blocklistd

Основная конфигурация для blocklistd хранится в man: blocklistd.conf[5]. Также доступны различные параметры командной строки для изменения поведения blocklistd во время выполнения. Постоянная конфигурация, сохраняемая после перезагрузок, должна храниться в [.filename]#/etc/blocklistd.conf#. Чтобы включить демон во время загрузки системы, добавьте строку `blocklistd_enable` в [.filename]#/etc/rc.conf# следующим образом:

[source, shell]
....
# sysrc blocklistd_enable=yes
....

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

[source, shell]
....
# service blocklistd start
....

=== Создание набора правил для blocklistd

Правила для blocklistd настраиваются в man: blocklistd.conf[5], по одному правилу на строку. Каждое правило содержит кортеж, разделённый пробелами или табуляциями. Правила относятся либо к `local`, либо к `remote`, что применяется соответственно к машине, на которой работает blocklistd, или к внешнему источнику.

==== Правила local

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

[.programlisting]
....
[local]
ssh             stream  *       *               *       3       24h
....

Все правила, следующие за разделом `[local]`, рассматриваются как локальные правила (что является значением по умолчанию) и применяются к локальной машине. При обнаружении раздела `[remote]` все последующие правила обрабатываются как правила для удалённых машин.

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

[.programlisting]
....
[address|interface][/mask][:port]
....

Адреса могут быть указаны в виде IPv4 в числовом формате или IPv6 в квадратных скобках. Также можно использовать имя интерфейса, например `_em0_`.

Тип сокета определяется вторым полем. TCP-сокеты имеют тип `stream`, тогда как UDP обозначается как `dgram`. В приведённом выше примере используется TCP, так как SSH работает по этому протоколу.

Протокол может быть указан в третьем поле правила blocklistd. Доступны следующие протоколы: `tcp`, `udp`, `tcp6`, `udp6` или числовое значение. Подстановочный символ, как в примере, обычно используется для соответствия всем протоколам, если нет необходимости различать трафик по определённому протоколу.

В четвёртом поле определяется эффективный пользователь или владелец процесса демона, который сообщает о событии. Здесь можно использовать имя пользователя или UID, а также подстановочный знак (см. пример правила выше).

Имя правила фильтра пакетов объявляется пятым полем, которое начинает поведенческую часть правила. По умолчанию, blocklistd помещает все блокировки в якорь pf под названием `blocklistd ` в [.filename]#pf.conf# следующим образом:

[.programlisting]
....
anchor "blocklistd/*" in on $ext_if
block in
pass out
....

Для отдельных списков блокировки в этом поле можно использовать имя якоря. В остальных случаях подойдет подстановочный знак. Если имя начинается с дефиса (`-`), это означает, что следует использовать якорь с добавленным именем правила по умолчанию. Модифицированный пример из вышеприведённого с использованием дефиса будет выглядеть так:

[.programlisting]
....
ssh             stream  *       *               -ssh       3       24h
....

С таким правилом любые новые правила из списка блокировки добавляются к якорю с именем `blocklistd-ssh`.

Для блокировки целых подсетей за одно нарушение правила можно использовать символ `/` в имени правила. Это приводит к тому, что оставшаяся часть имени интерпретируется как маска, применяемая к адресу, указанному в правиле. Например, это правило заблокирует все адреса, входящие в подсеть `/24`.

[.programlisting]
....
22              stream  tcp       *               */24    3       24h
....

[NOTE]
====
Здесь важно указать правильный протокол. IPv4 и IPv6 обрабатывают /24 по-разному, поэтому `*` нельзя использовать в третьем поле для этого правила.
====

Это правило определяет, что если любой хост в этой сети ведёт себя неправильно, всё остальное в этой сети также будет заблокировано.

Шестое поле, называемое `nfail`, устанавливает количество неудачных попыток входа, необходимых для внесения IP-адреса внешнего хоста в чёрный список. Если в этой позиции используется подстановочный знак, это означает, что блокировки никогда не будут происходить. В приведённом выше примере правила установлен лимит в три попытки, что означает, что после трёх попыток входа через SSH с одного соединения IP-адрес будет заблокирован.

Последнее поле в определении правила blocklistd указывает, как долго хост будет находиться в чёрном списке. По умолчанию единицей измерения являются секунды, но также можно указать суффиксы `m`, `h` и `d` для минут, часов и дней соответственно.

Пример правила в полном объёме означает, что после трёх попыток аутентификации по SSH будет создано новое правило блокировки PF для этого хоста. Совпадения правил проверяются путем последовательной проверки локальных правил от наиболее специфичных к наименее специфичным. Когда совпадение найдено, применяются правила `remote`, а поля `name`, `nfail` и `duration` изменяются в соответствии с совпавшим правилом `remote`.

==== Правила remote

Удалённые правила используются для указания того, как blocklistd изменяет своё поведение в зависимости от удалённого хоста, который в данный момент оценивается. Каждое поле в удалённом правиле совпадает с таковым в локальном правиле. Единственное различие заключается в том, как blocklistd их использует. Для объяснения используется следующее примерное правило:

[.programlisting]
....
[remote]
203.0.113.128/25 *      *       *               =/25    =       48h
....

Поле `address` может содержать IP-адрес (как v4, так и v6), порт или оба варианта. Это позволяет задавать специальные правила для определённого диапазона удалённых адресов, как в этом примере. Поля для типа сокета, протокола и владельца интерпретируются так же, как в локальном правиле.

Поле name отличается: знак равенства (`=`) в удаленном правиле указывает blocklistd использовать значение из соответствующего локального правила. Это означает, что запись правила межсетевого экрана берется, и добавляется префикс `/25` (маска сети `255.255.255.128`). Когда соединение из этого диапазона адресов попадает в черный список, затрагивается вся подсеть. Здесь также можно использовать имя якоря PF, и в этом случае blocklistd добавит правила для этого блока адресов в якорь с указанным именем. Если указана подстановка, используется таблица по умолчанию.

В столбце `nfail` можно задать произвольное количество неудачных попыток для адреса. Это полезно для исключений из конкретного правила, например, чтобы разрешить кому-то менее строгое применение правил или немного больше попыток входа. Блокировка отключается, если в шестом поле указана звёздочка.

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

=== Конфигурация клиента blocklistd

В FreeBSD есть несколько программных пакетов, которые могут использовать функциональность blocklistd. Два наиболее заметных — это package:ftp/freebsd-ftpd[] и man:sshd[8], предназначенные для блокировки чрезмерных попыток подключения. Чтобы активировать blocklistd в демоне SSH, добавьте следующую строку в [.filename]#/etc/ssh/sshd_config#:

[.programlisting]
....
UseBlocklist yes
....

Перезапустите sshd, чтобы изменения вступили в силу.

Cписок блокировки для package:ftp/freebsd-ftpd[] включается с помощью `-B`, либо в [.filename]#/etc/inetd.conf#, либо как флаг в [.filename]#/etc/rc.conf# следующим образом:

[.programlisting]
....
ftpd_flags="-B"
....

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

=== Управление blocklistd

Blocklistd предоставляет пользователю утилиту управления под названием man:blocklistctl[8]. Она отображает заблокированные адреса и сети, которые внесены в список блокировки согласно правилам, определённым в man:blacklistd.conf[5]. Чтобы просмотреть список текущих заблокированных хостов, используйте команду `dump` с параметром `-b`, например.

[source, shell]
....
# blocklistctl dump -b
rulename                address/ma:port id      nfail   last access
blocklistd        213.0.123.128/25:22   OK      6/3     2019/06/08 14:30:19
....

Этот пример показывает, что было 6 попыток из трёх разрешённых на порт 22, исходящих из диапазона адресов `213.0.123.128/25`. Количество попыток превышает разрешённое, потому что SSH позволяет клиенту выполнять несколько попыток входа в рамках одного TCP-соединения. Текущее соединение не прерывается blocklistd. Последняя попытка соединения указана в столбце `last access` вывода.

Чтобы увидеть оставшееся время, в течение которого этот хост будет в списке блокировки, добавьте `-r` к предыдущей команде.

[source, shell]
....
# blocklistctl dump -br
rulename                address/ma:port id      nfail   remaining time
blocklistd        213.0.123.128/25:22   OK      6/3     36s
....

В этом примере осталось 36 секунд, пока этот хост не будет разблокирован.

=== Удаление узлов из списка блокировки

Иногда необходимо удалить узел из чёрного списка до истечения оставшегося времени. К сожалению, в blocklistd нет функциональности для этого. Однако можно удалить адрес из таблицы PF с помощью pfctl. Для каждого заблокированного порта существует дочерний якорь внутри якоря blocklistd, определённого в [.filename]#/etc/pf.conf#. Например, если есть дочерний якорь для блокировки порта 22, он называется `blocklistd/22`. Внутри этого дочернего якоря находится таблица, содержащая заблокированные адреса. Эта таблица называется port с указанием номера порта. В данном примере она будет называться `port22`. Имея эту информацию, можно использовать man:pfctl[8] для отображения всех перечисленных адресов следующим образом:

[source, shell]
....
# pfctl -a blocklistd/22 -t port22 -T show
...
213.0.123.128/25
...
....

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

[source, shell]
....
# pfctl -a blocklistd/22 -t port22 -T delete 213.0.123.128/25
....

Адрес теперь удалён из PF, но всё ещё будет отображаться в списке blocklistctl, так как он не знает о внесённых в PF изменениях. Запись в базе данных blocklistd со временем истечёт и будет удалена из вывода. Запись будет добавлена снова, если хост снова совпадёт с одним из правил блокировки в blocklistd.
