From: Taras Savchuk <taras@elantech.ru.>
Newsgroups: email
Date: Mon, 03 Oct 2005 14:31:37 +0000 (UTC)
Subject: Построение шлюза с трансляцией адресов на двух интерфейсах во FreeBSD
ELANTECH - ИТ аутсорсинг, комплексная системная интеграция,
абонентское обслуживание, поддержка UNIX/Linux/FreeBSD серверов, WEB
дизайн, WEB разработка, разработка ПО, автоматизация.
1. Постановка задачи.
Задача формулируется просто: необходимо транслировать сетевые адреса
на двух разных интерфейсах, один из которых виртуальный. Цели две:
* Обеспечить для локальной сети доступ к ресурсам сети провайдера.
* Предоставить для пользователей локальной сети доступ к ресурсам
Интернет.
В нашем распоряжении машина с FreeBSD 5.4-STABLE, на которой
установлены следующие интерфейсы:
* sk0: 192.168.94.26/24 - смотрит в сеть провайдера (192.168.xxx.0/24).
* rl0: 10.0.0.1/24 - смотрит во внутреннюю сеть (10.0.0.0/24).
* ng0: 62.231.11.104 - туннель к VPN-серверу 192.168.2.1
(VPN-сервер находится в сети провайдера, туннель создается демоном mpd).
Ниже изображена схема сети:
2. Конфигурация ipfw.
Приведенная ниже конфигурация ipfw минимальна, так что следует при
необходимости добавить дополнительные ограничения. Основная задача
данной конфигурации ipfw - позволить mpd создать туннель к VPN-серверу
и распределить пакеты для Интернет и пакеты для сети провайдера по
разным divert-сокетам (8669 и 8668 соответственно), при этом не
позволяя ничего лишнего (особенно соединений "снаружи").
Предполагается, что комментариев к тексту будет достаточно для
понимания конфигурации.
Итак, /etc/elantech.firewall:
#!/bin/sh
### Полезные переменные ;)
fwcmd="/sbin/ipfw -q"
skip="skipto 30000"
### Внешний интерфейс, смотрит в сеть провайдера (ext_if)
ext_if="sk0"
ext_net="192.168.0.0/16"
ext_ip="192.168.94.26"
### Таблица доступа в интернет/к большой локалке для машин из "маленькой" сети
${fwcmd} table 13 flush
${fwcmd} table 13 add 10.0.0.13
${fwcmd} table 13 add 10.0.0.10
${fwcmd} table 13 add 10.0.0.1
### Только локальный траффик через loopback
${fwcmd} add 100 pass all from any to any via lo0
${fwcmd} add 200 deny all from any to 127.0.0.0/8
${fwcmd} add 300 deny ip from 127.0.0.0/8 to any
### Разрешаем GRE пакеты до и от VPN сервера, чтобы mpd мог поднять туннель
${fwcmd} add 501 allow gre from ${ext_ip} to ${vpn_ip} via ${ext_if}
${fwcmd} add 502 allow gre from ${vpn_ip} to ${ext_ip} via ${ext_if}
### Разрешаем PPTP к VPN серверу, опять же для создания туннеля
${fwcmd} add 503 allow tcp from ${ext_ip} to ${vpn_ip} dst-port ${vpn-port} via ${ext_if} setup keep-state
### NAT-им входящие из сети провайдера пакеты
${fwcmd} add 10000 divert natd all from ${ext_net} to any in via ${ext_if}
### NAT-им входящие пакеты из Internet
${fwcmd} add 10001 divert 8669 all from any to any in via ${inet_if}
###----------------------------------------------------------------------------
---------
### Требуем от ipfw обработки динамических правил
${fwcmd} add 11000 check-state
### Доступ из локалки в сеть провайдера
${fwcmd} add 12100 ${skip} tcp from table(13) to ${ext_net} out via ${ext_if} setup keep-state
${fwcmd} add 12105 ${skip} udp from table(13) to ${ext_net} out via ${ext_if} keep-state
${fwcmd} add 12110 ${skip} icmp from table(13) to ${ext_net} out via ${ext_if} keep-state
### Доступ из локалки в Internet
${fwcmd} add 12115 ${skip} tcp from table(13) to not ${ext_net} out via ${inet_if} setup keep-state
${fwcmd} add 12120 ${skip} udp from table(13) to not ${ext_net} out via ${inet_if} keep-state
${fwcmd} add 12125 ${skip} icmp from table(13) to not ${ext_net} out via ${inet_if} keep-state
### Доступ в Internet для себя
${fwcmd} add 12115 ${skip} tcp from ${inet_ip} to not ${ext_net} out via ng0 setup keep-state
${fwcmd} add 12120 ${skip} udp from ${inet_ip} to not ${ext_net} out via ng0 keep-state
${fwcmd} add 12125 ${skip} icmp from ${inet_ip} to not ${ext_net} out via ng0 keep-state
### Доступ в сеть провайдера для себя
${fwcmd} add 12130 ${skip} tcp from ${ext_ip} to ${ext_net} out via ${ext_if} setup keep-state
${fwcmd} add 12135 ${skip} udp from ${ext_ip} to ${ext_net} out via ${ext_if} keep-state
${fwcmd} add 12140 ${skip} icmp from ${ext_ip} to ${ext_net} out via ${ext_if}keep-state
### Доступ из локалки к внутреннему интерфейсу
${fwcmd} add 12145 ${skip} tcp from table(13) to ${elantech_ip} in via ${elantech_if} setup keep-state
${fwcmd} add 12150 ${skip} udp from table(13) to ${elantech_ip} in via ${elantech_if} keep-state
${fwcmd} add 12155 ${skip} icmp from table(13) to ${elantech_ip} in via ${elantech_if} keep-state
### Ексклюзив для root-а
${fwcmd} add 12200 ${skip} all from me to any out via ${elantech_if} setup keep-state
### Режем остальные пакеты
${fwcmd} add 14600 deny all from any to any via ${ext_if}
${fwcmd} add 14601 deny all from any to any via ${inet_if}
${fwcmd} add 14602 deny all from any to me via ${elantech_if}
${fwcmd} add 14603 deny all from me to any via ${elantech_if}
### NAT-им пакеты в сеть провайдера
${fwcmd} add 50000 divert natd all from any to ${ext_net} out via ${ext_if}
### NAT-им пакеты в интернет
${fwcmd} add 51000 divert 8669 all from any to any out via ${inet_if}
### Сюда долетели честные пакеты - пропускаем!
${fwcmd} add 55000 allow ip from any to any
###----------------------------------------------------------------------------
3. Созданиемодификация rc скриптов.
Итак, настало время подумать о запуске второго экземпляра natd.
Запускать его будем аналогично основному нату, поэтому делаем...
#cp /etc/rc.d/natd /etc/rc.d/natd2
... читаем man rc, man rc.subr, HANDBOOK и правим /etc/rc.d/natd2. Вот
результат (изменения выделены красным):
natd2_start()
{
dhcp_list="`list_net_interfaces dhcp`"
for ifn in ${dhcp_list}; do
case ${natd2_interface} in
${ifn})
natd_flags="$natd2_flags -dynamic"
;;
*)
;;
esac
done
if [ -n "${natd2_interface}" ]; then
if echo ${natd2_interface} |
grep -q -E '^[0-9]+(.[0-9]+){0,3}$'; then
natd2_flags="$natd2_flags -a ${natd2_interface}"
else
natd2_flags="$natd2_flags -n ${natd2_interface}"
fi
fi
echo -n ' natd2'
${natd2_program:-/sbin/natd} ${natd2_flags} ${natd2_ifarg} -P ${pidfile}
}
load_rc_config $name
run_rc_command "$1"
Хорошо видно, что мы подставили цифру "2" ко многим переменным,
добавили переменную pidfile, чтобы система скриптов могла корректно
останавливать и запускать natd2, а также добавили опцию -P с именем
pidfile к строке запуска natd2. Файл /etc/rc.d/natd тоже следует
немного модифицировать:
natd_start()
{
dhcp_list="`list_net_interfaces dhcp`"
for ifn in ${dhcp_list}; do
case ${natd_interface} in
${ifn})
natd_flags="$natd_flags -dynamic"
;;
*)
;;
esac
done
if [ -n "${natd_interface}" ]; then
if echo ${natd_interface} |
grep -q -E '^[0-9]+(.[0-9]+){0,3}$'; then
natd_flags="$natd_flags -a ${natd_interface}"
else
natd_flags="$natd_flags -n ${natd_interface}"
fi
fi
echo -n ' natd'
${natd_program:-/sbin/natd} ${natd_flags} ${natd_ifarg} -P ${pidfile}
}
load_rc_config $name
run_rc_command "$1"
Чтобы при старте/перезапуске/остановке ipfw аналогичным действиям
подвергался не только natd, но и natd2, правим /etc/rc.d/ipfw:
ipfw_precmd()
{
if ! ${SYSCTL} net.inet.ip.fw.enable > /dev/null 2>&1; then
if ! kldload ipfw; then
warn unable to load firewall module.
return 1
fi
fi
return 0
}
ipfw_start()
{
# set the firewall rules script if none was specified
[ -z "${firewall_script}" ] && firewall_script=/etc/rc.firewall
if [ -r "${firewall_script}" ]; then
. "${firewall_script}"
echo -n 'Firewall rules loaded, starting divert daemons:'
if [ -f /etc/rc.d/natd ] ; then
/etc/rc.d/natd start
fi
if [ -f /etc/rc.d/natd2 ] ; then
/etc/rc.d/natd2 start
fi
elif [ "`ipfw l 65535`" = "65535 deny ip from any to any" ]; then
echo 'Warning: kernel has firewall functionality, but'
' firewall rules are not enabled.'
echo ' All ip services are disabled.'
fi
echo '.'
# Firewall logging
#
if checkyesno firewall_logging; then
echo 'Firewall logging enabled'
sysctl net.inet.ip.fw.verbose=1 >/dev/null
fi
# Enable the firewall
#
${SYSCTL_W} net.inet.ip.fw.enable=1
}
ipfw_stop()
{
# Disable the firewall
#
${SYSCTL_W} net.inet.ip.fw.enable=0
if [ -f /etc/rc.d/natd ] ; then
/etc/rc.d/natd stop
fi
if [ -f /etc/rc.d/natd2 ] ; then
/etc/rc.d/natd2 stop
fi
}
load_rc_config $name
run_rc_command "$1"
4. Правка rc.conf.
Теперь допишем в /etc/rc.conf нужные для функционирования нашей
системы параметры:
hostname="oppa.elantech.ru"
### Интерфейс, который смотрит в сеть провайдера
ifconfig_sk0="192.168.94.26/24"
### Интерфейс, который смотрит в нашу локалку
ifconfig_rl0="10.0.0.1/24"
### Маршрутизация и файрволл
gateway_enable="YES"
firewall_enable="YES"
### Наш файрволльный скрипт
firewall_script="/etc/firewall.elantech"
### natd
natd_enable="YES"
natd_interface="sk0"
natd_flags=""
### natd2 (для него указываем внешний IP-адрес, а не интерфейс,
### т.к. на момент запуска natd2 интерфейс ng0 еще не создан)
natd2_enable="YES"
natd2_interface="62.231.11.104"
natd2_flags="-p 8669"
5. Проверка связи, проверка функционирования скриптов.
На практике убеждаемся, что ipfw, natd, natd2 и mpd корректно
останавливаются, запускаются, перезапускаются, VPN туннель создается,
доступ в интернет есть там, где он нужен.