NAT во FreeBSD с помощью IPFilter (ipnat)


Содержание
Введение
Подготовка
Простенький stateful NAT
Перенаправление (redirect)
FTP proxy
Использование отдельного IP-адреса для трансляций на внешнем интерфейсе
Безусловная полная трансляция
Контроль над трансляцией
Введение

Синдром гамака является одним из ключевых моментов деградации сисадмина. Он начинается с того, что стабильность и неподвижность становятся синонимами. Когда-то, до того, как IPFilter включили в состав FreeBSD, единственным нормальным способом сделать NAT во FreeBSD был natd. Обязательным условием для полноценной работы natd является наличие dummynet(4). natd и до сих пор может быть в некоторых случаях актуален. Мы же рассмотрим использование ipnat из IPFilter в качестве средства для Network Address Translation (NAT).

Этот документ не является пошаговым howto для чайников — подразумевается, что не нужно объяснять, что например после изменения /etc/syslog.conf ему нужно послать сигнал HUP, а файлы, куда он будет писать, создавать нужно самостоятельно и как «применять» изменённые правила. Если Вы считаете, что ещё не совсем освоились с *nix и с FreeBSD в частности, то по этой теме можно почитать:
ipnat(1), ipnat(5), ipnat(8)
ipf(5), ipf(8), ipfstat(8), ipftest(1), ipmon(8)

Здесь и далее будет считаться, что IPFilter у нас как минимум версии 3.4.29. Если в Вашем случае это не так — то Вы ещё не совсем вылезли из гамака, хотя уже тот факт, что Вы читаете этот текст, является доказательством Вашего на то желания.
Подготовка

Итак, рассмотрим ситуацию: мы имеем 2 интерфейса — fxp0 (200.200.200.1), смотрящий в большой и злой интернет, и dc0 (192.168.0.1), смотрящий во внутреннюю сеть.

Задача: сделать возможность пользователям внутренней сети создавать соединения к внешним хостам без использования socks5/http proxy и т.п.

В ядре нам понадобятся всего 2 строчки:
options IPFILTER
options IPFILTER_LOG

После этого IPFilter доступен нам во всей своей красе.

Также в /etc/rc.conf нужно добавить строчку gateway_enable="yes" или в /etc/sysctl.conf добавить строчку net.inet.ip.forwarding=1.

Теперь всё готово — пакеты перебрасываются между интерфейсами. Осталось контролировать их переброс и заменять в них IP адрес с внутреннего на внешний и наоборот.
Простенький stateful NAT

Начнём с простого stateful NAT т.е. IP пакеты с внешнего интерфейса для получателя во внутренней сети будут транслироваться только в том случае, если пользователь из внутренней сети сам установил это соединение. Все остальные пакеты, пришедшие на внешний интерфейс для получателя во внутренней сети бы будем блокировать (см. redirect далее, если какие-то пакеты извне нужно всегда пробрасывать внутрь).

Для соединений, установленных пользователем, мы будем создавать запись в state table с помощью простого правила IPFilter:
pass out quick on fxp0 proto tcp from 192.168.1.149 to any flags S/FSRA keep state (для TCP-соединений) и
pass out quick on fxp0 proto udp from 192.168.1.149 to any keep state (для UDP-соединений).
Можно вместо двух правил сделать одно общее как для TCP, так и для UDP:
pass out quick on fxp0 proto tcp/udp from 192.168.1.149 to any keep state
, однако в таком правиле мы не сможем отслеживать tcp-пакеты с флагом SYN с помощью flags, в результате чего любой пакет из внутренней сети будет создавать запись в state table (если её не было), а не те пакеты, которые являются первыми в установке TCP-соединения (с флагом SYN). Что удобнее/лучше/больше подходит — решайте сами.

Следом за этим правилом мы поместим правило, блокирующее все пакеты для внутренней сети:
block in quick on fxp0 from any to 192.168.0/16.
Под это правило будут попадать пакеты, не являющиеся частью соединений, запрошенных из внутренней сети либо пакеты из соединений, которых нет в state table

Теперь сама трансляция. Занесём в файл трансляций (по умолчанию это /etc/ipnat.rules) правило:
map fxp0 192.168.1.149/32 -> 200.200.200.1/32 portmap tcp/udp 20000:20099

В результате мы получаем следующую картину: при установке TCP-соединения самый первый пакет (с флагом SYN) из внутренней сети с хоста 192.168.1.149 при уходе в большой и злой интернет будет подвержен простому преобразованию — адрес хоста, запросившего установку соединения будет изменён с 192.168.1.149 на 200.200.200.1. Порт будет также изменён на первый доступный из диапазона от 20000 до 20099. Как можно заметить, в данном случае хост 192.168.1.149 может открыть наружу не более 100 соединений.


Всё что осталось — применить правила. ;-)

Для удобства отслеживания работы NAT можно добавить строчку в syslog.conf: local0.* /var/log/ipmon.log и запустить утилиту мониторинга работы IPFilter — ipmon с ключами -Dvas (стать демоном, показывать более детальную информацию, отслеживать все утройства IPFilter (NAT, state table и сам IPFilter) и работать через syslog). Если получаемый log-файл кажется сумбурным — не торопитесь включать log только NAT опцией -o N. Правила IPFilter попадающие в лог можно туда направлять с другой facility. Для этого достаточно в каждое правило с флагом log добавить facilty: block in log level local2.info quick on fxp0 from any to 192.168.0/16, а в syslog.conf добавить ещё одну строку: local2.* /var/log/ipfilter.block.log. В результате сего деяния в /var/log/ipmon.log будет попадать информация о NAT (созданные трансляции, закрытые трансляции) и state table (создание записи и её удаление), а в файл /var/log/ipfilter.block.log пакеты, которые были блокированы.

Если не желаете, чтобы в большом количестве правил была указана facility и правила были короче — можно отвергнуть возможность работы ipmon через syslog и заставить его писать прямо в файл; например так: ipmon -Dv -o NS /var/log/NAT.log (трансляции и state table протоколируем в файл /var/log/NAT.log) и ipmon -Dv -o I /var/log/ipfilter.log (работу самого фильтра протоколируем в файл /var/log/ipfilter.log).

Стоит заметить, что одним правилом можно транслировать несколько хостов. Например заменив в правилах ipnat и IPFilter 192.168.1.149/32 на 192.168.128/25 мы будем транслировать пакеты хостов из внутренней сети из диапазона от 192.168.1.128 до 192.168.1.254.
Перенаправление (редирект)

Если существует необходимость безусловно транслировать какие-то пакеты, пришедшие извне (например чтобы WWW/SMTP сервер из внутренней сети) был доступен снаружи - используется редирект (redirect).

Простой вариант: транслировать пакеты от любого хоста.

rdr fxp0 200.200.200.1/32 port 8080 -> 192.168.1.17 port 80 tcp

В данном случае любой пакет, пришедший на порт 8080 внешнего интерфейса 200.200.200.1 будет «проброшен» на 192.168.1.18 порт 80. Естественно, что хост 192.168.1.17 лучше поместить в таблицу трансляций как было описано в «Простеньком stateful NAT» и с учётом того, что инициироваться соединение будет снаружи — добавить правила IPFilter, которые бы разрешали их. В случае, если ipmon протоколирует работу IPFilter, допущенную ошибку можно будет легко увидеть.

Редирект можно делать не всех пакетов, а выборочно на основании их отправителя:
rdr fxp0 from 212.118.165.100/32 to 200.200.200.1/32 port = 6000 -> 192.168.1.89 port 6000

Этим правилом будут транслироваться пакеты, пришедшие на 200.200.200.1 порт 6000 только с хоста 212.118.165.100

Не стоит забывать создавать правила IPFilter, разрешающие прохождение таких пакетов. Хотя, если ipmon настроен и запущен, то он быстро об этом напомнит. ;-)
FTP proxy

Итак, всё работает замечательно, за исключением FTP из внутренней сети. Это обусловлено тем, что в ходе установки соединения, по которому будут передаваться данные, указывается IP-адрес клиента, который в нашем случае принадлежит частной сети.

ipnat способен в таких случаях быть «прокси-сервером» — то есть «ковыряться» в данных, передаваемых FTP-серверу и от него, и транслировать адреса из внутренних во внешние и наоборот. Для этого используется такое правило:
map fxp0 192.168.1.149/32 -> 200.200.200.1/32 proxy port ftp ftp/tcp

proxy-правило должно обязательно стоять перед другими правилами (за исключением redirect).
Использование отдельного IP-адреса для трансляций на внешнем интерфейсе

Если у Вас имеется в наличии не 1 IP-адрес, а сегмент сети, может быть удобным транслировать не с тех адресов, которые установлены на внешнем интерфейсе (fxp0), а с других. При этом важным является то, что на самом внешнем интерфейсе (fxp0) этот IP-адрес устанавливать нет никакой необходимости. ipnat делая трансляцию не открывает сокетов — об этом нужно помнить.

Допустим, что сеть 200.200.200.0/24 полностью принадлежит нам. В таком случае, во всех правилах трансляции мы можем ставить не реальный IP-адрес, установленный на внешнем интерфейсе (fxp0), а другой, например 200.200.200.200 (также можно по желанию сделать PTR запись для этого хоста в зоне 200.200.200.in-addr.arpa).
Безусловная полная трансляция

Если существует необходимость транслировать абсолютно все пакеты, пришедшие на какой-либо адрес (не забывайте, что этот IP-адрес необязательно должен быть установлен на внешнем интерфейсе fxp0) — используется правило bimap:
bimap fxp0 192.168.1.66/32 -> 200.200.200.100/32
и/или
bimap fxp0 192.168.1.66/32 -> 200.200.200.100/32 portmap tcp/udp
Контроль над трансляцией

Безусловно глупо настраивать NAT не глядя в протоколы, генерируемые ipmon'ом. Также возможно будет полезным ограничивать способность устанавливать соединения изнутри к привилегированным портам внешних хостов. Всё это можно и нужно настроить по желанию.

Хорошим помошником может быть старый добрый root-tail (/usr/ports/sysutils/roottail).

Обновлено: 12.03.2015