суббота, 25 апреля 2009 г.

Оптимизация и харденинг правил IPFilter

Так получилось, что мне пришлось очень часто использовать IPFilter. За почти четыре с половиной года активного применения IPF выработались несколько конфигураций, которые достаточно часто оптимизировались - как по скорости, так и по степени защищенности. За прошедшее время часть правил выбыли за ненадобностью, часть изменились.

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

Возьмем с нее работающую конфигурацию и разберем по основным блокам с некоторыми пояснениями.

Прежде всего - основной принцип, которого стараюсь придерживаться лично я:

Блокируем все, разрешаем только то, что необходимо.

Сначала всегда ставим блок, отсекающий мусор, фрагменты, основные атаки:

# ---------------------
# Common blocking rules
# ---------------------

# Block all IP fragments
block in quick all with frag

# Block all short IP fragments
block in quick proto tcp all with short

# Block any IP packets with options set in them
block in quick all with ipopts

# Block nmap OS fingerprint attempts
block in log first quick proto tcp all flags FUP

Данные правила блокируют обломки пакетов, возникающие ниоткуда пакеты (вне сессий) с установленными опциями, и сканы NMAP, определяющие OS.

Замечание: Для полного сбивания NMAP с толку последнего правила недостаточно, определению ОС весьма способствуют двунаправленные порты, для избежания которых, как будет видно далее, нужно просто вдумчиво написать конфигурацию IPF.

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

# Make sure the loopback allows packets to traverse it
# ---------- lo0 interface ---------
pass in quick on lo0 all
pass out quick on lo0 all
# ---------- lo0 interface ---------

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

# DEBUG ICMP permit rules
# --------- bge0 interface ----------
#pass in quick on bge0 proto icmp from any to host_name icmp-type echo keep state
#pass out quick on bge0 proto icmp from host_name to any icmp-type echorep keep state
# --------- bge0 interface ----------
# --------- bge1 interface ----------
#pass in quick on bge1 proto icmp from any to host_name2 icmp-type echo keep state
#pass out quick on bge1 proto icmp from host_name2 to any icmp-type echorep keep state
# --------- bge1 interface ----------
# --------- nge0 interface ----------
#pass in quick on nge0 proto icmp from any to host_name3 icmp-type echo keep state
#pass out quick on nge0 proto icmp from host_name3 to any icmp-type echorep keep state
# --------- nge0 interface ----------

# Group 300 - Opened incoming ports on bge1
# Group 300 setup
pass in on bge1 head 300
# --------- bge1 interface ----------
# 80 port - HTTP
pass in quick on bge1 proto tcp from any to host_name2 port=80 flags S keep state group 300
# 443 port - HTTPS
pass in quick on bge1 proto tcp from any to host_name2 port=443 flags S keep state group 300
# --------- bge1 interface ----------

# Group 400 - Opened incoming ports on nge0
# Group 400 setup
pass in on nge0 head 400
# -------- nge0 interface -----------
# 80 port - HTTP
pass in quick on nge0 proto tcp from any to host_name3 port=80 flags S keep state group 400
# 443 port - HTTPS
pass in quick on nge0 proto tcp from any to host_name3 port=443 flags S keep state group 400
# -------- nge0 interface -----------

Нам ведь совершенно не нужно, чтобы нас пинговали все кому не лень, как, например, пытались запинговать до смерти сайты Грузии ;). Сайт Sun совершенно не пингуется, однако это совсем не мешает ему работать.

Кстати, порты ICMP, открытые постоянно - это как раз то самое, что позволяет NMAP определить ОС (одним из способов), следовательно, оставим их закрытыми.

Если машина будет работать в качестве роутера, полезно включить вот такое правило:

# Stealth for traceroute (hide as hop)
# ---------- bge0 interface ----------
#block in quick on bge0 fastroute proto udp from any to any port 33434 >< 33465
# ---------- bge0 interface ----------

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

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

# Group 100 - Blocked networks on any interface
# Group 100 setup
block in all head 100
# Anti-spoofing rules
block in quick from 192.168.0.0/16 to any group 100
block in quick from 172.16.0.0/12 to any group 100
block in quick from 10.0.0.0/8 to any group 100
block in quick from 127.0.0.0/8 to any group 100
block in quick from 0.0.0.0/8 to any group 100
block in quick from 169.254.0.0/16 to any group 100
block in quick from 192.0.2.0/24 to any group 100
block in quick from 204.152.64.0/23 to any group 100
block in quick from 224.0.0.0/3 to any group 100
# Mail.ru bot, EDN Sovintel (Russia)
block in quick from 194.186.55.0/24 to any group 100

..... и далее все, кого мы посылаем. :)

Обратите внимание - группа не привязана ни к какому интерфейсу, это во-первых - мы хотим гнать подобные пакеты прочь на всех активных интерфейсах, а во-вторых, так как машина бастионная, то мы уверены в себе, что от нас не будут уходить спуфинговые пакеты, следовательно, блокировать мы их не стали (что подразумевает и другие меры защиты на хосте, кроме IPF - например, минимизацию, BART итп.).

Теперь пришел черед разрешающих правил для доступа к сервисам:

# Group 200 - Opened incoming ports on bge0
# Group 200 setup
pass in on bge0 head 200
# --------- bge0 interface ---------
# 21 port - FTP service. Disable port and service on Internet servers!
#pass in quick on bge0 proto tcp from any to host_name port=21 flags S keep state group 200
# 22 port - SSH
#pass in quick on bge0 proto tcp from any to host_name port=22 flags S keep state group 200
# 80 port - HTTP
pass in quick on bge0 proto tcp from any to host_name port=80 flags S keep state group 200
# 177 port - x11 service. Disable port and service on Internet servers!
#pass in quick on bge0 proto udp from any to host_name port=177 keep state group 200
# 443 port - HTTPS
pass in quick on bge0 proto tcp from any to host_name port=443 flags S keep state group 200
# 2222 port - SSH Alternative
pass in quick on bge0 proto tcp from any to host_name port=2222 flags S keep state group 200
# 1158 port - OEM console.
# Note: Secure console on Internet servers!
#pass in quick on bge0 proto tcp from any to host_name port=1158 flags S keep state group 200
# Note: OEM console may be on port 5500
pass in quick on bge0 proto tcp from any to host_name port=5500 flags S keep state group 200
# 1521 port - Oracle. Disable port on Internet servers!
#pass in quick on bge0 proto tcp from any to host_name port=1521 flags S keep state group 200
# 7777 port - OHS. Disable port when using WebCache.
#pass in quick on bge0 proto tcp from any to host_name port=7777 flags S keep state group 200
# 8888 port - OC4J main http port.
# Disable port after mount with mod_oc4j using AJP13.
#pass in quick on bge0 proto tcp from any to host_name port=8888 flags S keep state group 200
# 9400 port - WebCache admin default port.
pass in quick on bge0 proto tcp from any to host_name port=9400 flags S keep state group 200
# --------- bge0 interface ---------

# Group 300 - Opened incoming ports on bge1
# Group 300 setup
pass in on bge1 head 300
# --------- bge1 interface ---------
# 80 port - HTTP
pass in quick on bge1 proto tcp from any to host_name2 port=80 flags S keep state group 300
# 443 port - HTTPS
pass in quick on bge1 proto tcp from any to host_name2 port=443 flags S keep state group 300
# --------- bge1 interface ---------

# Group 400 - Opened incoming ports on nge0
# Group 400 setup
pass in on nge0 head 400
# --------- nge0 interface ---------
# 80 port - HTTP
pass in quick on nge0 proto tcp from any to host_name3 port=80 flags S keep state group 400
# 443 port - HTTPS
pass in quick on nge0 proto tcp from any to host_name3 port=443 flags S keep state group 400
# --------- nge0 interface ---------

Важно - мы обязательно группируем эти правила и привязываем к интерфейсам. Важно - мы указываем для протокола TCP пакеты с флагами S (SYN) - инициирование сессии и задаем модификатор keep state (запомнить в таблице состояний).

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

# 53 port - DNS.
# --------- bge0 interface ---------
# Allow incoming DNS requests to server (DNS-server)
#pass in quick on bge0 proto tcp/udp from any to host_name port=domain keep state group 200
# --------- bge0 interface ---------
# --------- nge0 interface ---------
pass in quick on nge0 proto tcp/udp from any to host_name3 port=domain keep state group 400
# --------- nge0 interface ---------

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

То же самое относится к NTP:

# 123 port - NTP.
# --------- bge0 interface ---------
# Allow incoming NTP requests to server (NTP-server)
#pass in quick on bge0 proto udp from any to host_name port=ntp keep state group 200
# --------- bge0 interface ---------

Говоря простым языком - разрешающие правила при необходимости выпустят NTP-клиента наружу. Нет необходимости в особых разрешающих правилах для этого. Впускать NTP-пакеты мы будем только в случае, если машина является NTP-сервером, и то можем ограничить доступность разрешающим входящим правилом, помимо настроек NTP (например, чтобы гарантированно избежать ненужной ретрансляции пакетов NTP-сервера).

Собственно, разрешающая группа:

# Group 700 - Outgoing and callbacks sessions
# Group 700 setup
pass out all head 700
# Allow connections originating from local machine out
pass out quick proto tcp all flags S/SA keep state keep frags group 700
# Enable outgoing UDP from server
# Note: We dont open it for security reasons, however
# it must be open when use DNS-servers for zones
# transfers, outgoing traceroute etc.
pass out quick proto udp all keep state group 700
pass out quick proto icmp all keep state keep frags group 700

Здесь мы разрешаем себе все, в чем выше отказали другим ;) - исходящие фрагменты, пакеты SYN-ASK... ;)

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

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

Финальный штрих - все пакеты, которые не удовлетворили одному из вышеперечисленных правил, режутся полностью:

# Finally block all unmatched
block in quick all

Несколько пояснений.

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

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

С другой стороны, я сознательно не утруждаю себя ни созданием honeypots, ни сокрытием того факта, что машина защищена файрволом. Да, приемы сокрытия фактов защиты существуют, например, в посылке пакета RST вызывающему, либо destination/port unreacheable. Но зачем я буду утруждаться посылкой пакета, когда можно и так выяснить, что машина под защитой, а тратить ресурсы (особенно сетевые) на ответы в большинстве случаев совершенно ни к чему.

Также можно заметить, что я чхал на логгирование пакетов. Я использую журналирование только первого пакета и только один раз:

# Block nmap OS fingerprint attempts
block in log first quick proto tcp all flags FUP

Бывает, что мне интересно, кто сканировал мои порты. Но вести гигабайтные логи, просто "шоб було", не заглядывая в них годами, мне как-то претит.

Собственно, вот и все.

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

"А я просто закроюсь яйцами и пускай ломают ноги!" :)

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