Некоторое время назад наводил порядок в фаерволлах на серверах FreeBSD, доставшихся в наследство от прошлых системных администраторов. Захотелось сразу продумать фаерволл таким образом, чтобы потом в нём было легко ориентироваться и было очевидно, как в него добавить новое правило. Использовался фаерволл ipfw и его таблицы. Я решил не менять фаерволл на ipf или pf, а просто последовательно преобразовывать имеющиеся правила в понятную структуру, пока меня не устроит конечный результат. Результатом и хочу поделиться, как одним из примеров того, как можно организовать фаерволл.
В файле /etc/rc.conf должны быть две строчки, предписывающие включить фаерволл и использовать правила из файла /etc/firewall.conf:
firewall_enable="YES" firewall_script="/etc/firewall.conf"
Теперь приведу упрощённый пример файла /etc/firewall.conf:
fw='/sbin/ipfw -q' ${fw} -f flush ${fw} add pass all from any to any via lo0 ${fw} add deny all from any to 127.0.0.0/8 ${fw} add deny all from 127.0.0.0/8 to any ${fw} add check-state ${fw} add allow ip from me to any keep-state ########## ping ########## ${fw} add allow icmp from any to me in via em0 keep-state ########## ssh ########## table_ssh=100 ${fw} table $table_ssh flush ${fw} add allow tcp from "table($table_ssh)" to me 22 in via em0 keep-state # lan gateway ${fw} table $table_ssh add 192.168.0.1 # office computers ${fw} table $table_ssh add 192.168.0.128/25 ######################################### ${fw} add deny ip from any to any
Фаерволл разрешает серверу устанавливать любые исходящие подключения. Любому внешнему сетевому узлу разрешается выполнять ICMP-запросы (среди которых могут быть не только запросы Ping). Последнее правило запрещает любой входящий трафик, который не разрешён явным образом.
В начале файла есть команда, которая очищает текущий список правил фаерволла:
${fw} -f flush
При очистке списка правил таблицы остаются нетронутыми. Поэтому внутри каждого блока, разрешающего доступ к определённому сервису на сервере, выполняется очистка соответствующей таблицы:
${fw} table $table_ssh flush
После очистки таблицы в списке правил фаерволла создаётся необходимое правило, а затем наполняется таблица, связанная с этим правилом.
Наибольший интерес представляет блок, разрешающий подключения по SSH и продолжающийся вплоть до последнего правила. Чтобы разрешить доступ к ещё одному сервису, можно скопировать блок, переименовать переменную, хранящую номер таблицы, поменять сам номер таблицы. Затем нужно заменить имя таблицы в последующих строчках, отредактировать само правило и изменить список разрешённых IP-адресов и сетей, которые будут добавлены в таблицу. Например, можно добавить блок, разрешающий доступ по HTTP:
########## http ########## table_http=110 ${fw} table $table_http flush ${fw} add allow tcp from "table($table_http)" to me 80 in via em0 keep-state # office computers ${fw} table $table_http add 192.168.0.128/25 # zabbix server ${fw} table $table_http add 192.168.0.2
Как видно, всё очень просто. Для применения изменений нужно выполнить всего одну команду:
# /etc/rc.d/ipfw restart
Будьте внимательны, не допускайте ошибок в файле - иначе он может выполниться не до конца и можно потерять удалённый доступ к серверу. Советую первыми в список включать правила для SSH - тогда они будут отрабатывать раньше, чем интерпретатор дойдёт до строки с ошибкой и отбросит весь остаток файла. Вероятность того, что правила SSH будут применены при ошибках в других более часто редактируемых правилах, будет выше. А ещё лучше перед применением отредактированного файла изменить значение переменной fw с этого:
fw='/sbin/ipfw -q'
На вот это:
fw='/sbin/ipfw -q -n'
Команды не будут выполняться реально, будет только проверен их синтаксис. Если сообщений об ошибках не получено, то можно вернуть значение fw на исходное и снова выполнить команду, уже для реального применения изменений.
Ещё одно достоинство этого фаерволла - он получается простым, т.к. в самом фаерволле есть всего несколько правил. Для наглядности приведу реальный список правил в фаерволле на одном из серверов:
# ipfw list 00100 allow ip from any to any via lo0 00200 deny ip from any to 127.0.0.0/8 00300 deny ip from 127.0.0.0/8 to any 00400 check-state 00500 allow ip from me to any keep-state 00600 allow icmp from any to me in via em0 keep-state 00700 allow tcp from table(110) to me dst-port 3306 in via em0 keep-state 00800 allow tcp from table(111) to me dst-port 10054 in via em0 keep-state 00900 allow tcp from table(112) to me dst-port 80 in via em0 keep-state 01000 allow tcp from table(116) to me dst-port 22 in via em0 keep-state 01100 allow udp from table(117) to me dst-port 69 in via em0 keep-state 01200 allow tcp from table(118) to me dst-port 48048 in via vlan32 keep-state 01300 allow udp from table(119) to me dst-port 162 in via em0 01400 deny ip from any to any 65535 deny ip from any to any
Трудно запутаться в семи существенных правилах, не так ли?
Второе достоинство фаерволла - он достаточно быстр, т.к. сравнение IP-адресов клиентов происходит с использованием таблиц, а не при помощи отдельных правил, которые просматриваются последовательно. Посмотреть текущее содержимое любой из таблиц тоже довольно просто:
# ipfw table 118 list 10.1.206.0/24 0 10.1.217.0/24 0
Если нужно сделать так, чтобы IP-адреса и сети в таблицу добавлялись динамически, каким-нибудь внешним скриптом, то достаточно не вписывать команды, очищающие таблицу и добавляющие в неё какие-то правила. Например, вот так:
########## tftp ########## table_tftp=120 ${fw} add allow tcp from "table($table_tftp)" to me 69 in via em0 keep-state
Таким образом, при применении изменённых правил, динамически добавленные в таблицу адреса не исчезнут из таблицы.
Поскольку мы настроили фаерволл с поддержкой состояний, то правила для обратного трафика будут добавляться в него динамически. Таким образом, нужно следить за тем, чтобы список динамических правил не переполнялся. Контролировать текущее количество динамических правил в списке и их максимальное возможное количество можно при помощи утилиты sysctl:
# sysctl net.inet.ip.fw.dyn_count net.inet.ip.fw.dyn_count: 656 # sysctl net.inet.ip.fw.dyn_max net.inet.ip.fw.dyn_max: 8192
Если значение первого счётчика будет близко подбираться к значению второго, то максимальное количество динамических правил можно увеличить при помощи sysctl:
# sysctl -w net.inet.ip.fw.dyn_max=16384
И чтобы это значение восстанавливалось при перезагрузке, нужно прописать его в файл /etc/sysctl.conf:
net.inet.ip.fw.dyn_max=16384