04 марта 2013

Ядерный NAT, фаервол и dhcp

Сегодня мы поднимем маршрутизатор для локальной сети на основе freebsd 9.1 с использованием ядерного nat, штатного фаервола ipfw и isc-dhcp-server.

Обновление ядра и мира
Для этого нам потребуется потребуется пересобрать ядро из исходных кодов. Если вы забыли установить их с диска или хотите получить самые последние, то воспользуемся svn.

Для этого установим subversion, процедура длится порядка 10-15 минут. Сперва обновляем порты.
portsnap fetch update
cd /usr/ports/devel/subversion
make install clean
Так же у вас есть уникальная возможность обновить мир и собрать новую систему. Переехать на другую архитектуру, версию, ветку. 

1) Выполняем первое обновление сорцов
rm -rf /usr/src
mkdir /usr/src
svn checkout svn://svn.freebsd.org/base/stable/10 /usr/src
или
#svnlite co svn://svn.freebsd.org/base/stable/10 /usr/src/ 
Можете в браузере посмотреть, что там есть
http://svn.freebsd.org/base/stable/10/
Последующие обновления делаются проще
svn update /usr/src
Вместо checkout и update можно использовать сокращения co и up, соответственно.

А далее все по хэндбуку. Почитать в первоисточнике можно здесь.

2) Если процесс построения мира уже запускался ранее на этой системе, то в /usr/obj могла остаться копия предыдущей сборки. Удалите этот каталог для ускорения процесса построения нового мира и возможного сокращений работы по разрешению зависимостей.
# chflags -R noschg /usr/obj/*
# rm -rf /usr/obj
# cd /usr/src
# make cleandir
3) Обновляем мир. Процедура крайне долгая, займет час, два и более!
cd /usr/src
make -s -j4 buildworld
Для ускорения указываем ключ -j, он запускает процесс в несколько потоков, а ключ -s уменьшает количество выводимой информации.  

3) Копируем свой конфиг из шаблона generic под именем mykernel.
cd /usr/src/sys/i386/conf
cp GENERIC mykernel
Открываем и меняем ident с GENERIC на mykernel

Добавляем в него необходимые опции :
# управление шириной канала
options DUMMYNET
# собственно фаэрвол
options IPFIREWALL
# правило по умолчанию allow ip from any to any
options IPFIREWALL_DEFAULT_TO_ACCEPT
# логирование пакетов, если в правиле написано `log`
options IPFIREWALL_VERBOSE
# ограничение повторяющихся логов
options IPFIREWALL_VERBOSE_LIMIT=50
# перенаправление пакетов. в freebsd 10 этой опции уже нет, она в модуле ipfw.ko
options IPFIREWALL_FORWARD
# ядерный nat и его библиотека
options IPFIREWALL_NAT
options LIBALIAS
4) Теперь собираем ядро, процесс тоже длительный, может занимать до 40 минут, в зависимости от мощности вашего компьютера.
cd /usr/src
make -j4 -s buildkernel KERNCONF=mykernel
5) И устанавливаем его
make installkernel KERNCONF=mykernel
Перезагружаемся
shutdown -r now
и смотрим, что новое ядро загрузилось нормально для проверки даем команду  
uname -a
6) Ставим мир. При этом рекомендуется загрузиться в однопользовательском режиме,  boot -s в приглашении загрузчика. Или пункт 4 на начальном экране загрузки. Затем смонтировать все файловые системы.
# mount -u /
# mount -a
Если zfs, то так
# zfs mount -u /
# zfs mount -a
# zfs set readonly=off zpool/ROOT/default
Следующим шагом является выполнение первоначального обновления файлов конфигурации в /etc для подготовки к новому миру.
# mergemaster -p
Дальше переходим в каталог с исходниками и устанавливаем.
# cd /usr/src
# make installdworld
7) Обновляем остальные файлы конфигурации.
# mergemaster -iF
Удаляем устаревшие файлы. Это важно, так как в противном случае они могут вызвать проблемы.
# make delete-old
Теперь нужна полная перезагрузка системы для того, чтобы загрузить новое ядро и мир с использованием новых конфигурационных файлов.
# reboot
DHCP-server
Далее ставим dhcp-сервер из портов.
cd /usr/ports/net/isc-dhcp42-server
make config install clean
Отредактируем его файл настроек /usr/local/etc/dhcpd.conf
У меня выглядит так
# имя подключения сообщаемое клиентам
option domain-name "example.ru";
# передаем клиентам dns провайдера (есть смысл поставить у себя кэширующий dns)
option domain-name-servers 195.64.192.35, 195.64.222.2;
# Время аренды IP–адреса (в секундах)
max-lease-time 7200;
# разрешаем dhcp самостоятельно перезапускать процедуру получения IP-адресов
authoritative;
# логирование
log-facility local7;
# настраиваем подсеть

subnet 192.168.10.0 netmask 255.255.255.0{
range 192.168.10.10 192.168.10.20;
option routers 192.168.10.1;
}
# для одного компа назначаем фиксированный ip
host comp1{
hardware ethernet 50:E5:49:ED:D3:5B;
fixed-address 192.168.10.5;
}
Если нужно настроить dhcpd для двух и более сетевух, то делаем отдельно для каждой подсети:
subnet 192.168.10.0 netmask 255.255.255.0 {
range 192.168.10.10 192.168.10.20;
option routers 192.168.10.1;
}
subnet 192.168.15.0 netmask 255.255.255.0 {
range 192.168.15.10 192.168.15.20;
option routers 192.168.15.1;
} 
И в /etc/rc.conf  прописываем прослушиваемые интерфейсы следующим образом:
dhcpd_ifaces="rl0 vr0"
Еще настраиваем логирование dhcp в отдельный лог /var/log/dhcpd.log в конец /etc/syslog.conf добавляем строчки
!dhcpd *.*
/var/log/dhcpd.log
Cоздаем /var/log/dhcpd.log
touch /var/log/dhcpd.log
Настроим ротацию
vi /etc/newsyslog.conf
/var/log/dhcpd.log 644 7 * @T00 JC
Перезапускаем syslog
/etc/rc.d/syslogd restart
Теперь добавляем следующие строчки в /etc/rc.conf
где rl1 - внешний интерфейс, подключен к интернет, rl0 - внутрений, к локальной сети.
gateway_enable="YES"
firewall_enable="YES"
firewall_nat_enable="YES"
firewall_type="/etc/fw"
firewall_nat_interface="rl1"
dhcpd_enable="YES"
dhcpd_ifaces="rl0"
Для перезапуска dhcpd даем команду
#  service isc-dhcpd restart
IPFW
Составляем правила для фаервола ipfwкоторые сохраним в файле /etc/fw. Здесь есть один важный нюанс, который нужно учитывать. Не все пакеты пришедшие на внешний интерфейс предназначены для локальной сети и поэтому они не должны направляться в NAT. Исходя из этого в начале списка следует располагать запрещающие правила, чтобы убрать вредный трафик, затем разрешающие правила для различных сервисов, таких как ssh, ftp, http и др., которые должны быть доступны извне, а уже потом направлять трафик в NAT. После трансляции адресов должны идти разрешающие правила для локальной сети, потом правила разрешающие исходящие соединения и в конце запрет всего. Согласно этим рекомендациям получим следующий список:
# запрещаем фрагментированные пакеты
add 1010 deny all from any to any frag
# разрешаем пинги (эхо-запрос, эхо-ответ, время жизни пакета истекло)
add 1020 allow icmp from any to any icmptypes 0,8,11
# разрешаем авторизатиру слушать порт
add 1030 allow tcp from bill.convex.ru to me 6326 in via rl1 setup
# разрешаем соединение по ssh
add 1040 allow tcp from any to any 22
# разрешаем фтп-серверу принимать соедиения по 20, 21 и в диапазоне 40000-50000
add 1050 allow tcp from any to me 20,21, 40000-50000 in via rl1
# web-сервер слушает 80 порт
add 1060 allow tcp from any to any 80 in via rl1
# направляем оставшийся трафик, проходящий через внешний интерфейс в NAT и делаем редирект портов для активного режима DC++
nat 1 config log if rl1 reset same_ports deny_in redirect_port tcp 192.168.10.5:35889 35889 redirect_port udp 192.168.10.5:35889 35889
add 1070 nat 1 ip from any to any via rl1
# разрешаем все соединения для локального интерфейса
add 1080 allow all from any to any via rl0
# разрешаем весь исходящий трафик через внешний интерфейс
add 1090 allow ip from any to any out xmit rl1
# запрещаем все и ведем лог /var/log/security
add 65534 deny log all from any to any
Теперь у нас работает ядерный nat, а ip раздаются по dhcp. 
Чтобы перезагрузить правила без перезагрузки сервера выполняем команду: 
/etc/rc.d/ipfw restart


Что бы разрешить пинг из внутренней сети нужно убрать правило 1020 и добавить второе правило для nat.
nat 2 config if rl1 reset same_ports
add 1075 nat 2 icmp from any to any icmptypes 0,8 via rl1
В правилах могут использоваться различные атрибуты, такие как in и out - движение пакета относительно к операционной системе,  recv, xmit, via - а это действие интерфейса.
recv - интерфейс получил пакет; 
xmit - интерфейс передал пакет; 
via - прошел через интерфейс неважно в какую сторону.
Возможны следующие комбинации:
out recv re0 - исходящий пакет, полученный re0 интерфейсом
out xmit re0 - исходящий пакет, отправленный re0 интерфейсом
in recv re0 - входящий пакет, полученный с интерфейса re0
in xmit rl0 - входящий пакет, отправленный rl0 интерфейсом - неверное, ipfw выдаст ошибку.
Следующая схема это наглядно поясняет

Принцип действия достаточно простой. Пакет полученный от драйвера сетевой карты rl0 помечается, как "in recv rl0" и "in via rl0", далее обработчик проверяет пакет и если он транзитный, то меняет атрибут "in" на "out", но "recv rl0" и "via rl0" все равно сохраняются. А еще добавляются "via rl1" и "xmit rl1", через которые он выходит.

Отсюда следует, что все входящие не транзитные пакеты не имеют атрибута "out", а исходящие не имеют "in". Пользуйтесь этим при написании гибких правил для ipfw.

Для наблюдения за сетевыми подключениями в реальном времени удобно использовать:
systat -netstat n

Комментариев нет:

Отправка комментария