Настройка простого firewall и NAT на pf во FreeBSD

Что имеем: Установленный и настроенный FreeBSD 8.1 с настроенным ipfw фаерволом.
Что хотим получить: То же самое но с pf фаерволом.
Зачем? В pf больше синтаксис понравился и порядок обработки правил.

Firewall pf был портирован в FreeBSD из OpenBSD. На сегодняшний день актуальная версия pf в FreeBSD соответствует версии pf из OpenBSD 4.1 (2007 год). Вскоре планируется портирование pf из OpenBSD 4.5. На версию стоит обращать внимание т.к. в pf OpenBSD 4.7+ есть значительные изменения синтаксиса, т.е. читая руководства по новым версиям pf firewall - можно получить мало актуальную для FreeBSD информацию. В частности NAT и перенаправление портов в pf из OpenBSD 4.1 настраивается отлично от pf из OpenBSD 4.7+.

Как получилось что захотелось ставить pf, если был настроенный ipfw? Читал статьи о настройке firewall, среди них были и о настройке firewall на базе pf. Обратил внимание на красивый/лаконичный синтаксис. Так же хвалили производительность NAT в pf. Хотя с нынешними мощностями процессора это малоактуально для небольших firewall-ов. У меня и natd на Duron 1.8 спокойно 10 Мегабайт в секунду пропускал, при этом загрузка процессора была порядка 10-15%.
В общем решил попробовать настроить простой pf firewall аналогичный уже настроенному ipfw, так сказать посмотреть как он выглядит в реальной жизни, за рамками сухих строчек руководств. Если понравится, буду использовать pf firewall в дальнейшем.
Подготовка
Собираем ядро с поддержкой pf

Дописываем в конфигурационный файл для ядра:
device pf
device pflog
# QOS and bandwidth control
options ALTQ
options ALTQ_CBQ # Class Bases 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)

Если нужна поддержка CARP и кластеров для firewall-а то добавляем еще:
device pfsync

Собираем ядро (см. собираем_мир_и_ядро), перезагружаемся.
Отключаем предыдущий firewall

Обязательно отключаем предыдущий firewall!
Обратите внимание, для отключения ipfw мало написать в /etc/rc.conf:
firewall_enable="NO"
firewall_script="/etc/ipfw.rules"
firewall_logging="NO"
natd_enable="NO"
natd_interface="vr0"
natd_flags="-f /etc/natd.conf"
Надо по меньшей мере написать в /etc/sysctl.conf:

net.inet.ip.fw.enable=0
net.inet6.ip6.fw.enable=0

или пересобрать ядро без поддержки ipfw.
Иначе после перезагрузки вы получите работающий ipfw который по умолчанию все заблокировал. Что грозит всевозможной морокой обладателям компьютеров без мониторов управляемых по ssh.
Пишем правила для firewall pf

Правила pf пишутся в следующем порядке:

Macros: Пользовательские переменные для имен интерфейсов, переменных и т.д.

Tables: Структуры хранящие списки IP адресов (иногда большие списки).

Options: Различные опции контролирующие работу pf.

Scrub: Предобработка пакетов для их нормализации и дефрагментации.

Queueing: Приоритезация и контроль полосы пропускания.

Translation: Перенаправление пакетов и NAT.

Filter Rules: И фильтрация пакетов.

Нужно придерживаться этого порядка при написании файла с правилами pf, хотя конечно отдельные секции можно опустить, если в них отсутствует надобность.
У меня отсутствуют планы описывать синтаксис pf firewall в этой статье, так как есть гораздо более полные и интересные руководства на эту тему.
Начать можно с OpenBSD 4.1 pf FAQ

Правила для pf firewall можно записать в любой желаемый файл, например в /etc/pf.conf
Что стоит помнить при написании pf firewall-а?

Что pf - statefull firewall (правила по умолчанию statefull).

Что выигрывает последнее подошедшее правило, т.е., к примеру, в firewall с политикой «все запрещено, кроме того что разрешено», в начале правил фильтрации стоит block all, а потом идут разрешающие правила.

Ниже пример firewall-а который я написал для себя.

# macros

# Interfaces

# Internal Interface
int_if="vr2"
# External interface
out_if="vr0"

# IP addresses

# Internal IP address
int_ip="192.168.1.254"
# External IP address
out_ip="111.111.111.111"
# Internal LAN
int_lan="192.168.1.0/24"

# Services

# TCP Services
# Allow ftp (20,21,64000:64999), ssh (21), web services (80,9090), dc hub (411), torrent (4444,6881)
tcp_services="{ 20, 21, 22, 80, 411, 4444, 6881, 9090, 64000:64999 }"
# UDP Services
# Allow Torrent DHT (6881)
udp_services="{ 6881 }"
# ICMP Services
icmp_types="{ echoreq, unreach}"

# Port passthrough
# Allow torrent passthrough on home workstation.
tcp_passthrough_services="{ 4444, 6881 }"
udp_passthrough_services="{ 6881 }"
workstation="192.168.78.1"

# options

set block-policy return
set loginterface $out_if
set skip on lo

# Scrubs
scrub in all

# NAT match rules
nat on $out_if inet from $int_lan to any -> ($out_if:0)

# Redirects
rdr on $out_if inet proto tcp to ($out_if) port $tcp_passthrough_services -> $workstation
rdr on $out_if inet proto udp to ($out_if) port $udp_passthrough_services -> $workstation

# Filter rules

# Block packet by default
block in log
# Allow multicast, keystone is allow-opts.
pass in quick proto igmp to { 224.0.0.0/8, 239.0.0.0/8 } allow-opts
pass out quick proto igmp all allow-opts
# Allow out packets.
pass out quick

# Antispoofing
antispoof quick for { lo $int_if }

# IPTV igmp pass
pass in quick on $out_if proto udp to 239.0.0.0/8 port 1234

# Services pass
pass in on $out_if inet proto tcp from any to ($out_if) port $tcp_services
pass in on $out_if inet proto udp from any to ($out_if) port $udp_services

# Redirected ports pass
pass in on $out_if inet proto tcp from any to $workstation port $tcp_passthrough_services synproxy state
pass in on $out_if inet proto udp from any to $workstation port $udp_passthrough_services synproxy state

# ICMP pass
pass in inet proto icmp all icmp-type $icmp_types

# Internal LAN pass.
pass quick on $int_if

Запуск pf firewall

Пробуем наш firewall:
pfctl -F all -f /etc/pf.conf
pfctl -e

Если все хорошо, то прописываем firewall в системе на постоянной основе, добавив в /etc/rc.conf:
pf_enable="YES"
pf_rules="/etc/pf.conf"
pf_flags=""
pflog_enable="YES"
pflog_logfile="/var/log/pf.log"
pflog_flags=""

Теперь pf firewall с нами после каждой перезагрузки.
Управление pf firewall

Включить pf:
pfctl -e

Отключить pf:
pfctl -d

Обнулить все правила и загрузить правила из файла /etc/pf.conf:
pfctl -F all -f /etc/pf.conf

Смотрим отчет по правилам фильтрации, NAT-а и keep state таблицам:
pfctl -s [ rules | nat | state ]

Проверить синтаксис /etc/pf.conf на ошибки, без загрузки правил:
pfctl -vnf /etc/pf.conf
Ведение логов и отладка pf firewall-а

pf хранит логи в бинарном формате, который доступен для чтения через
tcpdump -r

К примеру для просмотра логов в режиме реального времени, можно использовать команду:
tcpdump -n -e -ttt -i pflog0

Что бы избежать захламления сотнями мегабайтов логов своего диска, добавим в файл /etc/newsyslog.conf строку:
/var/log/pf.log 600 3 10240 * JB /var/run/pflogd.pid

Что ограничит размер лога 10-ю мегабайтами, будет хранить 3 лога, архивные копии лога будут сжаты bzip2. Естественно можете поставить сюда желаемые вами значения.
Заметки по теме статьи

Обратите внимание что включение и выключение firewall командами pfctl -e и pfctl -d и загрузка правил командой pfctl -f <файл с правилами> это взаимодополняющие команды, т.е. надо например включить pf и загрузить для него правила.

http://jakshi.org.ua/dokuwiki/настройка_простого_firewall_и_nat_на_pf

Обновлено: 12.03.2015