Policy-based-фильтрация с помощью iptables
Этот документ и связанные с ним идеи ранее обсуждались в некоторых постах/тредах [1], [2], [3], [4], [5], [6]. По умолчанию здесь предполагается, что используемя система — последний релиз стабильного Debian'а.
Введение
Попытка написать сколь-нибудь сложные правила iptables сразу сталкивается со следующими трудностями:
- iptables — не «policy-based»-фильтр. Свою «policy» нужно реализовывать самому. Например, для того, чтобы один клиент на lo общался с каким-то демоном на lo, нужно написать четыре правила: два на INPUT и два на OUTPUT. Если коммуникация идёт с сервером в сети, то тоже нужны минимум два правила: одно для OUTPUT и одно для INPUT. Действительно, нам очень редко нужен случай «мы хотим посылать пакеты на заданный хост, но не хотим никогда получать от него что-либо обратно». Одним словом, почти 100% всего, что когда-либо кому-либо понадобится в плане реальной фильтрации, будет сопряжено с бессмысленным механическим дубляжом правил (с минимальными исправлениями), разрешающих обратные ответы.
- Другая проблема — читаемость правил. Опций много, они длинные, порядок следования свободный, перед многими опциями нужно много раз указывать бессмысленные -m tcp. Зачем? Хочется всю эту шелуху упрятать куда-то внутрь, чтобы осталось только то, что несёт информационную нагрузку.
Чтобы не писать кишки, я начал с того, что написал алиасы. Было решено не изобретать велосипед, а взять слова из того словаря, который уже используется в pf, и к которому многие, владеющие pf, привыкли. PF является именно «policy based»-фильтром, поэтому возникло желание писать так, как это пишется в PF, а скрипт чтобы всё превращал в iptables-правила. Эта задача была бы неподъёмно сложной, поэтому я был вынужден обойтись полумерой, при которой при внешне похожих правилах их семантическое значение нередко сильно отличается. У такого подхода есть и плюсы: в отличие от программы с нетривиальной логикой, которую нелегко проанализировать постороннему на предмет ошибок и утечек, в этом подходе очень легко понять, какие алиасы и функции каким iptables-правилам соответствуют. С учётом того, что при прочих раных простота способствует безопасности, это имеет свои плюсы.
Алиасы и функции
Чтобы сделать настоящие pass in/out on, одних алиасов было бы недостаточно, поэтому в скрипте $pass_out играет просто роль разрешающего правила iptables без каких-либо keep state.1 По смыслу к настоящему pass out в скрипте ближе $allow_out (аналогично для $allow_in).
Реализовать настоящие списки также было бы проблематичным, поэтому вместо них есть multi-функции, принимающие аргументами списки вида IP:порт. Каждый список имеет вид:
X.X.X.X:PORT1 X.X.X.X:PORT2 X.X.X.X:PORT3
Помимо multi-функций есть частичная поддержка ipset-списков. В частности, скрипт ipset_create_DB.sh используется для создания ipset-списков Tor'овских нод и их портов. Этот скрипт должен быть выполнен до запуска нижеприведённого iptables-скрипта, причём предполагается, что системный Tor уже работал в системе и успел создать файлы Tor-статистики в /var. Tor'у в iptables-скрипте можно указывать конкретные типы Tor-нод, с которыми ему разрешено соединяться.
Часто приходится для одного и того же пользователя писать как allow_in, так и allow_out-правило при фильтрации на lo, поэтому была создана ещё одна функция, lh_filter, которая объединяет их функционал.
Для Tor'а требуются многократные идентичные allow_in, отличающиеся только номерами портов и содержащие хак для user=root,2 поэтому для удобства все такие листинги были объединены в одну функцию allow_in_tor с простым синтаксисом. Аналогично функции allow_in_tor Tor'овская функция allow_out тоже была переделана в более функциональную allow_out_tor.
Правило allow_dns разрешает DNS-трафик для конкретного пользователя. Есть ещё несколько экспериментальных функций, которые нет смысла здесь описывать, т.к. они использовались только для тестов. Синтаксис большинства правил предельно прост. Например:
allow_out <интерфейс> <протокол> <src IP> <dst IP> <порт> <пользователь> allow_out_tor <интерфейс> <src IP> <dst IP> <список портов> <пользователь> allow_in <интерфейс> <протокол> <src IP> <dst IP> <порт> <пользователь> allow_in_tor <интерфейс> <src IP> <dst IP> <список портов> <пользователь>
На данный момент правила никак не касаются перенаправлений, маскарадинга и прочих специфических случаев: во-первых, я с ними никогда не разбирался; во-вторых, не факт, что их можно легко представить как нечто pf-подобное. Даже при добавлении логирования пакетов особенности того, что iptables — не pf, сильно вылазят наружу, их уже легко не спрячешь.
iptables-скрипт
Предполагается, что скрипт-конвертор pf2iptables.sh, а также ipset-скрипт ipset_create_DB.sh с создаваемым им файлом ipset_DB.txt находятся в той же директории, что и нижеприведённый iptables-скрипт. Ниже приведён конкретный его пример, который следует отредактировать под свой случай.
#!/bin/sh #------------------------------- # Этот скрипт делает всё хорошо #------------------------------- ## Раскомментируйте, если сделали ошибку в скрипте, и хочется его отладить: #set -x ################################################################################ # Настройки сети ################################################################################ ## IP, сетевой интерфейс и gateway вычисляются автоматически: #eIF=$(ifconfig -a |sed '2,$d;s/[[:space:]].*$//') # eth output interface #eIP=$(ifconfig $eIF |grep 'inet addr:' |sed 's/^.*addr:\([^ ]*\) \(.*\)/\1/') ## eIF = ethernet InterFace, eIP = ethernet IP. ## Аналогичным образом определяются параметры для wifi-интерфейсов (например, ## wIF и wIP). # Если используется только один ethernet-интерфейс, проще его задать вручную: oIF=eth0 # Текущий IP вычисляется автоматически: oIP=$(ifconfig $oIF |grep 'inet addr:' |sed 's/^.*addr:\([^ ]*\) \(.*\)/\1/') ## Аналогичным образом можно узнавать свой gateway (предполагается, что ## последнее число его IP-адреса -- единица): #GW=$(echo $oIP |sed 's/\(^.*\)\.\(.*$\)/\1.1/') # My gateway #arp -s $GW XX:XX:XX:XX:XX:XX # "Глобальные" переменные: lh=127.0.0.1 az=0.0.0.0 a255=255.255.255.255 ################################################################################ # Настройки DNS ################################################################################ ## См. функцию allow_dns в pf2iptables.sh, которую следует отредактировать под ## свой случай. Если нужно, здесь можно указать свои DNS-сервера, но если весь ## трафик идёт через Tor, в этом нет необходимости. #DNS1=X.X.X.X #DNS2=X.X.X.X #DNS_local_1=X.X.X.X #DNS_local_2=X.X.X.X #DNS_inet_1=X.X.X.X #DNS_inet_2=X.X.X.X ## Если этот скрипт используется для разных сетей (к примеру, домашей и рабочей), ## где используемые IP-адреса на исходящих интерфейсах разные, можно ## автоматически получать правильные настройки DNS, опираясь на сетевой адрес, ## заранее назначенный интерфейсу (отредактируйте функцию allow_dns под свой ## случай): #xIP=X.X.X.X #if [ $oIP = $xIP ]; then # # DNS IP на работе: # DNS1=X.X.X.X # DNS2=Y.Y.Y.Y #else # # DNS IP дома: # DNS1=Z.Z.Z.Z # DNS2=W.W.W.W #fi ################################################################################ # Инициализация (сброс всех настроек и выставление запрещающей policy) ################################################################################ # Переключить в 'on', если нужен список правил на выходе без их загрузки. dry_run=off . ./pf2iptables.sh clear_all block_all ## Здесь мы могли бы сразу заблокировать весь UDP-трафик (см. функцию block_all), ## если он не нужен, но лучше сначала его записать в логи, и только затем ## заблокировать (см. правила логирования и блокирования в конце этого скрипта): #$block_out $proto udp ################################################################################ # Загрузка ipset-списков Tor-нод ################################################################################ # Здесь мы получаем нужные IP и порты Tor-нод, чтобы ниже фильтровать по ним # трафик Tor'а. Не забывайте время от времени выполнять скрипт # ./ipset_create_DB.sh, который обновляет базу данных Tor-нод (по локальной # Tor-статистике) в памяти ipset'а и в файле ipset_DB.txt. oIP_prefix=$(echo $oIP |sed 's/^\(.*\)\.\(.*\)$/\1/') if grep $oIP_prefix ipset_DB.txt > /dev/null ; then # Это может быть признаком атаки. Такая нода не должна использоваться нами # при построении Tor-цепочек. echo Warning: IP from our subnet is in the ipset list fi # Удаляем все ipset-списки вместе с их содержимым из памяти и создаём новые # списки, используя базу данных из заранее подготовленного файла ipset_DB.txt: ipset x ipset restore -file ipset_DB.txt ################################################################################ # Правила для пользователя torbrowser_user ################################################################################ # Разрешаем соединение с системным Tor'ом для Tor Browser'а: allow_out lo tcp $lh $lh 9150 torbrowser_user # Чтобы не засорять лог-файлы, сразу заблокируем обращения Tor Browser'а # к управляющему порту Tor'а (ControlPort, man torrc): $block_out $on_o lo $proto tcp $from $lh $to $lh $dt_port 9151 \ $user torbrowser_user # Разрешаем соединение с системным Tor'ом (используя другие Tor-цепочки) для # тулзов типа wget, которые могут могут быть нужны (под этим же пользователем): allow_out lo tcp $lh $lh 9510 torbrowser_user ##------------------------------------------------------------------ ## Использование Tor Browser'а с локальным Tor'ом (небезопасно): ##------------------------------------------------------------------ ## Если Tor Browser используется с локальным Tor'ом, iptables мало ## чем может помочь. Однако, этот вариант может быть сделан немного ## более безопасным, если разрешить Tor'у соединяться только с ## определёнными заданными guard-нодами, которые в данный момент ему ## нужны: #multi_allow_out $oIF tcp $oIP guard_list_file torbrowser_user ## Т.е. вышеприведённое правило лучше, чем разрешать вообще весь ## TCP-трафик в Интернет: #allow_out $oIF tcp $oIP all 0:65535 torbrowser_user ## В случае локального Tor'а также требуется разрешить ## torbrowser_user'у входящие и исходящие соединения на ## loopback-интерфейсе: #lh_filter 9150:9151 torbrowser_user ## Множество пакетов ошибочно блокируются функцией lh_filter по ## неизвестной причине (проблема только с Tor'ом; другие сервисы -- ## например, ssh, работают нормально). Если понизить требования к ## безопасности, некоторые из этих ошибочно блокируемых пакетов ## могут быть разрешены следующими правилами, но только некоторые ## (часть пакетов продолжит блокироваться): #tor_lh_filter 9150:9151 torbrowser_user #bug_workaround ## Поскольку Tor и другие программы работают нормально со ## стандартным правилом lh_filter (несмотря на блокируемые пакеты), ## лучше не использовать вышеприведённые функции tor_lh_filter и ## bug_workaround. Однако, эти функции могут быть полезны (см. ## скрипт pf2iptables.sh) для анализа проблемы и нахождения её ## причины. ################################################################################ # Правила для основного пользователя "main_user" ################################################################################ # Разрешить использовать SOCKS-порт (40000) SSH-соединения, запускаемого # от имени main_user'а: lh_filter 40000 main_user ## Если используется системная privoxy, разрешить соединение с ней: #allow_out lo tcp $lh $lh 8118 main_user # Разрешить соединение с mpd-сервером, который запущен от имени пользователя # music_user (правило allow_out здесь было бы избыточным, потому что # для пользователя music_user используется lh_filter, см. ниже): $pass_out $on_o lo $proto tcp $from $lh $to $lh $dt_port 6600 $user main_user # Объяснение: коммуникация между клиентом и сервером на "lo" требует 4 # правила: 2 входящих и 2 исходящих. Все они уже добавлены через lh_filter # (клиент и сервер mpd запускаются, в том числе, под пользователем # music_user). # # Мнемоническое правило: lh_filter для пользователя music_user = allow_out для # music_user + allow_in для music_user. allow_in уже указано для music_user. # Следовательно, нам нужна только функция allow_out для пользователя # main_user. Кроме того, allow_out = $pass_out + $pass_in. Однако, поскольку # iptables не поддерживает опцию "owner" для входящих пакетов, нужное правило # $pass_in уже добавлено в список. Т.е., нам нужно добавить только $pass_out. # Иными словами говоря, если $pass_in указан для одного пользователя # посредством lh_filter, он же добавлено и для всех других пользователей -- # для них остаётся добавить только $pass_out. Если вместо $pass_out мы напишем # allow_out, одно и то же фильтрующее правило iptables будет указано в списке # iptables-save дважды. # Разрешаем соединение с системным Tor'ом через порты на "lo" (набор разных # портов нужен, чтобы использовать разные Tor-цепочки в разных программах): allow_out_tor lo $lh $lh 9070:9072 9099 main_user # Не показывать в логах UDP-пакеты, автоматически генерируемые mail-программой, # когда приходит новое письмо. Сразу блокируем такие пакеты, чтобы не засорять # ими dmesg: $block_out $on_o lo $proto udp $from $lh $to $lh $du_port 512 $user main_user ## Если нужно, эти пакеты могут быть отдельно залогированы ниже следующим ## правилом: #$match_out_log $log_label "mail UDP packets: " \ # $on_o lo $proto udp $from $lh $to $lh $du_port 512 $user main_user ##------------------------------------------------------------------ ## Правила для прямого соединения main_user'а с Интернетом (опасно) ##------------------------------------------------------------------ ## Скорей всего, если ваши настройки безопасные, вам не понадобятся ## эти правила. ## Разрешить DNS-запросы (DNS-порты и DNS-сервера фиксированы ## функцией allow_dns): #allow_dns main_user ## Разрешить весь TCP-трафик в Интернет: #allow_out $oIF tcp $oIP all 0:65535 main_user ## но лучше его ограничить более специфичными правилами (см. ниже). ## Разрешить соединение с конкретным списком хостов. Каждый список ## имеет формат "host:port" (один на строку), причём возможны ## комментарии и пустые строки: #multi_allow_out $oIF tcp $oIP host_and_port_list_1 main_user #multi_allow_out $oIF tcp $oIP host_and_port_list_2 main_user ## Разрешить соединение с конкретными SSH-серверами (задаются ## по IP): #allow_out $oIF tcp $oIP X.X.X.X 22 main_user #allow_out $oIF tcp $oIP Y.Y.Y.Y 22 main_user ## Разрешить соединение с конкретными XMPP-серверами (задаются ## по IP): #allow_out $oIF tcp $oIP X.X.X.X 5222 main_user #allow_out $oIF tcp $oIP Y.Y.Y.Y 5223 main_user ################################################################################ # Правила для пользователя debian-tor (loopback-интерфейс) ################################################################################ # Разрешаем Tor'у принимать соединения от других пользователей на # loopback-интерфейсе [порты, разделённые пробелом, будут в разных правилах # iptables, что позволяет отслеживать счётчики пакетов (команда "iptabels-save # -c") по каждому из портов или их списков отдельно]: allow_in_tor lo $lh $lh 9150 9510 9070:9072 9080 9099 debian-tor ##------------------------------------------------------------------ ## DNS-резолвинг через Tor (если какой-то программе требуется): ##------------------------------------------------------------------ ## В норме DNS не требуется, поскольку там, где это нужно, ## DNS-резолвинг происходит на exit-нодах Tor'а автоматически. ## Однако, если какой-то программе всё же нужен локальный ## DNS-резолвинг, можно разрешить его делать через Tor [не забудьте ## указать опцию DNSPort в torrc, добавить "nameserver 127.0.0.1" в ## resolv.conf и указать нужное allow_out-правило для того ## пользователя, которому нужен DNS (см. ниже пример правила для ## пользователя "userX")]: #allow_in lo udp $lh $lh 53 debian-tor ## Некоторые пакеты ошибочно получают root'а в качестве owner'а, ## поэтому для работы DNS следующее правило также необходимо: #$pass_out $on_o lo $proto udp $from $lh $su_port 53 $to $lh \ # $user root $keep_state ################################################################################ # Правила для пользователя debian-tor (ethernet-интерфейс) ################################################################################ ## Нужно позволить Tor'у соединяться с Интернетом. Мы можем разрешить все хосты: #allow_out $oIF tcp $oIP all 0:65535 debian-tor ## но лучше ограничить список разрешённых хостов только (какими-то) Tor-нодами. ## Ниже предложены правила для двух типов настроек: ipset (все Tor-ноды ## разрешены) и фиксированные guard-ноды (разрешены только некоторые Tor-ноды из ## имеющих guard-флаг). Вариант, разрешённый в скрипте по умолчанию -- ipset. #------------------------------------------------------------------ # Метод "ipset" (трафик разрешён ко всем Tor-нодам): #------------------------------------------------------------------ # Требуется установленный ipset (см. выше), чтобы использовать # списки Tor-нод в правилах фильтрации. В норме Tor соединяется # только с портами ORPort (см. man torrc) тех нод, которые имеют # guard-флаг. Однако, при первом запуске Tor может соединяться с # портами ORPort и других нод. Существует иной порт, DirPort (см. # man torrc), используемый Tor'ом для получения статистики. Пока # непонятно, может ли Tor-клиент коннектиться к портам DirPort # каких-либо нод напрямую, но такая возможно разрешена здесь # используемыми правилами фильтрации. Таким образом, у нас есть # два типа портов и два типа Tor-нод (с guard-флагом и без него) -- # вместе получается 4 различных варианта списков ipset, все из # которых могут быть обновлены вручную с помощью скрипта # ipset_create_DB.sh, который использует файлы Tor-консенсуса в # /var. В списках ipset фиксированы как IP, так и порты Tor-нод. # Разрешить соединение с портами ORPort нод, имеющих guard-флаг: allow_out_ipset $oIF tcp $oIP GuardORPort debian-tor # Разрешить соединение с портами ORPort нод без guard-флага: allow_out_ipset $oIF tcp $oIP NonGuardORPort debian-tor # Разрешить соединение с портами DirPort нод, имеющих guard-флаг: allow_out_ipset $oIF tcp $oIP GuardDirPort debian-tor # Разрешить соединение с портами DirPort нод без guard-флага: allow_out_ipset $oIF tcp $oIP NonGuardDirPort debian-tor #------------------------------------------------------------------ # Проблема ошибочно блокируемых пакетов в методе "ipset": #------------------------------------------------------------------ # После выполнения "/etc/init.d/tor stop" множество нужных # Tor-пакетов блокируется, потому что они не имеют какого-либо # owner'а (нам нужен owner = debian-tor). Формат блокируемых # пакетов выглядит примерно так: # # SRC=$oIP DST=Tor_IP LEN=40 TOS=0x00 PREC=0x00 TTL=64 ID=XXXX DF # PROTO=TCP SPT=XXXX DPT=Tor_port WINDOW=XXXX RES=0x00 ACK URGP=0 # # Причина проблемы неизвестна. Поскольку нет желания логировать # такие пакеты и засорять ими dmesg, мы их здесь же и заблокируем: $block_out $on_o $oIF $proto tcp $from $oIP \ $for_ipset GuardORPort $to_ipset $ipt_module tcp # Нет понимания, нужно ли указывать "$ipt_module tcp" в # вышеприведённой и других командах, но вариант с написанием # встречается в некоторых статьях в сети. # # Списки ipset содержат как IP, так и порт для каждого хоста. # Вышеприведённые правила (см. также функцию allow_out_ipset в # скрипте pf2iptables.sh) работают и без указания "$ipt_module tcp" # [однако, есть опасность, что порт хоста может игнорироваться в # этом случае, т.е. все порты трактоваться как разрешённые -- я не # проверял, так ли это]. Вообще, опция "$ipt_module tcp" (т.е. "-m # tcp") нужна в обычных правилах iptables, когда требуется указать # TCP-порт, но здесь TCP-порт указан в базе данных ipset-списка. # Одним словом, непонятно, нужно ли продолжать указывать эту опцию # в правилах с ipset. ## Нижеприведённое правило решило бы проблему ошибочно блокируемых ## пакетов, но оно слишком небезопасно, поскольку позволяет всем ## пользователям посылать некоторые пакеты (правда, возможно, ## только в определённых случаях) в Интернет напрямую: #$pass_out $on_o $oIF $proto tcp $from $oIP \ # $for_ipset GuardORPort $to_ipset $ipt_module tcp ##------------------------------------------------------------------ ## Метод "фиксированные guard'ы" (лишь некоторые guard'ы разрешены) ##------------------------------------------------------------------ ## Если используются только какие-то конкретные guard-ноды, напрямую ## заданные в torrc-файле, можно разрешить Tor'у соединяться только ## с ними (требуется приготовить файл "guard_list_file" и положить ## его в директорию, где находятся все скрипты): #multi_allow_out $oIF tcp $oIP guard_list_file debian-tor ## В принципе, по аналогии с ipset-скриптом мы могли бы узнавать ## список текущих guard'ов, используемых Tor'ом, анализируя ## state-файл. Однако, если мы ограничим Tor соединениями только с ## ними, возникнут проблемы, когда Tor захочет обновить их список ## (что случается не так часто, но, тем не менее) и ошибочно ## посчитает новые выбираемые guard'ы нерабочими. Наверно, лучше не ## вмешиваться в поведение Tor'а таким жёстким образом, хотя более ## умные варианты фильтрации, позволяющие Tor'у обновлять списки ## guard'ов, могли бы использоваться. В частности, ipset позволяет ## динамически формировать и обновлять списки хостов по факту ## срабатывания нужных правил фильтрации/мачинга. ################################################################################ # Правила для пользователя root ################################################################################ # Разрешить соединение с Tor'ом на loopback-интерфейсе (к примеру, для apt-get): allow_out lo tcp $lh $lh 9080 root # Разрешить принимать соединения по SSH на loopback-интерфейсе (нужно # пользователю ssh_user): allow_in lo tcp $lh $lh 22 root ##------------------------------------------------------------------ ## Правила для прямого содиения root'а с Интернетом (небезопасно): ##------------------------------------------------------------------ ## --- Интернет-соединение --- ## Разрешить соединение с Интернетом (все хосты по TCP и несколько ## заданных хостов для DNS): #allow_out $oIF tcp $oIP all 0:65535 root #allow_dns root ## ------ DHCP--запросы ------ ## Возможно, эти правила имеют смысл для каких-то старых систем, но ## в современных системах DHCP-запросы и ответы вообще не ## фильтруются посредством iptables: #dhcp_server=X.X.X.X #allow_dhcp $dhcp_server ## Это почти точно не нужно: ## My IP: #mIP=Y.Y.Y.Y #$pass_in $on_i $oIF $proto udp $from $dhcp_server $su_port 67 \ # $to $mIP $du_port 68 $keep_state ################################################################################ # Правила для пользователя userX ################################################################################ # Разрешить соединение с SOCKS-сервером SSH'а, запускаемого от имени # пользователя main_user [поскольку правило для него использует функцию # lh_filter (см. выше), здесь достаточно указать $pass_out вместо allow_out # (см. пояснения выше)]: $pass_out $on_o lo $proto tcp $from $lh $to $lh $dt_port 40000 $user userX ## Если это требуется для каких-то программ, можно разрешить пользователю userX ## делать DNS-резолвинг через Tor: #allow_out lo udp $lh $lh 53 userX ## Можно разрешить соединение с privoxy, если нужно: #allow_out lo tcp $lh $lh 8118 userX ##------------------------------------------------------------------ ## Прямое соединение пользователя userX с Интернетом (опасно) ##------------------------------------------------------------------ ## Этого должно быть достаточно: #allow_dns userX #allow_out $oIF tcp $oIP all 0:65535 userX ## Лучше не позволять никакому пользователю (помимо debian-tor) ## напрямую соединяться с Интернетом во время нормальной работы ## системы (при множестве залогиненных пользователей и т.д.). В тех ## редких случаях, когда прямое соединение допустимо и необходимо, ## лучше использовать не этот iptables-скрипт, а отдельный простой, ## специально заточенный под эту задачу. Стоит отметить, что ## перезагрузка правил iptables -- тоже не безопасная процедура при ## залогиненных пользователях. ################################################################################ # Правила для других пользователей ################################################################################ # Пользователю "music_user" разрешено быть клиентом и сервером для сервиса mpd: lh_filter 6600 music_user # Пользователю "ssh_user" разрешаем получать root'а по SSH на # loopback-интерфейсе: allow_out lo tcp $lh $lh 22 ssh_user ################################################################################ # Неиспользуемые пользователи ################################################################################ ## ------ privoxy ------ ## Если используется privoxy, разрешаем ей принимать соединения на "lo": #allow_in lo tcp $lh $lh 8118 privoxy ## Если privoxy использует Tor как parent proxy с определённым портом ## "SOME_Tor_PORT", разрешаем ей коннектиться на этот порт: #allow_out lo tcp $lh $lh SOME_Tor_PORT privoxy ## Если privoxy нужно соединяться с SSH SOCKS-прокси (запущенной от имени ## main_user) как с parent proxy, разрешаем это (используется $pass_out вместо ## allow_out, потому что порт 40000 был добавлен посредством правила lh_filter): #$pass_out $on_o lo $proto tcp $from $lh $to $lh $dt_port 40000 $user privoxy ## ------- userY ------- ## Пользователю userY разрешаем прямой доступ в Интернет (всё TCP, но UDP только ## для DNS): #allow_dns userY #allow_out $oIF tcp $oIP all 0:65535 userY ################################################################################ # Блокирование и логирование всех остальных соединений ################################################################################ # Множество Tor-пакетов ошибочно блокируются на lo-интерфейсе, потому что они # потеряли параметр "owner" и не подпадают под состояние TCP-сессии (state # ESTABLISHED). Пакеты имеют вид: # # IN= OUT=lo SRC=$lh DST=$lh LEN=40 TOS=0x00 PREC=0x00 TTL=64 ID=XXXXX DF # PROTO=TCP SPT=XXXXX DPT=Tor_Port WINDOW=XXXX RES=0x00 ACK FIN URGP=0 # # IN= OUT=lo SRC=$lh DST=$lh LEN=40 TOS=0x00 PREC=0x00 TTL=64 ID=XXXX DF # PROTO=TCP SPT=Tor_Port DPT=XXXXX WINDOW=XXX RES=0x00 ACK FIN URGP=0 # # Чтобы не засорять ими dmesg, мы блокируем их до попадания в лог (аналогично # для SSH, mpd и др. сервисов): block_bug_mports lo $lh $lh 9150 9510 9070:9072 9080 9099 22 6600 40000 # Прыгаем в LOGGING-цепочку iptables'а: start_logging # ------------------------------------------------------------------------ # Если некоторые Tor-пакеты на ethernet-интерфейсе будут блокироваться, # здесь мы сможем проанализировать причину, логируя их в dmesg'е (пока что # ошибочных блокирований не замечено): #for IPSET in GuardORPort GuardDirPort NonGuardORPort NonGuardDirPort ; do # for SRC_USER in debian-tor root ; do # $match_out_log $log_label "$IPSET $SRC_USER: " \ # $on_o $oIF $proto tcp $from $oIP \ # $for_ipset $IPSET $to_ipset $ipt_module tcp $user $SRC_USER # done #done #for IPSET in GuardORPort GuardDirPort NonGuardORPort NonGuardDirPort ; do # $match_out_log $log_label "$IPSET no_user: " \ # $on_o $oIF $proto tcp $from $oIP \ # $for_ipset $IPSET $to_ipset $ipt_module tcp #done # ------------------------------------------------------------------------ # Логируем все пакеты, которые достигли этой стадии (ещё не были разрешены # или заблокированы). Используем метки, чтобы указать на направление, # интерфейс и протокол (например, "dmesg |grep 'out,eth,TCP'" покажет # список записанных в лог исходящих TCP-пакетов на ethernet-интерфейсе): # Пакеты на loopback-интерфейсе: log_out 'out,lo,TCP:' lo tcp log_out 'out,lo,UDP:' lo udp log_out 'out,lo,ICMP:' lo icmp log_out 'out,lo,GEN:' lo log_in 'in,lo,TCP:' lo tcp $lh log_in 'in,lo,UDP:' lo udp $lh log_in 'in,lo,ICMP:' lo icmp $lh log_in 'in,lo,GEN:' lo $lh log_in lo # Пакеты на ethernet-интерфейсе: log_out 'out,eth,TCP:' $oIF tcp log_out 'out,eth,UDP:' $oIF udp log_out 'out,eth,ICMP:' $oIF icmp log_out 'out,eth,GEN:' $oIF log_in 'in,eth,TCP:' $oIF tcp $oIP log_in 'in,eth,UDP:' $oIF udp $oIP log_in 'in,eth,ICMP:' $oIF icmp $oIP log_in 'in,eth,GEN:' $oIF $oIP log_in $oIF # Наконец, блокируем все записанные в лог пакеты: $block_log # В этих правилах нет необходимости, потому что на этой стадии все пакеты уже # были разрешены или заблокированы (или залогированы и потом заблокированы), # но для гарантии лучше оставить эти правила здесь, в конце [если в процессе # редактирования скрипт(а,ов) где-то будет допущена фатальная ошибка, эти # правила станут последним эшелоном, который может уменьшить утечку]: $block_out $on_o $oIF $proto tcp $block_out $on_o $oIF $proto udp $block_out $on_o $oIF $proto icmp # EOF
allow_out lo tcp $lh $lh 9150 torbrowser_user $block_out $on_o lo $proto tcp $from $lh $to $lh $dt_port 9151 \ $user torbrowser_user allow_out lo tcp $lh $lh 9510 torbrowser_user lh_filter 40000 main_user $pass_out $on_o lo $proto tcp $from $lh $to $lh $dt_port 6600 $user main_user allow_out_tor lo $lh $lh 9070:9072 9099 main_user $block_out $on_o lo $proto udp $from $lh $to $lh $du_port 512 $user main_user allow_in_tor lo $lh $lh 9150 9510 9070:9072 9080 9099 debian-tor allow_out_ipset $oIF tcp $oIP GuardORPort debian-tor allow_out_ipset $oIF tcp $oIP NonGuardORPort debian-tor allow_out_ipset $oIF tcp $oIP GuardDirPort debian-tor allow_out_ipset $oIF tcp $oIP NonGuardDirPort debian-tor $block_out $on_o $oIF $proto tcp $from $oIP \ $for_ipset GuardORPort $to_ipset $ipt_module tcp allow_out lo tcp $lh $lh 9080 root allow_in lo tcp $lh $lh 22 root $pass_out $on_o lo $proto tcp $from $lh $to $lh $dt_port 40000 $user userX lh_filter 6600 music_user allow_out lo tcp $lh $lh 22 ssh_user block_bug_mports lo $lh $lh 9150 9510 9070:9072 9080 9099 22 6600 40000
Буду благодарен за критику и замечания.
1 В современных версиях PF настоящий pass out подразумеает keep state, т.е. разрешение обратных пакетов-ответов.
2 Часть пакетов проходит как принадлежащие debian-tor, как и должно быть, а другая часть — почему-то как принадлежащая root. Должен заметить, что в BSD тоже есть такие ошибки.