Настройка простого firewall на ipfw и NAT через natd во FreeBSD

Подключились к локальной сети, и хотим раздавать интернет из этой локальной сети в домашней сети.
На интерфейсе подключенном к локальной сети реальные IP адреса, привязка по MAC адресу, интернет доступен сразу, без дополнительных трюков с подключением VPN.

Firewall мы будем использовать стандартный - ipfw.
NAT будем использовать - natd.
Прописываем сетевые интерфейсы и правим MAC адрес нашего сетевого адаптера на подходящий

Записываем в rc.conf параметры нашей домашней сети и локальной сети. vr0 - внешний (публичный) интерфейс, vr2 - внутренний интерфейс подключенный к домашней сети.
ifconfig_vr0="inet 123.123.123.123 netmask 255.255.255.0"
ifconfig_vr0_alias0="link 00:aa:aa:aa:aa:aa"
ifconfig_vr2="inet 192.168.12.254 netmask 255.255.255.0"
defaultrouter="123.123.123.1"

Обратите внимание на команду - ifconfig_vr0_alias0=«link 00:aa:aa:aa:aa:aa» - тут надо вписать MAC адрес который должен быть у сетевой карты подключенной к этому провайдеру.
Включаем поддержку ipfw и natd в ядре

Дописываем в наш конфиг ядра следующие строчки:
options IPFIREWALL
options IPFIREWALL_VERBOSE
options IPFIREWALL_VERBOSE_LIMIT=100
options IPFIREWALL_FORWARD
options IPDIVERT
options DUMMYNET

Описание данных опций:

options IPFIREWALL - включает собственно firewall на основе ipfw, кроме ipfw в FreeBSD есть еще firewall-ы ipf и pf. Все хороши, так что если выберете другой - он тоже отлично будет работать. Но документацию тогда читайте конечно подходящую к типу firewall-а ;)

options IPFIREWALL_VERBOSE - Поддержка записи проходящих пакетов в лог-файл, при использовании опции log в правилах и активации log-а для пакетов в rc.conf или через sysctl переменную.

options IPFIREWALL_VERBOSE_LIMIT=100 - Указывает количество пакетов которые будут записаны в log для каждого правила. Значение по умолчанию. Значение по умолчанию может быть переопределено с помощью переменной sysctl - net.inet.ip.fw.verbose_limit. Для конкретного правила ipfw может быть переопределено с помощью ipfw log logamount на другое желаемое значение. Например:
ipfw add deny log logamount 300 ip from any to any

options IPFIREWALL_FORWARD - Поддержка перенаправления пакетов между сетевыми интерфейсами.

options IPDIVERT - Поддержка NAT и natd в частности.

options DUMMYNET - Поддержка ограничителя скорости.

Кроме того есть параметр
options IPFIREWALL_DEFAULT_TO_ACCEPT

Если ядро собрать с этой опцией, и включить firewall - правило по умолчанию будет:
allow ip from any to any

Иначе же правило по умолчанию будет:
deny ip from any to any

Если сервер настраивается через ssh - то можно для подстраховки включить эту опцию в ядре, собрать ядро, установить, потом после окончания настройки ipfw и получения рабочей конфигурации - выключить эту опцию, и пересобрать ядро снова уже без нее (для безопасности).
итак - собираем ядро:
cd /usr/src
make buildkernel KERNCONF=MY_KERNEL
make installkernel KERNCONF=MY_KERNEL

о сборке ядра - читаем соответствующий раздел handbook-а.
Включаем поддержку firewall в системе и пишем простенький скрипт заглушку

Для того что бы ipfw и natd заработали нужно внести изменения в rc.conf, дописав примерно следующие строки:
firewall_enable="YES"
firewall_script="/etc/ipfw.rules"
firewall_logging="YES"
gateway_enable="YES"
natd_enable="YES"
natd_interface="vr0"
natd_flags="-m"

Описание опций

firewall_enable=«YES» - включить собственно firewall. Если firewall имеется в ядре - он включается, в ином случае подгружается модуль.
Осторожно! При загрузке модулем, правило по умолчанию deny ip from any to any, так что если вы включили options IPFIREWALL_DEFAULT_TO_ACCEPT в ядре, забыли установить новое ядро, и включили firewall - в надежде на правило allow ip from any to any - вас может постигнуть разочарование

firewall_script=»/etc/ipfw.rules» - Обычный shell скрипт который запустится при старте firewall - по идее там должны находиться команды ipfw которые опишут наш firewall. Эта сточка может и отсутствовать - тогда запуститься скрипт по умолчанию - /etc/rc.firewall.

firewall_logging=«YES» - Включить запись проходящих пакетов в log. При условии что в правиле для указано ключевое слово log.

gateway_enable=«YES» - форвардинг пакетов между интерфейсами. Присутствие этой опции в rc.conf устанавливает значение sysctl переменной net.inet.ip.forwarding в 1 при запуске компьютера.

natd_enable=«YES» - Включение NAT демона - natd.

natd_interface=«vr0» - Внешний интерфейс для NAT. Публичный интерфейс.

natd_flags=»-m» - Флаги которые передаются natd. Подробней смотрите в man natd.

После внесения опция в rc.conf - пишем скрипт заглушку для нашего firewall-а в файле /etc/ipfw.rules. Этот firewall будет позволять доступ отовсюду.
#!/bin/sh

# ipfw cmd
fwcmd="/sbin/ipfw"
${fwcmd} -q -f flush
${fwcmd} add allow ip from any to any

После чего перезагружаемся.
reboot

Если все сделали правильно - то после перезагрузки - мы

сможем снова попасть на свою машину по ssh.

увидим в выводе ipfw show - наши правила (пока всего одно правило и правило по умолчанию)
Пишем простой firewall

В ipfw правила выполняются следующим образом: Проходящий пакет проверяется на соответствие правилам, в том порядке в котором они расположены, до тех пор пока встретится подходящее для него - после чего над пакетом выполняется действие описанное в правиле.
Правила ipfw - нумеруются и первым проверяется правило с меньшим номером, и т.д. После того как пакет попал под какое нибудь правило - он вываливается из firewall-а.
Исключение - пакеты которые попали под divert (и например в NAT через natd), после действий над ними в divert они возвращаются в firewall, в следущее после divert правило, и идут дальше по списку правил, до тех пор пока встретят подходящее правило.
Это поведение можно изменить, но в данной статье подробности об этом опущу. Синтаксис ipfw - опущу, дабы избегать лишнего повторения и загромождения текста статьи. Смотрите man ipfw, соответствующий раздел handbook-а, и ссылки в конце статьи.

Мне понравился следующий метод отладки firewall:

создаем файл для тестирования firewall, например /etc/ipfw.rules.test.

делаем его исполняемым. Вносим в него команды для формирования нашего firewall.

запускаем.

если результат нас удовлетворяет - то вносим содержимое тестового firewall-а в скрипт который будет выполняться при загрузке машины. В ином случае - правим скрипт и повторяем с пункта 3.

Если мы создали в файле /etc/ipfw.rules.test firewall который заблокировал нам удаленный доступ к машине - машину нужно перезагрузить, после этого загрузится firewall из /etc/ipfw.rules который наверняка работает.
Полезные команды ipfw:

ipfw list - показывает список правил

ipfw show - тоже показывает список правил, но вместе с числом пакетов и байт, что попали под эти правила. Очень удобно для отладки.

ipfw -d show - показывает динамические правила (keep state).

ipfw zero - обнуление всех счётчиков. Если вести в виде ipfw zero #_правила то будет обнулён только счётчик правила с этим номером.

Имеет смысл соблюдать такую последовательность правил:

Запрещающие правила

NAT

Разрешающие правила

Запретить все

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

После отладки у меня получился, такой вот простой firewall:
#!/bin/sh

# ipfw cmd
fwcmd="/sbin/ipfw"

# Internal Interface
iif="vr2"
# External Interface
oif="vr0"

# Internal IP address
iip="192.168.78.254"
# External IP address
oip="178.165.65.79"

# Internal LAN
ilan="192.168.78.0/24"

${fwcmd} -q -f flush

# Check dynamic rules
${fwcmd} add check-state

# Allow localhost internal activity
${fwcmd} add allow ip from any to any via lo0

# don't allow localhost to send packets outside
${fwcmd} add deny ip from any to 127.0.0.0/8
${fwcmd} add deny ip from 127.0.0.0/8 to any

# Deny fragmented icmp packets
${fwcmd} add deny icmp from any to any frag

# Deny connections to internal network from external network.
${fwcmd} add deny ip from any to ${ilan} in via ${oif}

# Deny broadcast icmp on external interface
${fwcmd} add deny log icmp from any to 255.255.255.255 in via ${oif}
${fwcmd} add deny log icmp from any to 255.255.255.255 out via ${oif}

# Do NAT for internal network
${fwcmd} add divert natd ip from ${ilan} to any out via ${oif}
${fwcmd} add divert natd ip from any to ${oip} in via ${oif}

# Allow established connects
${fwcmd} add allow tcp from any to any established

# Allow outgoing traffic
${fwcmd} add allow ip from ${oip} to any out xmit ${oif}

# Allow multicast
${fwcmd} add allow igmp from any to 224.0.0.0/8 in via ${oif}
${fwcmd} add allow igmp from any to 239.0.0.0/8 in via ${oif}
${fwcmd} add allow udp from any to 239.0.0.0/8 in via ${oif}

# Allow DNS
${fwcmd} add allow udp from any 53 to any via ${oif}

# Allow NTP
${fwcmd} add allow udp from any to any 123 via ${oif}

# Allow SSH connection to server
${fwcmd} add allow tcp from any to me 22 in via ${oif}

# Allow HTTP connection to the server
${fwcmd} add allow tcp from any to me 80 in via ${oif}

# Allow FTP connection to the server
${fwcmd} add allow tcp from any to me 20,21,64000-64999 via ${oif}

# Allow icmp echo request, echo reply and expire packet TTL
${fwcmd} add allow icmp from any to any icmptypes 0,8,11

# Allow connects from localnetwork
${fwcmd} add allow ip from any to any via ${iif}

# Deny other activity
${fwcmd} add deny log logamount 10000 ip from any to any


Так же, возможно, будут интересны следующие параметры sysctl что относятся к firewall:
# sysctl -a | grep ip.fw
net.inet.ip.fw.static_count: 17
net.inet.ip.fw.default_to_accept: 0
net.inet.ip.fw.tables_max: 128
net.inet.ip.fw.default_rule: 65535
net.inet.ip.fw.verbose_limit: 100
net.inet.ip.fw.verbose: 1
net.inet.ip.fw.autoinc_step: 100
net.inet.ip.fw.one_pass: 1
net.inet.ip.fw.dyn_keepalive: 1
net.inet.ip.fw.dyn_short_lifetime: 5
net.inet.ip.fw.dyn_udp_lifetime: 10
net.inet.ip.fw.dyn_rst_lifetime: 1
net.inet.ip.fw.dyn_fin_lifetime: 1
net.inet.ip.fw.dyn_syn_lifetime: 20
net.inet.ip.fw.dyn_ack_lifetime: 300
net.inet.ip.fw.dyn_max: 4096
net.inet.ip.fw.dyn_count: 0
net.inet.ip.fw.curr_dyn_buckets: 256
net.inet.ip.fw.dyn_buckets: 256
net.inet.ip.fw.enable: 1

Заметки

правила check-state и tcp from any to any established в начале firewall-а могут поднять производительность, т.к. будут проверять динамические правила и установленные соединения в самом начале, в результате чего количество проверок уменьшится.

http://jakshi.org.ua/dokuwiki/настройка_простого_firewall_на_ipfw_и_nat_через_natd

Обновлено: 12.03.2015