*NIX. Построение шлюза с трансляцией адресов на двух интерфейсах во FreeBSD
Построение шлюза с трансляцией адресов на двух интерфейсах во FreeBSD
Содержание
1. Постановка задачи.
2. Конфигурация ipfw.
3. Созданиемодификация rc скриптов.
4. Правка rc.conf.
5. Проверка связи, проверка функционирования скриптов.
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"
### Внутренний интерфейс, смотрит в "маленькую" локалку (elantech_if)
elantech_if="rl0"
elantech_net="10.0.0.0/24"
elantech_mask="255.255.255.0"
elantech_ip="10.0.0.1"
### Internet интерфейс (VPN туннель)
inet_if="ng0"
inet_ip="62.231.11.104"
### VPN-сервер
vpn_ip="192.168.2.1"
vpn_port="1723"
###----------------------------------------------------------------------------
### Сбрасываем старые правила
${fwcmd} -f flush
### Таблица доступа в интернет/к большой локалке для машин из "маленькой" сети
${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. Вот
результат (изменения выделены красным):
#!/bin/sh
#
# $FreeBSD: src/etc/rc.d/natd,v 1.1.2.1 2004/10/10 09:50:53 mtm Exp $
#
# PROVIDE: natd2
# KEYWORD: nostart nojail
. /etc/rc.subr
. /etc/network.subr
name="natd2"
rcvar=`set_rcvar`
command="/sbin/natd"
start_cmd="natd2_start"
pidfile="/var/run/${name}.pid"
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 тоже следует
немного модифицировать:
#!/bin/sh
#
# $FreeBSD: src/etc/rc.d/natd,v 1.1.2.1 2004/10/10 09:50:53 mtm Exp $
#
# PROVIDE: natd
# KEYWORD: nostart nojail
. /etc/rc.subr
. /etc/network.subr
name="natd"
rcvar=`set_rcvar`
command="/sbin/${name}"
start_cmd="natd_start"
pidfile="/var/run/${name}.pid"
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:
#!/bin/sh
#
# $FreeBSD: src/etc/rc.d/ipfw,v 1.8.2.1 2004/10/10 09:50:53 mtm Exp $
#
# PROVIDE: ipfw
# REQUIRE: ppp-user
# BEFORE: NETWORKING
# KEYWORD: nojail
. /etc/rc.subr
. /etc/network.subr
name="ipfw"
rcvar="firewall_enable"
start_cmd="ipfw_start"
start_precmd="ipfw_precmd"
stop_cmd="ipfw_stop"
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"
### Статические маршруты
route_fnet="192.168/16 192.168.94.1"
static_routes="fnet"
### DNS
named_enable="YES"
### PPTP клиент
mpd_enable="YES"
5. Проверка связи, проверка функционирования скриптов.
На практике убеждаемся, что ipfw, natd, natd2 и mpd корректно
останавливаются, запускаются, перезапускаются, VPN туннель создается,
доступ в интернет есть там, где он нужен.
oppa# /etc/rc.d/ipfw stop
net.inet.ip.fw.enable: 1 -> 0
Stopping natd.
Waiting for PIDS: 285, 285, 285, 285, 285.
Stopping natd2.
Waiting for PIDS: 295, 295, 295, 295, 295.
oppa# /etc/rc.d/ipfw start
Firewall rules loaded, starting divert daemons: natd natd2.
net.inet.ip.fw.enable: 0 -> 1
oppa# ifconfig
sk0: flags=8843 mtu 1500
inet 192.168.94.26 netmask 0xffffff00 broadcast 192.168.94.255
inet6 fe80::211:d8ff:fe98:9d1a%sk0 prefixlen 64 scopeid 0x2
ether 00:11:d8:98:9d:1a
media: Ethernet autoselect (100baseTX )
status: active
rl0: flags=8843 mtu 1500
options=8
inet 10.0.0.1 netmask 0xffffff00 broadcast 10.0.0.255
inet6 fe80::280:48ff:fe33:9d77%rl0 prefixlen 64 scopeid 0x3
ether 00:80:48:33:9d:77
media: Ethernet autoselect (100baseTX )
status: active
plip0: flags=108810 mtu 1500
lo0: flags=8049 mtu 16384
inet 127.0.0.1 netmask 0xff000000
inet6 ::1 prefixlen 128
inet6 fe80::1%lo0 prefixlen 64 scopeid 0x6
ng0: flags=88d1 mtu 1500
inet 62.231.11.104 --> 172.16.0.1 netmask 0xffffffff
inet6 fe80::211:d8ff:fe98:9d1a%ng0 prefixlen 64 scopeid 0x7
oppa#
Все работает!