pf и FreeBSD


Обстоятельства диктуют переходить с ipfw на pf. Ну чтобы не попасть в просак решил я для начала поставить его на домашнем сервере.

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

В нашем случае требовалось обеспечить следующее.
Сетевых интерфейсов 2 (внешний и внутренний), включен nat.
С сервера наружу в интернет можно все.
Ping можно везде.
Из интернета на сервер можно пассивный ftp, ssh.
Из LAN и обратно можно все через туннель.
Из LAN в интернет можно dns, ssh, pop3, smtp, 1723, gre (остальное через прокси).
Необходимо пробросить порт на определенную машину в LAN чтобы работал torrent.
В ядро добавляем опции и компилим новое ядро. Как это сделать смотрите здесь

options ALTQ
options ALTQ_CBQ
options ALTQ_RED
options ALTQ_RIO
options ALTQ_HFSC
options ALTQ_PRIQ
options ALTQ_NOPCC
device pf
device pflog
device pfsync

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

Далее добавим в /etc/rc.conf следующие строчки

gateway_enable="YES"
pf_enable="YES"
pf_rules="/etc/pf.conf"
pf_flags=""
pflog_enable="YES"
pflog_logfile="/var/log/pflog"
pflog_flag=""

А теперь собственно сам файл /etc/pf.conf с пояснениями.


#int_if - внутренний интерфейс, ext_if - внешний интерфейс
int_if = "rl0"
ext_if = "fxp0"
#comp_rdr_lan на этот комп пробросим порты
comp_rdr_lan = "192.168.2.2"
#порты пробрасываемые внутрь локалки на комп comp_rdr_lan (торент)
comp_rdr_port = "6881, 55555"
#локальная сетка через туннель
lan_work = "192.168.1.0/24"
#Разрешаем из локалки эти порты
tcp_udp_out = "53, 6112"
tcp_out = "22, 25, 110, 1723"
#tcp_services - сервисы на нашем серваке 21 - фтп, 35500:35600 - порты фтп
# для пассивного режима, использую proftpd эти порты указаны в proftpd.conf
# для пассивного режима, порт 113 идент - вроде как используется некоторыми
# сервисами, 22 - ssh
tcp_services = "{ 21, 35500:35600, 113, 22 }"
#icmp_types - какие типы icmp принимать
icmp_types = "echoreq"
# списки приватных сетей, для блокировки их на внешнем интерфейсе
priv_nets = "{ 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 }"
#Поведение по умолчанию для правил фильтра, когда срабатывает правило block
# в нашем случае для отброшенных пакетов TCP отсылается TCP RST, для прочих
#ICMP Unreacheble
set block-policy return
#Нормализация входящего трафика
scrub in all
#Натим пакеты из локалки
nat on $ext_if from $int_if:network to any -> ($ext_if)
#Проброска портов в локалку на comp_rdr_lan
rdr pass on $ext_if inet proto { tcp, udp } from any to ($ext_if)
port { $comp_rdr_port } -> $comp_rdr_lan
# по умолчанию запрещаем все
block all
#разрешаем все через локальный интерфейс
pass quick on lo0 all
#Блокируем приватные сети на внешнем интерфейсе
block drop in quick on $ext_if from $priv_nets to any
block drop out quick on $ext_if from any to $priv_nets
#Разрешаем доступ к нашему серваку снаружи к определенным портам
pass in on $ext_if inet proto tcp from any to $ext_if port $tcp_services
flags S/SA keep state
#Разрешаем входящие icmp ping
pass in inet proto icmp all icmp-type $icmp_types keep state
#Разрешаем все через туннель
pass on tun0 all
pass on tun1 all
#Разрешить из локалки доступ к внутреннему интерфейсу и к локалке работы
pass in quick on $int_if from $int_if:network to { $int_if, $lan_work }
keep state
#Разрешить из локалки tcp и udp на tcp_udp_out
pass in quick on $int_if inet proto { tcp, udp } from $int_if:network to any
port { $tcp_udp_out } keep state
#Разрешить из локалки tcp на tcp_out
pass in quick on $int_if inet proto tcp from $int_if:network to any port
{ $tcp_out } keep state
#Разрешить из локалки gre
pass in quick on $int_if inet proto gre from $int_if:network to any keep state
#Разрешить в локалку все через внутренний интерфейс
pass out on $int_if from any to $int_if:network keep state
#Разрешить наружу tcp через внешний интерфейс
pass out on $ext_if proto tcp all modulate state flags S/SA
#Разрешить наружу udp, icmp, gre через внешний интерфейс
pass out on $ext_if proto { udp, icmp, gre } all keep state


Ну и как памятка некоторые команды.

pfctl -sn Показать текущие правила NAT.
pfctl -sr Показать текущие правила фильтра.
pfctl -ss Показать текущую таблицу состояний.
pfctl -sa Показать все что можно.

P.S. Конфигурационный файл взят с абсолютно рабочей машины.


http://argo-uln.blogspot.com/2008/01/pf-freebsd-62.html

Обновлено: 12.03.2015