Настройка PF — nat + firewall

Статья из серии статей о настройке сервера для небольшого офиса на OpenBSD.

Захотелось вот настроить на старенькой машинке маршрутизатор для небольшой дружественной компании. Парк машин (читай, количество клиентов) около 20-30 штук. Такчто особой вычислительной мощи от компа и не требуется. Конечно же вопрос о выборе системы тоже обдумывался. Самым весомым аргументом в выборе было личное любопытство к пакетному фильтру PF. Вот чтото захотелось мне его пощупать. Много всякого слышал о pf, кто хвалит, кто ругает … почитал немного в инете разные тематические форумы, посмотрел как люди делают … и тд.- вот и загорелся желанием и сам опробовать в действии сей продукт. Да и начальник о pf лестно отзывался — мол просто настраивается и работает стабильно. Ну ладно, раз так — значит буду пробовать! Но тут еще засада с выбором OS, pf то есть в нескольких операционках! Точнее во всех семейства BSD 🙂 Правда, после того как я узнал что PF был разработан как сетевой фильтр именно для OpenBSD, выбор был сделан именно в пользу опёнка.
Как, что и куда крутить — инфы в инете скопилось навалом. Но всеже с самого начала очень рекомендую почитать на официальном сайте OpenBSD очень хорошее FAQ по PF Львиная часть, кстати, на русском 🙂 Почитали? Тогда начнемс!

Итак, что имеем вначале:

  • весьма скромная железка в духе Celeron 1GHz, RAM 256M, HDD 40G
  • установил на нее OpenBSD 4.6 — тоесть с OS тоже понятно 🙂
  • набор политик безопасности
  • соображения по обеспечению оптимальной работы с каналом internet
  • машину внутри сети с windows для которой нужны будут некоторые «плюшки»
  • желание разобраться с новым для меня инструментом — PF

Для простоты изложения назову основных участников конфигурации:

gw — наш сервер с OpenBSD
win2k3 — виндосервер внутри сети на который админам надо дать доступ из инета
clients — остальные клиенты внутри сети
admins — ip или DNS адреса машин админов (дом/работа) которым нужен доступ на gw
Что хотим получить:

— Политики:

  • «натим» только нужные нам протоколы — tcp,udp,icmp
  • по smtp пускаем общаться с сервером провайдера только gw и win2k3
  • по smtp clients могут общаться только с gw
  • для admins необходимо пробросить RDP и VNC порты на win2k3
  • максимально закрыться от доступа со стороны internet
  • подбирающих пароли к ssh  — банить (желательно средствами pf)
  • предусмотреть защиту от DDoS

— Некоторые соображения по оптимизации работы всех клиентов с каналом доступа в internet:

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

Главный конфигурационный файл pf в OpenBSD находится обычно тут: /etc/pf.conf

Вот что я туда накалякал:

root@gw:~# cat /etc/pf.conf
#
# Remember to set net.inet.ip.forwarding=1 and/or net.inet6.ip6.forwarding=1
# in /etc/sysctl.conf if packets are to be forwarded between interfaces.
INET_IF = "tun0"
LAN_IF = "rl0"
NAT_PROT = "{tcp, udp, icmp}"
WIN_2K3 = "192.168.0.10"
CYFRA_SMTP = "smtp.uatele.com"
ICMP_TYPE = "{echoreq, unreach}"
####   <== We need some Tables ==> ####
table <admins> const {99.55.66.22, 66.44.88.22, 93.51.57.66}
table <rfc1918> const {127.0.0.0/8, 192.0.2.0/24, 172.16.0.0/12, 169.254.0.0/16, 0.0.0.0/8, 240.0.0.0/4}
table <bruteforce> persist

set skip on lo0         # Skip All Rules For lo Interface
set block-policy return # Policy for icmp
set timeout { frag 10, tcp.established 3600 } # timeout policy for TCP sessions
set loginterface $INET_IF
set loginterface $LAN_IF

altq on $INET_IF cbq bandwidth 4Mb queue \
{ main, smtp, admin, ssh, ack, icmp }
  queue main  bandwidth 63% priority 2 cbq(default borrow red)
  queue smtp  bandwidth 10% priority 3 cbq(borrow red)
  queue admin bandwidth 8%  priority 4 cbq(borrow red)
  queue ssh   bandwidth 8%  priority 5 cbq(borrow red)
  queue ack   bandwidth 6%  priority 6 cbq(borrow red)
  queue icmp  bandwidth 4%  priority 0 cbq
#altq on $INET_IF bandwidth 960Kb hfsc queue { ack, dns, admin, ssh, main, mail, icmp }
#  queue ack        bandwidth 30% priority 8 qlimit 500 hfsc (realtime 20%)
#  queue dns        bandwidth  5% priority 7 qlimit 500 hfsc (realtime  5%)
#  queue admin      bandwidth 10% priority 6 qlimit 500 hfsc (realtime  7%)
#  queue ssh        bandwidth 10% priority 6 qlimit 500 hfsc (realtime 10%) {ssh_login, ssh_bulk}
#   queue ssh_login bandwidth 50% priority 6 qlimit 500 hfsc
#   queue ssh_bulk  bandwidth 50% priority 5 qlimit 500 hfsc
#  queue main       bandwidth 30% priority 5 qlimit 500 hfsc (realtime 20% default)
#  queue mail       bandwidth 10% priority 3 qlimit 500 hfsc (realtime  5%)

#  queue icmp       bandwidth  5% priority 2 qlimit 500 hfsc (realtime  5%)
##### <== Main Rule For NAT ==> #####
nat on $INET_IF proto $NAT_PROT from $LAN_IF:network to any -> ($INET_IF)

##### <== Redirect VNC and RDP to WIN2K3 ==> #####
rdr pass on $INET_IF proto tcp from  to ($INET_IF) port {5900, 3389} -> $WIN_2K3
#####<== By Default Block All ==> #####
block log on { $INET_IF, $LAN_IF } all
#block in quick log from urpf-failed                             # PF Spoofing proteCt!!!
#antispoof log quick for { lo0, $INET_IF, $LAN_IF } inet # PF Spoofing protect IPv4
block log quick from <bruteforce>                              # Block ssh bruteforcers

##### <== Block Not Routable  Networks ==> #####
block drop in quick log on $INET_IF from <rfc1918> to any
block drop out quick log on { $INET_IF , $LAN_IF } from any to <rfc1918>

##### <== For Redirected Ports ==> #####
pass out quick on $LAN_IF proto {tcp,udp} from <admins> to $WIN_2K3 port {5900, 3389}
##### <== Rules for 25 PORT ==> #####
pass in quick log on $LAN_IF inet proto tcp from $WIN_2K3 to $CYFRA_SMTP port 25 queue (smtp, ack)
pass in quick log on $LAN_IF inet proto tcp from $LAN_IF:network to $INET_IF port 25 queue (smtp, ack)
pass in quick log on $INET_IF inet proto tcp from { $CYFRA_SMTP <admins> } to $INET_IF port 25 queue (smtp, ack)
block in quick log on $LAN_IF inet proto tcp from any to ! $LAN_IF port 25
block out quick log on $INET_IF proto tcp from $INET_IF to ! $CYFRA_SMTP port 25
##### <=== For LanNetwork ===> #####
pass in on $LAN_IF from $LAN_IF:network to any
##### <=== For PFStat ===> #####
pass in on $INET_IF inet proto tcp from <admins> to port 80 queue (admin, ack)
##### <=== VPN RULES ===> #####
pass in on $INET_IF inet proto tcp from <admins> to port pptp queue (admin, ack)
pass in on $INET_IF proto gre from <admins> to any queue (admin, ack)
pass out on $LAN_IF inet from 192.168.0.200/30 to $LAN_IF:network queue (admin, ack)
pass out on $LAN_IF inet from tun to $LAN_IF:network
##### <== SSH RULES ==> #####
pass in quick on $INET_IF inet proto tcp from <admins>  to $INET_IF port 22 queue (ssh, ack) modulate state
pass in on $INET_IF inet proto tcp from any to $INET_IF port 22 queue (ssh, ack) synproxy state \
(max-src-conn 10, max-src-conn-rate 5/30, overload <bruteforce> flush global)

##### <== Pass OUT ==> #####
pass out on $INET_IF inet to any queue (main, ack) modulate state
##### <== Pass ICMP ==> #####
pass log inet proto icmp all icmp-type $ICMP_TYPE queue icmp
root@gw:~#

Понятие о том что здесь написано приходит после прочтения FAQ по PF . А вообще, благодаря очень простому синтаксису и принципу работы, правила pf читаются очень легко 🙂

Более подробно остановлюсь, разве что, на ssh правилах. Последнее время розвелось много всяких паскудных ботов, которые подбирают пароли для входа по всему чем можно войти 😀 Приходится както защищаться от километровых failed — ов в логах 🙂 Вот один из вариантов для ssh:

#####<== SSH RULES ==> #####
pass in quick on $INET_IF inet proto tcp from <admins>  to $INET_IF port 22 queue (ssh, ack) modulate state
pass in on $INET_IF inet proto tcp from any to $INET_IF port 22 queue (ssh, ack) synproxy state \
(max-src-conn 10, max-src-conn-rate 5/30, overload  flush global)

Первое правило — пропускаем тех кто в таблице admins без всяких там приколов.

Второе — пропускаем из инета на порт 22 всех но с ограничениями:

  • max-src-conn 10 — не более 10 state с одного ip
  • max-src-conn-rate 5/30 — не более 5 соединений в течении 30 секунд

Все кто превысил данные лимиты, помещаются в таблицу bruteforce и блокируются правилом

block log quick from <bruteforce>             # Block ssh bruteforcers

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

admin@gw:~$ sudo crontab -l |grep -i brute
#### <- For clean entries from table SSH BRUTEFORCES older than 1 hour ! -> ####
59      *       *       *       *       /sbin/pfctl -t bruteforce -T expire 86400
admin@gw:~$

В процессе отладки наш лучший друг, товарищ и брат — tcpdump, который может слушать как реальные интерфейсы, так и интерфейс pflog0 на котором видно те пакеты, которые логируются (слово log в правиле какраз для этого). Использование снифера с такими опциями:

root@gw:~# tcpdump -n -e -ttt -i pflog0
tcpdump: listening on pflog0, link-type PFLOG
Mar 16 22:12:22.963961 rule 1/(match) block in on rl0: 0.0.0.0.68 > 255.255.255.255.67: xid:0x52525230 secs:39663 flags:0x8000 [|bootp] [ttl 1]
^C
1 packets received by filter
0 packets dropped by kernel
root@gw:~#

очень информативно рассказывает о судьбе пакетов. В данном примере мы видим что пакет заблочен (block) первым правилом (rule 1) на внутресетевом интерфейсе (rl0) ну и source/destination пакета — 0.0.0.0.68 > 255.255.255.255.67 (в конце после четвертого октета, после точки, показан номер порта источника/назначения соответственно)

Ну вот какбы и все 🙂 Пакетики натятся, клиенты довольные сидят в инете 🙂 А админы пьют пиво и закусывают рыбкой …. все удачи!

    • PPublic
    • Декабрь 12th, 2010 5:42пп

    Наконец-то нормальная человеческая статья, в которой просто описан процесс нарезки трафика по приоритетам. Спасибо автору.

    • test
    • Декабрь 24th, 2010 5:34пп

    pass out on $LAN_IF inet from 192.168.0.200/30 to $LAN_IF:network queue (admin, ack) — БРЕД!

    • admin
    • Декабрь 24th, 2010 5:42пп

    А точнее …
    Сам понимаю что бредово выглядит …. но факт остается фактом — без этого правила не работает VPN 🙁 А розбираться нету времени 🙂

  1. Трэкбэков пока нет.

Why ask?