среда, февраля 13, 2008

pf, freebsd 6.2 и 3 внешних канала.

В данной заметке привожу рабочий конфигурационный файл маршрутизатора с тремя внешними каналами, трансляцией адресов, переадресацией сервисов во внутреннюю сеть. Работает симметричная маршрутизация для правильного возврата пакетов в канал, с которого соединение было инициировано. Внутри локальной сети терминальный сервер 192.168.1.3. На самом сервере доступно из вне ftp ssh и почта. На втором и третьем внешнем интерфейсе на 5000 порту работает сервер vtun. У локальной сети 192.168.1.0/24. У туннельных сетей 192.168.3.0/24 и 192.168.4.0/24. Из локалки наружу разрешено 110, 3389, 53, остальное через прокси сервер squid.

#/etc/pf.conf
# Внешний интерфейс и шлюз для канала A (WAN_1).#
ext_if_a = "em0"
ext_gw_a = "x.x.x.x"
# Внешний интерфейс и шлюз для канала B (WAN_2).#
ext_if_b = "em2"
ext_gw_b = "x.x.x.x"
# Внешний интерфейс и шлюз для канала C (WAN_3).#
ext_if_c = "fxp0"
ext_gw_c = "x.x.x.x"
# Внутренний интерфейс (LAN).#
int_if = "em1"
# Обслуживаемые TCP и UDP сервисы.
tcp_svc = "22 25 21 35000:35100"
# TCP сервисы, обслуживаемые сервером из внутренней сети, и адрес сервера.
# 3389
tcp_svc_lan_terminal = "3389"
tcp_rdr_lan_terminal = "192.168.1.3"
# Не фильтровать трафик на lo интерфейсах.#
set skip on lo0
# Нормализовать весь входящий трафик.#
scrub in
# Включить трансляцию адресов на внешних интерфейсах.#
nat on $ext_if_a inet from !(self) -> $ext_if_a:0
nat on $ext_if_b inet from !(self) -> $ext_if_b:0
nat on $ext_if_c inet from !(self) -> $ext_if_c:0
# Переадресовать TCP сессии для сервисов, обслуживаемых локальным сервером.
# Правила rdr здесь НЕ должны содержать слова pass.
rdr on $ext_if_a inet proto tcp to $ext_if_a:0 port { $tcp_svc_lan_terminal\
} tag EXT_IF_A -> $tcp_rdr_lan_terminal
rdr on $ext_if_b inet proto tcp to $ext_if_b:0 port { $tcp_svc_lan_terminal\
} tag EXT_IF_B -> $tcp_rdr_lan_terminal
rdr on $ext_if_c inet proto tcp to $ext_if_c:0 port { $tcp_svc_lan_terminal\
} tag EXT_IF_C -> $tcp_rdr_lan_terminal
# Разрешить подключение к переадресованным сервисам из локальной сети по
# внешним адресам.
rdr pass on $int_if inet proto tcp to { $ext_if_a:0 $ext_if_b:0 $ext_if_c:0\
} port { $tcp_svc_lan_terminal } tag INT_IF_RDR -> $tcp_rdr_lan_terminal
nat on $int_if tagged INT_IF_RDR -> $int_if:0
# По умолчанию блокировать весь трафик на всех интерфейсах. Для входящих TCP
# соединений возвращать RST.
block on { $ext_if_a $ext_if_b $ext_if_c $int_if}
block return on { $ext_if_a $ext_if_b $ext_if_c $int_if} inet proto tcp
#Разрешить доступ через лок интерфейс к терминал серверу
pass out quick on $int_if inet proto tcp from any to $tcp_rdr_lan_terminal\
port $tcp_svc_lan_terminal keep state
# Пропускаем входящие пакеты для переадресованых сервисов. Устанавливаем
# для них симметричную маршрутизацию (если пакет пришел
# из канала A, ответ пойдет через канал A независимо от default route).
pass in quick reply-to ($ext_if_a $ext_gw_a) tagged EXT_IF_A keep state
pass in quick reply-to ($ext_if_b $ext_gw_b) tagged EXT_IF_B keep state
pass in quick reply-to ($ext_if_c $ext_gw_c) tagged EXT_IF_C keep state
# Выпускать исходящие пакеты. Установить маршрутизацию в зависимости от
# адреса источника. Пакеты с адресом интерфейса A уходят в канал A,
# с адресом интерфейса B - в канал B.
pass out route-to ( $ext_if_a $ext_gw_a ) inet from $ext_if_a:0 keep state
pass out route-to ( $ext_if_b $ext_gw_b ) inet from $ext_if_b:0 keep state
pass out route-to ( $ext_if_c $ext_gw_c ) inet from $ext_if_c:0 keep state
# Разрешить входящие ICMP PING пакеты.#
pass in on $ext_if_a reply-to ($ext_if_a $ext_gw_a) inet proto icmp\
to $ext_if_a:0 icmp-type echoreq code 0 keep state
pass in on $ext_if_b reply-to ($ext_if_b $ext_gw_b) inet proto icmp\
to $ext_if_b:0 icmp-type echoreq code 0 keep state
pass in on $ext_if_c reply-to ($ext_if_c $ext_gw_c) inet proto icmp\
to $ext_if_c:0 icmp-type echoreq code 0 keep state

# Разрешить входящие TCP сессии для обслуживаемых сервисов.#
pass in on $ext_if_a reply-to ($ext_if_a $ext_gw_a) inet proto tcp\
to $ext_if_a:0 port { $tcp_svc } flags S/SA keep state
pass in on $ext_if_b reply-to ($ext_if_b $ext_gw_b) inet proto tcp\
to $ext_if_b:0 port { $tcp_svc } flags S/SA keep state
pass in on $ext_if_c reply-to ($ext_if_c $ext_gw_c) inet proto tcp\
to $ext_if_c:0 port { $tcp_svc} flags S/SA keep state
#Разрешим туннели на интерфейсе WAN_2 и WAN3
pass in on $ext_if_b reply-to ($ext_if_b $ext_gw_b) inet proto tcp\
from x.x.x.x to $ext_if_b:0 port 5000 flags S/SA keep state
pass in on $ext_if_c reply-to ($ext_if_c $ext_gw_c) inet proto tcp\
from x.x.x.x to $ext_if_c:0 port 5000 flags S/SA keep state
pass quick on tun0 all
pass quick on tun1 all
#Разрешить в локалку пакеты от внутреннего интерфейса, из локальных сетей
# туннелей
pass out on $int_if inet from { $int_if:0, 192.168.3.0/24,\
192.168.4.0/24 } to $int_if:network:0 keep state
#Разрешить из локалки пакеты на внутренний интерфейс, в локальные сети
# туннелей
pass in on $int_if inet from $int_if:network:0 to { $int_if:0,\
192.168.3.0/24, 192.168.4.0/24 } keep state
#Разрешить из локалки пинг
pass in on $int_if inet proto icmp from $int_if:network:0 to any\
icmp-type echoreq code 0 keep state
#Разрешить из локалки 110, 3389, 53
pass in on $int_if inet proto tcp from $int_if:network:0 to any port\
{ 110, 3389 } flags S/SA keep state
pass in on $int_if inet proto udp from $int_if:network:0 to any port\
53 keep state


P.S. И теперь снаружи со всех трех каналов видно терминальный сервер, фтп и почтовый сервера.
Кто-то скажет что фаервол это не маршрутизатор и я согласен. Но на циску денег не дают. Поэтому чем не выход.



Обсудить сообщение можно в форуме admins.arg.su

36 комментариев:

sallevan комментирует...

А я вот не считаю, что Firewall — это не маршрутизатор. Даже термин есть такой — FBR, то есть, Firewall-based Routing.
Так что идея сама делать маршрутизацию pf'ом очень и очень даже здрава.

Анонимный комментирует...

т.е балансировки нагрузки нет, просто Policy Routing ???

Анонимный комментирует...

да интересная конструкция только не ясно какй шлюз по умолчанию

Айзятуллен Рамиль комментирует...

Балансировка в моем случае не нужна.
На счет шлюза по умолчанию. А какая разница то?

Анонимный комментирует...

А если потребуется мониторить оба соединения, делать ping со сменой defaultrouter?

Айзятуллен Рамиль комментирует...

В моем случае шлюз по умолчанию канал А,
прописаны статические маршруты для туннелей в каналы В и С. Также прописаны статические маршруты для бесплатных сетей провайдеров В и С.
На счет мониторить соединения - мониторьте. Не вижу для этого препятствий.
:)

Анонимный комментирует...

набираем пинг, он идет в канал А.
Правильно?
Чтобы проверить канал В надо же соответственно сменить шлюз.
Правильно?

Айзятуллен Рамиль комментирует...

У каждого канала есть свой шлюз, вот и пингуйте шлюз.

Анонимный комментирует...

т.е. при этих настройках
ping ext_gw_a
пойдет через ext_if_a

а, ping ext_gw_b
пойдет через ext_if_b?

и соответственно при
ping www.ru пойдёт через ext_if_a, если он является по умолчанию?

Айзятуллен Рамиль комментирует...

да

Skif комментирует...

nat on $ext_if_a inet from !(self) -> $ext_if_a:0
а объясните плиз что сие значит?
нет я понимаю что нат но конкретно "!(self)" и "$ext_if_a:0"? что за обозначение такое ":0"?

Айзятуллен Рамиль комментирует...

"!(self)" натим все что не от себя (в смысле не от сервака)
"$ext_if_a:0" ":0" это значит не учитывать алиасы интерфейса

Анонимный комментирует...

Когда-то очень давно я делал подобное на IPFW, только без проброса портов вовнутрь.

На pf выглядит как-то привлекателельнее, на мой первый взгляд.

Айзятуллен Рамиль комментирует...

А как вы сделали на ipfw возврат пакетов обратно в канал если шлюз по умолчанию смотрит в другой канал.

Анонимный комментирует...

А как вы сделали на ipfw возврат пакетов обратно в канал если шлюз по умолчанию смотрит в другой канал.

ipfw fwd на нужный IP шлюза.

Анонимный комментирует...

"Остальное через прокси сервер squid."

А как трафик на СКВИД заворачивал?

Айзятуллен Рамиль комментирует...

Никак не заворачивал, в клиентах прописан прокси :)

Анонимный комментирует...

Народ, подскажите пожалуйста, как правильно сделать нат, если есть так же три интерфейса и проблема в том что на двух интерфейстов куча алиасов. Вот так например
xl1: flags=8843 UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST mtu 1500
options=1 RXCSUM,VLAN_MTU
inet 115.46.11.115 netmask 0xfffffff0 broadcast 115.46.11.207
inet 115.46.11.114 netmask 0xffffffff broadcast 115.46.11.114
inet 115.46.11.116 netmask 0xffffffff broadcast 115.46.11.116
inet 115.46.11.117 netmask 0xffffffff broadcast 115.46.11.117

весь трафик по умолчанию идет через 115.46.11.114, вопрос в том, как в pf сделать чтоб тот кто через нам работает ходил скажем с 115.46.11.116 ip?
Заранее спасибо!

Айзятуллен Рамиль комментирует...

Думаю так
nat on $ext_if_a inet from !(self) -> 115.46.11.116
А вообще вопросу прошу в форум http://admins.arg.su

Анонимный комментирует...

Камрад, а как же обстоит дело с "дефолтным маршрутом"? Или default gateway вообще не учавствует в сетевом обмене?

Анонимный комментирует...

И вот еще какой вопрос назрел. Что произойдет в случае отказа одного, двух каналов? Как будет вести себя маршрутизатор?

Айзятуллен Рамиль комментирует...

Что касается маршрута по умолчанию, у меня он в канал А. И прописаны статические маршруты к сетям в два других канала. При отказе к примеру канала А сервисы доступные снаружи останутся доступны по каналам В и С, естественно изнутри все что в дефолтовый канал А работать не будет. Нужно переключать шлюз по умолчанию в другой канал.

Анонимный комментирует...

Возможно ли сделать чтобы любой пакет на любой порт с какого интерфейса пришел на тот и должен уйти?

Айзятуллен Рамиль комментирует...

Да так и есть для # Обслуживаемые TCP и UDP сервисы. tcp_svc = "22 25 21 35000:35100"
Уберите в правилах ниже tcp_svc

# Разрешить входящие TCP сессии для обслуживаемых сервисов.#
pass in on $ext_if_a reply-to ($ext_if_a $ext_gw_a) inet proto tcp\
to $ext_if_a:0 port { $tcp_svc } flags S/SA keep state
pass in on $ext_if_b reply-to ($ext_if_b $ext_gw_b) inet proto tcp\
to $ext_if_b:0 port { $tcp_svc } flags S/SA keep state
pass in on $ext_if_c reply-to ($ext_if_c $ext_gw_c) inet proto tcp\
to $ext_if_c:0 port { $tcp_svc} flags S/SA keep state

Анонимный комментирует...

Вы имеете ввиду
в tcp_svc = "22 25 21 35000:35100"
увеличть диапазон
tcp_svc = "1:65000", а потом написать то-же для например для udp, icmp?

Айзятуллен Рамиль комментирует...

нет вот так
pass in on $ext_if_a reply-to ($ext_if_a $ext_gw_a) inet proto tcp\
to $ext_if_a:0 flags S/SA keep state
pass in on $ext_if_b reply-to ($ext_if_b $ext_gw_b) inet proto tcp\
to $ext_if_b:0 flags S/SA keep state
pass in on $ext_if_c reply-to ($ext_if_c $ext_gw_c) inet proto tcp\
to $ext_if_c:0 flags S/SA keep state

Анонимный комментирует...

При таких правилах вроде и работает (почта и радмин ходят), а вот с пингами и трассировкой никак.
У меня сейчас используется
pass out quick route-to ($1_if $1_gw) from $1_ip to any keep state
pass out quick route-to ($2_if $2_gw) from $2_ip to any keep state

При этих правилах трассировка с внешнего хоста до второго канала доходит нормально, и пинги до второго идут через первый (дефолт. рут)

Есть варианты?

Айзятуллен Рамиль комментирует...

Все возможно, давайте уже в форуме пообщаемся http://admins.arg.su
Постарайтесь четко сформулировать вопрос
Вот эти правила, цитата
"У меня сейчас используется
pass out quick route-to ($1_if $1_gw) from $1_ip to any keep state
pass out quick route-to ($2_if $2_gw) from $2_ip to any keep state"
для исходящих соединений от вашего сервера

Анонимный комментирует...

А если требуется сместить сервис на внешнем интерфейсе, на другой порт?
В ipfw делается довольно просто
redirect_port tcp 192.168.0.1:4899 4444
А в pf?

Айзятуллен Рамиль комментирует...

И зачем это может понадобиться?
Вопросы в форум admins.arg.su

Анонимный комментирует...

man ping эбаут (-S)
ping -S srcip dstip

FreePascal комментирует...

В линухе пинг умеет пинговать через указанный интерфейс.
Во фряхе можно установить hping.

Анонимный комментирует...

Балансировка нагрузки исходящего трафика (http://www.opennet.ru/base/sec/pf_extended.txt.html#top5)

Пулы адресов могут использоваться для балансировки нагрузки между двумя и более внешними каналами с использованием опции route-to в случае невозможности организовать динамическую маршрутизацию (например, с использованием протокола BGP4). Совместное использование route-to и пула адресов round-robin исходящие соединения могут быть распределены между разными провайдерами.

В качестве дополнительной информации необходимо указать адреса маршрутизаторов для каждого Интернет-соединения. Это необходимо для опции route-to, дабы управлять исходящими пакетами.

Этот пример покажет нам балансировку нагрузки между двумя каналами:

lan_net = "192.168.0.0/24"
int_if = "dc0"
ext_if1 = "fxp0"
ext_if2 = "fxp1"
ext_gw1 = "68.146.224.1"
ext_gw2 = "142.59.76.1"

pass in on $int_if route-to \
{ ($ext_if1 $ext_gw1), ($ext_if2 $ext_gw2) } round-robin \
from $lan_net to any keep state

Опция route-to используется для приема трафика на внутреннем интерфейсе и назначения ему внешнего сетевого интерфейса и шлюза, таким образом обеспечивая балансировку. Обратите внимание, что опция route-to должна быть указана в каждом правиле, предназначенном для балансировки трафика. Ответные пакеты приходят на тот интерфейс, с которого ушел запрос и они будут перенаправлены во внутрь как обычно.

Для гарантии того, что пакеты с $ext_if1 всегда направляются к $ext_gw1 (и соответственно для $ext_if2 к $ext_gw2), в правилах можно указать следующее:

pass out on $ext_if1 route-to ($ext_if2 $ext_gw2) from $ext_if2 \
to any
pass out on $ext_if2 route-to ($ext_if1 $ext_gw1) from $ext_if1 \
to any

В заключение хочу сказать, что NAT можно использовать на каждом из внешних интерфейсов:

nat on $ext_if1 from $lan_net to any -> ($ext_if1)
nat on $ext_if2 from $lan_net to any -> ($ext_if2)

Полный пример балансировки исходящего трафика будет выглядеть так:

lan_net = "192.168.0.0/24"
int_if = "dc0"
ext_if1 = "fxp0"
ext_if2 = "fxp1"
ext_gw1 = "68.146.224.1"
ext_gw2 = "142.59.76.1"

# nat outgoing connections on each internet interface
nat on $ext_if1 from $lan_net to any -> ($ext_if1)
nat on $ext_if2 from $lan_net to any -> ($ext_if2)

# default deny
block in from any to any
block out from any to any

# pass all outgoing packets on internal interface
pass out on $int_if from any to $lan_net
# pass in quick any packets destined for the gateway itself
pass in quick on $int_if from $lan_net to $int_if
# load balance outgoing tcp traffic from internal network.
pass in on $int_if route-to \
{ ($ext_if1 $ext_gw1), ($ext_if2 $ext_gw2) } round-robin \
proto tcp from $lan_net to any flags S/SA modulate state
# load balance outgoing udp and icmp traffic from internal network
pass in on $int_if route-to \
{ ($ext_if1 $ext_gw1), ($ext_if2 $ext_gw2) } round-robin \
proto { udp, icmp } from $lan_net to any keep state

# general "pass out" rules for external interfaces
pass out on $ext_if1 proto tcp from any to any flags S/SA modulate state
pass out on $ext_if1 proto { udp, icmp } from any to any keep state
pass out on $ext_if2 proto tcp from any to any flags S/SA modulate state
pass out on $ext_if2 proto { udp, icmp } from any to any keep state

# route packets from any IPs on $ext_if1 to $ext_gw1 and the same for
# $ext_if2 and $ext_gw2
pass out on $ext_if1 route-to ($ext_if2 $ext_gw2) from $ext_if2 to any
pass out on $ext_if2 route-to ($ext_if1 $ext_gw1) from $ext_if1 to any

Анонимный комментирует...

to Рамиль
А как у тебя rc.conf настроен? Ведь на внешних интерфейсах разные сетевые настройки (шлюз, DNS).

Анонимный комментирует...

Рамиль, а прикрутите еще один интерфейс для DMZ (10.110.1.254)
а там пусть крутится
1) отдельно майл 25, 110 (10.110.1.252)
2) веб 80 для всех, 443 только для лан (10.110.1.253)

ато запутался с tcp_svc_lan_

Рамиль комментирует...

>А как у тебя rc.conf настроен? Ведь на >внешних интерфейсах разные сетевые >настройки (шлюз, DNS).
в rc.conf прописан шлюз в канал по-умолчанию (в основной), а DNS не там настраивается :)
>Рамиль, а прикрутите еще один интерфейс >для DMZ (10.110.1.254)
пишите в форум admins.arg.su вместе и настроим :)