Балансировка 2-х и более каналов на FreeBSD с использованием PF + Squid

от автора

Доброе время суток, хаброжители!

В связи с тем, что хотя моя предыдущая попытка поделиться мыслями принесла мне инвайт, однако карма ушла в минус, поэтому сейчас попытаюсь реабилитироваться.

Итак, о задаче: есть два канала интернет, шлюз на FreeBSD

gate# uname -a FreeBSD gate 9.0-RELEASE FreeBSD 9.0-RELEASE #0: Thu Nov 1 06:48:52 OMST 2012 root@gate:/usr/obj/usr/src/sys/GATE amd64
Не то, чтобы необходимость, но желание создать гибкую систему с балансировкой трафика по каналам и желание получить премию от руководства.
Канал №1: безлимитка, скорость 7 Мб, реальный ip-адрес
Канал №2: безлимитка, скорость до 60Мб, реальный ip-адрес.
Со стороны провайдера были установлены шлюзы, через которые реализую DMZ на «ловушки» для хакеров, поэтому настройки PF и SQUID минимальны

Описание поднятия балансировки

Все манипуляции от пользователя root. (Подключаемся к хосту любым пользователем, затем su, пароль root).

1. Опции ядра для включения PF:
Если не считать трафик средствами PF, то второй пункт можно отключить

cd /sys/amd64/conf
cp GENERIC GATE
ee GATE

device pf
device pflog

options ALTQ
options ALTQ_CBQ
options ALTQ_RED
options ALTQ_RIO
options ALTQ_HFSC
options ALTQ_PRIQ
options ALTQ_NOPCC

2. сборка ядра
make kernel KERNCONF=GATE

3. В шлюз установлены 2 сетевых карты, одна интегрированная в материнскую плату:
re0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500 options=389b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,WOL_UCAST,WOL_MCAST,WOL_MAGIC> inet 192.168.1.2 netmask 0xffffff00 broadcast 192.168.1.255 re1: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500 options=389b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,WOL_UCAST,WOL_MCAST,WOL_MAGIC> inet 192.168.63.26 netmask 0xfffffff8 broadcast 192.168.63.31 nfe0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500 options=82008<VLAN_MTU,WOL_MAGIC,LINKSTATE> inet 10.1.1.30 netmask 0xffffff00 broadcast 10.1.1.255
re0 провайдер №1, re1 — провайдер №2. Внутренний интерфейс nfe0 (назначение ip адресов и масок сложилось исторически, я работаю с тем, что есть). И конечно же подняты vlan на внутреннем интерфейсе, раскидываю по ним систему ip видеонаблюдения и различные отделы компании.

4. Настройка rc.conf
ee /etc/rc.conf
pf_enable="YES"
pf_rules="/etc/pf.conf"
squid_enable="YES"
reboot

То есть при загрузке выполняются правила из файла /etc/pf.conf.

5. Листинг pf.conf.

cat pf.conf
#Задаем переменные, где int_if — внутренний интерфейс, ext_if — внешние.
int_if=«nfe0»
ext_if=«re1»
ext_if2=«re0»
int_net=«10.1.1.0/24»
freeBSD=«10.1.1.30»
icmp_types="{ echoreq, unreach}"
http=«80»
https=«443»
ssh=«22»

#описываем шлюзы для каждого из провайдеров.
gw1=«192.168.63.25»
gw2=«192.168.1.1»

#Для тех, кто должен идти с конкретного провайдера вводим принудительное направление траффика в обход балансировки.
#Например, клиент-банки или тендерные площадки чувствительны к смене ip-адреса при балансировке.
to_ertel="{10.1.1.42, 10.1.1.33, 10.1.1.4, 10.1.1.12, 10.1.1.5, 10.1.1.48, 10.1.1.25, 10.1.1.3, 10.1.1.243, 10.1.1.5 }"
to_trans="{ 10.1.1.27, 10.1.1.2, 10.1.1.181, 10.1.1.46, 10.1.1.39, 10.1.1.27, 10.1.1.31, 10.1.1.113 }"

#настройки политик файрвола — всех из вне посылать,
set block-policy drop
set skip on lo
#собирать все пакеты перед их отправкой.
scrub in all

#Нат на первый интернет-вход
nat on $ext_if inet from any to any -> ($ext_if)
#Нат на второй интернет-вход
nat on $ext_if2 inet from any to any -> ($ext_if2)
#Проброс на SQUID
rdr on $int_if inet proto tcp from any to any port www -> 127.0.0.1 port 3128

block in from any to any
block out from any to any

#Антиспуфинг
antispoof quick for $int_if inet

# пропускаем все исходящие пакеты на внутреннем интерфейсе
pass out on $int_if from any to $int_net

#проброс без балансировки (согласно списку адресов)
pass in quick on $int_if route-to ($ext_if2 $gw2) from $to_trans to !$int_net keep state
pass in quick on $int_if route-to ($ext_if $gw1) from $to_ertel to !$int_net keep state

# пропускаем (quick) пакеты предназначенные самому шлюзу
pass in quick on $int_if from $int_net to $int_if
pass in quick on $int_if route-to { ($ext_if $gw1), ($ext_if2 $gw2)} round-robin proto tcp from $int_net to any flags S/SA keep state

# балансировка исходящего icmp и udp трафика идущего из внутренней сети
pass in on $int_if route-to { ($ext_if $gw1), ($ext_if2 $gw2) } round-robin proto { udp, icmp } from $int_net to any keep state

# основные «выпускаюшие» правила на внешнем интерфейсе
pass out on $ext_if proto tcp from any to any flags S/SA modulate state
pass out on $ext_if 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

# маршрутизация пакетов идущих с любого IP на $ext_if1 через $ext_gw1 и
# пакетов идущих на $ext_if2 через $ext_gw2
pass out on $ext_if route-to ($ext_if2 $gw2) from $ext_if2 to any
pass out on $ext_if2 route-to ($ext_if $gw1) from $ext_if to any

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

Недостаток данного пути в том, что иногда, когда что-то случается со стороны одного из провайдеров, балансировка нагрузки работать перестает.
Для решения данной проблемы были созданы конфигурационные файлы: pf.conf.ertelecom и pf.conf.trans, которые отличаются от основного файла настроек только тем, что строка:
#pass in on $int_if route-to { ($ext_if $gw1), ($ext_if2 $gw2) } round-robin proto { udp, icmp } from $int_net to any keep state
из них выкинута.
Скрипты переключения выглядит так:

cat er_conn.sh
#!/bin/sh

GW1=«192.168.63.25»
if1=«192.168.63.26»

#отключаем файрволл
/sbin/pfctl -d
#и включаем файрволл с правилами провайдера №2
/sbin/pfctl -e -f /etc/pf.conf.dom
#Убиваем шлюз по-умолчанию
/sbin/route del default
#Включаем шлюз по-умолчанию нашего провайдера №2
/sbin/route add default $GW1

Аналогично этому скрипту работают скрипты управления включением провайдера №1 и балансировки нагрузки. Меняется только адрес шлюза и файл правил настройки pf.
В cron подключаем скрипт, который каждые несколько (на выбор) минут проверяет на ответ от шлюза:
ping -S <ip адрес шлюза провайдера> <наш реальный ip-адрес>.
Если ответа нет, автоматически подгружаем правила живого канала.

Что касается настройки SQUID, то там я сделал вообще все примитивно. Собрал squid 3 из портов, не меняя ничего, кроме настройки transparent, то есть прозрачный прокси. Привожу его конфигурационный файл:
cat squid.conf http_port 127.0.0.1:3128 transparent icp_port 0 hierarchy_stoplist cgi-bin ? acl QUERY urlpath_regex cgi-bin \? no_cache deny QUERY cache_mem 256 MB maximum_object_size 8092 KB maximum_object_size_in_memory 512 KB cache_dir ufs /bkp/var/squid/cache 2048 64 256 cache_access_log /bkp/var/squid/access.log cache_log /bkp/var/squid/cache.log cache_store_log /bkp/var/squid/store.log cache_mgr root@xxx.ru cache_effective_user squid cache_effective_group squid visible_hostname gate coredump_dir /bkp/var/squid/cache pid_filename /var/run/squid/squid.pid acl our_networks src 10.1.1.0/24 http_access allow our_networks

Вот с этим и работаем уже около двух лет, практически ничего не вылетает, шлюз перезагружается только по прихотям электриков, пользователи забыли, что такое проблемы с доступом интернет.
Трафик считаю спиртом (cnupm), с последующей выгрузкой его логов в базу MySQL.

ссылка на оригинал статьи http://habrahabr.ru/post/177767/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *