ToFoIn v 1. Резервирование шлюзов и переключение между внешними каналами во FreeBSD

в 3:30, , рубрики: bash, CARP, freebsd, networking, VLAN, Сетевые технологии

Аннотация

В прошлой публикации рассматривался вопрос организации резервирования для шлюзов локальной сети. Как вариант решения был предложен скрипт, который на тот момент решал задачу, однако имел ряд недостатков. Спустя некоторое время получилось эти недостатки устранить, частично переписать код и на выходе получить уже что-то приемлемое. Сейчас можно говорить, что скрипты достаточно протестированы, чтобы называться стабильными. Для упрощения понимания всей системы основные моменты по настройке второстепенных сервисов (с точки зрения темы статьи) будут частично продублированы ниже. Причина проста — за это время были переработаны также правила ipfw, dns отправился жить в AD на Samba4 с bind-frontend и безопасным обновлением записей из isc-dhcpd с использованием kerberos, а также вторичными dns-серверами в виде bind-а на шлюзах, был настроен CARP… В общем, стало сильно интереснее, но подробнее о том, что и как работает — ниже. Всё, что можно будет давать ссылками на первоисточник, будет оформлено именно таким образом, дабы не плодить сущности. То, что бралось из каких либо других мест, но что более недоступно, будет приводиться здесь с соответствующими комментариями.

Введение

Итак, повысить помехоустойчивость канала связи с внешним миром со стороны потребителя, можно двумя способами: резервирование шлюза и резервирование точки подключения. Другими словами, в первом случае ставится второй шлюз, который включится в работу в случае отказа первого, во втором же — организуется резервный интернет канал на случай каких либо проблем с основным, причем, чем дальше они пересекаются, тем, очевидно, лучше. Если первую задачу для FreeBSD решает CARP, то вторую, после организации второго внешнего канала, можно, опять же, решать несколькими способами. Как минимум, можно организовать балансировку трафика или переключение между каналами. В силу большой разницы по пропускной способности внешних каналов, мне первый вариант не подошел, поэтому был написан основной виновник публикации: ToFoIn — набор bash-скриптов, который нацелен на решение задач диагностики и переключения на работающий внешний канал. После доработки его можно применять для n шлюзов и m каналов. Слабо представляется ситуация, где n и m больше 2-х, но нижепредставленные скрипты должны работать до больших значений n и m, т.к. логически предел не установлен. В целом же, подозреваю, что с помощью данных скриптов можно решать достаточно широкий спектр задач, зависящих от статуса подключения, ограниченный, пожалуй, только лишь воображением.

ToFoIn v 1. Резервирование шлюзов и переключение между внешними каналами во FreeBSD - 1
Приблизительно такая топология сети предполагает в простейшем виде использование набора скриптов ToFoIn. Конечно, скрипты должны отрабатывать и в случае одного маршрутизатора, но в этом случае придётся сильно изменять модуль Daemon, чтобы убрать зависимость последовательности действий от состояния CARP, который будет просто отсутствовать в системе. Дальнейшее же резервирование этих и других узлов зависит лишь от степени важности соответствующих услуг.

Цели и задачи

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

  • Имеется n «маршрутизаторов» с m внешних каналов на каждом. При этом все n «маршрутизаторов» находятся в строгой иерархии и связаны друг с другом при помощи CARP по всем необходимым интерфейсам.
  • На всех машинах независимо друг от друга работает агент, задача которого, основываясь на текущем статусе CARP своей машины:
    • Если backup — определять и настраивать машину на маршрутизатор, являющийся master в настоящий момент;
    • Если master — проверять состояние соединений на текущий момент и, при необходимости, производить переключение между внешними каналами.

Решение

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

Dhcpd действует в primary — secondary режиме и ему, в общем-то, всё равно, какие еще роли играет его машина — связь между dhcpd происходит во внутренней сети, в которую маршрутизаторы смотрят всегда.

Мастер-bind убран в AD, который спрятан в локальную сеть, в то время как на маршрутизаторах-шлюзах на равных правах работают вторичные bind-сервера.

Правила ipfw различаются в зависимости от того, какой канал в заданный момент считается основным и перезапускаются модулем Daemon при смене роли.

Наконец о самих скриптах. Теперь файлы расположены по соответствующим каталогам, работают от своего пользователя и имеют стартовый скрипт в rc.d. Задачи, требующие root доступа, решаются sudo. Имеется скрипт установки, учитывающий возможное наличие установленной версии, а также достаточно подробный файл настроек. Модули прежние с небольшими изменениями, некоторые почти не изменились по функционалу:

Daemon – как и следует из названия, — основной процесс, который по таймеру запускает модули тестирования и переключения, а также отслеживает CARP.
Tester – тестирует наличие связи по внешним каналам всё еще с помощью команды ping. (если запущен, то считает, что машина имеет CARP в состоянии Master)
Judge – исходя из результатов тестов, определяет, какой внешний канал работает и необходимо ли переключение, осуществляет переключение(если запущен, то считает, что машина имеет CARP в состоянии Master).
Scout — новый модуль. Запускается в случае нахождения CARP в состоянии Backup. Нужен для определения, какой из оставшихся маршрутизаторов в настоящий момент является основным.
Logger – отвечает за ведение журнала событий. Необходим для того, чтобы информация о событиях не дублировалась и журнал проще читался.
Watchdog – запускается по расписанию из crontab. Определяет «зависания» всех модулей и (по возможности) пытается решить возникшие проблемы. Т.е. прибить всех, проще говоря.

Помимо самих скриптов, стоит рассмотреть еще некоторые важные файлы:

Tofoin.conf – единый файл настроек.
Tofoin.log – единый файл журнала событий.
Result_<внутренний номер канала> — рабочий файл, сюда «складываются» результаты тестирования, создаётся в /tmp рядом с .pid и прочими рабочими файлами.

На вопросы касательно работы модулей по существу могу с удовольствием ответить, пояснив решения в комментариях.

Техническая часть

Оборудование

По сравнению с прошлым разом шлюзы переехали на P4, получили по 1536 Mb RAM и по три 40 Gb HDD (mirror + spare). Сетевые карты всё так же PCI, БП обычные, естественно в наличие ИБП.
Увеличение мощностей связано с освободившимся железом и излишне утомительным обновлением из исходников, но в основном — первое. ОС FreeBSD 11.1, ФС zfs.

Настройки системных компонентов

Подробнее

Ядро собрано с такими дополнительными параметрами (кое-что можно задать и в loader-е, но лучше так):

options IPFIREWALL # ipfw firewall
options IPFIREWALL_VERBOSE
options IPFIREWALL_VERBOSE_LIMIT=50
options IPFIREWALL_NAT
options LIBALIAS
options DUMMYNET
options HZ=1000
options ROUTETABLES=4
options KSTACK_PAGES=4
options KVA_PAGES=512
device carp

Настройки /boot/loader.conf:

geom_mirror_load="YES"
zfs_load="YES"
kern.geom.label.gptid.enable="0"
vm.kmem_size="1024M"
vm.kmem_size_max="1024M"
vfs.zfs.arc_max="512M"
vfs.zfs.vdev.cache.size="30M"
vfs.zfs.prefetch_disable=1
kern.vty=vt

Настройки /etc/rc.conf на первой машине(основной интерес представляет настройка CARP):

ifconfig_eth0="up"
vlans_eth0="vlan111 vlan222"
create_args_vlan111="vlan 111"
create_args_vlan222="vlan 222"
ifconfig_eth1="up"
vlans_eth1="vlan333 vlan444 vlan555"
create_args_vlan333="vlan 333"
create_args_vlan444="vlan 444"
create_args_vlan555="vlan 555"
ifconfig_eth2="up"
vlans_eth2="vlan666 vlan777 vlan888"
create_args_vlan666="vlan 666"
create_args_vlan777="vlan 777"
create_args_vlan888="vlan 888"
ifconfig_vlan666="inet 192.168.0.1/24"
ifconfig_vlan666_alias0="vhid 1 advskew 100 pass MyPassword alias 192.168.0.5/32"
ifconfig_vlan777="inet 192.168.1.1/24"
ifconfig_vlan777_alias0="vhid 1 advskew 100 pass MyPassword alias 192.168.1.5/32"
ifconfig_vlan888="inet 192.168.2.1/24"
ifconfig_vlan888_alias0="vhid 1 advskew 100 pass MyPassword alias 192.168.2.5/32"
ifconfig_vlan111="inet 192.168.3.1/30"
ifconfig_vlan111_alias0="vhid 1 advskew 100 pass MyPassword alias 1.1.1.2/24"
ifconfig_vlan222="inet 192.168.4.1/30"
ifconfig_vlan333="inet 192.168.5.1/30"
ifconfig_vlan333_alias0="vhid 1 advskew 100 pass MyPassword alias 2.2.2.2/30"
ifconfig_vlan444="inet 192.168.6.1/30"
ifconfig_vlan444_alias0="vhid 1 advskew 100 pass MyPassword alias 3.3.3.2/30"
ifconfig_vlan555="inet 192.168.7.1/30"
defaultrouter="1.1.1.1"
setfib1_enable="YES"
setfib1_defaultrouter="3.3.3.1"
setfib2_enable="YES"
setfib2_defaultrouter="2.2.2.1"
zfs_enable="YES"
named_enable="YES"
dhcpd_enable="YES"
firewall_enable="YES"
firewall_logging="YES"
firewall_script="/etc/firewall.sh"
gateway_enable="YES"
tofoin_enable="YES"

Легенда:
eth0,eth1,eth2 — физические адаптеры
vlan666, vlan777, vlan888 — виртуальные адаптеры локальной сети,
vlan222 и vlan555 — адаптеры для резервной связи между внешними сетевыми картами(возможно уже и не нужны, активно использовались ранее)
vlan111 — основной внешний канал
vlan444 — резервный внешний канал
vlan333 — телефония

Настройки /etc/rc.conf на второй машине(основной интерес представляет настройка CARP, часть повторяющихся строк убрана):

ifconfig_vlan666="inet 192.168.0.2/24"
ifconfig_vlan666_alias0="vhid 1 advskew 0 pass MyPassword alias 192.168.0.5/32"
ifconfig_vlan777="inet 192.168.1.2/24"
ifconfig_vlan777_alias0="vhid 1 advskew 0 pass MyPassword alias 192.168.1.5/32"
ifconfig_vlan888="inet 192.168.2.2/24"
ifconfig_vlan888_alias0="vhid 1 advskew 0 pass MyPassword alias 192.168.2.5/32"
ifconfig_vlan111="inet 192.168.3.2/30"
ifconfig_vlan111_alias0="vhid 1 advskew 0 pass MyPassword alias 1.1.1.2/24"
ifconfig_vlan222="inet 192.168.4.2/30"
ifconfig_vlan333="inet 192.168.5.2/30"
ifconfig_vlan333_alias0="vhid 1 advskew 0 pass MyPassword alias 2.2.2.2/30"
ifconfig_vlan444="inet 192.168.6.2/30"
ifconfig_vlan444_alias0="vhid 1 advskew 0 pass MyPassword alias 3.3.3.2/30"
ifconfig_vlan555="inet 192.168.7.2/30"
defaultrouter="1.1.1.1"
setfib1_enable="YES"
setfib1_defaultrouter="3.3.3.1"
setfib2_enable="YES"
setfib2_defaultrouter="2.2.2.1"

Некоторые правила, которые пригодятся при настройке ipfw(nat):
для разрешения траффика CARP:

/sbin/ipfw -q add allow carp from any to any

«ядерный» nat:

/sbin/ipfw -q nat 1 config log ip vlan111 reset same_ports deny_in unreg_only
/sbin/ipfw -q add nat 1 ip from any to any in

использование определённых таблиц маршрутизации с определёнными адаптерами:

/sbin/ipfw -q add setfib 0 all from any to any via vlan666

Вообще, про применённые у меня настройки ipfw можно было бы спокойно отдельную статью написать, но это в какой-нибудь другой раз.

Стороннее программное обеспечение

Подробнее

Т. к. есть необходимость одновременно работать с двумя и более внешними каналами, для этого удобно иметь несколько таблиц маршрутизации, по одной на каждый канал. И неплохо было бы, если бы эти таблицы создавались при старте сами. В этом поможет rc.d скрипт setfib. Логика, используемая в ToFoIn, предполагает, что имя файла(setfib1, setfib2 и т. д.) совпадает с номером таблицы, в которую отдельно взятый скрипт добавляет маршрут по умолчанию. Таблица по умолчанию имеет номер «0».
DNS сервера с Bind в главной роли работают в режиме вторичных, в роли основного выступает samba4+bind, спрятанная в локальной сети. Настройка вторичных bind прекрасно раскрыта в книге «DNS и BIND» Крикета Ли и Пола Альбитца. Каких-то особых требований, учитывающих использование samba4 для secondary серверов, я не помню, и у меня в файле настроек о них упоминания нет. Разве что, для разных интернет каналов может потребоваться создать 2 разных файла, которые затем скриптом ToFoIn будут копироваться в место, из которого его будет читать сам bind. Это сделано вследствие того, что при указании в одном файле адресов dns-серверов обоих провайдеров, с учетом того, что bind работает только с одной таблицей маршрутизации, возникает проблема, связанная с разрешением адресов с недоступных в определённый момент вышестоящих серверов.
Failover isc-dhcpd. Dhcpd не принципиален для работы ToFoIn, более того, его отсутствие вообще никак не скажется на работе скриптов, однако, как мне кажется, достаточно логично разместить dhcp сервера именно на шлюзах и тогда вопрос failover всё-таки встаёт. А тут, по сравнению с прошлым разом, стало интереснее… Помимо настройки, необходимой для failover, которую я описывал в прошлый раз(начало раздела «предварительная настройка» внутри раскрывающегося меню).
Потребуется также скрипт для безопасного обновления записей dns в AD средствами samba4. Сам сервер samba4 должен быть просто установлен. Настройка и запуск не требуется, нас интересуют только инструменты управления, идущие в комплекте. Прочую информацию желающие могут найти в разделе «DHCP with dynamic DNS updates» по адресу.
Выглядит страшно, но работает.
На этом с настройкой стороннего софта закончено.

Немного о ToFoIn

Весь текст проекта вместе с установочным скриптом доступен на gitlab.

В завершение, рассмотрен пример параметров файла настроек ToFoIn:

Число маршрутизаторов, используемых в системе:

RNUMBER=2

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

ADDITLAN=2

Имя внутреннего адаптера:

INT_IF=vlan666

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

ALL_IF="vlan111 vlan333 vlan444 vlan666 vlan777 vlan888"

vhid, который был использован при настройке CARP:

CARP_VHID=1

IP адреса во внутренней сети других маршрутизаторов в порядке важности, при необходимости, далее просто используется ASERV_IP_2, ASERV_IP_3 и т.д.

ASERV_IP_1=192.168.0.2

Число внешних каналов подключения:

CNUMBER=2

Настройки для основного внешнего канала подключения:
Имя адаптера:

EXT_0_IF=vlan111

Номер таблицы маршрутизации:

RTABLE_0=0

Шлюз по умолчанию:

DEFAULT_GATEWAY=2.2.2.1

Настройки для резервного внешнего канала подключения:
Имя адаптера:

EXT_1_IF=vlan444

Номер таблицы маршрутизации:

RTABLE_1=1

Шлюз по умолчанию не требуется, т. к. для всех таблиц маршрутизации, кроме основной используется rc.d скрипт setfib<номер таблицы>, который, как предполагается логикой, должен совпадать с номером таблицы.
Параметры модуля Tester:
Число адресов, по которым будет происходить проверка:

TNUMBER=2

Адреса машин, по которым отправляются ping-запросы. Лучше всего использовать в первом случае доменное имя, и только после этого ip адреса:

PTARGET_0=ya.ru
PTARGET_1=8.8.8.8

Число ping пакетов, отправляемых одной целе:

PNUMBER=2

Настройки модуля Judge
Число успешных тестирований основного канала перед возвращением на него. Время возврата на основной канал после возобновления его работы приблизительно рассчитывается по формуле: (WNUMBER+1)*JUDGEPERIOD секунд.

WNUMBER=3

Настройки модуля Logger
Данные 2 параметра означают, с какой частотой Logger будет записывать повторяющиеся события. После записи о событии в следующий раз сообщается о LOGFREQ1 числе повторов, далее о LOGFREQ2 числе повторов. Учитываются только события идущие подряд.

LOGFREQ1=5
LOGFREQ2=20

Таймеры запуска модулей в секундах
Период запуска модуля Tester. Имеет смысл рассчитывать исходя из времени неудачных попыток тестирования всех целей.

TESTERPERIOD=240

Период запуска модуля Judge. Не стоит устанавливать меньше TESTERPERIOD.

JUDGEPERIOD=300

Период запуска модуля Scout.

SCOUTPERIOD=360

Период ожидания перед проверкой таймеров запуска модулей Tester и Judge. Логично устанавливать меньше или равным значению TESTERPERIOD.

SENSITIVITY=60

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

TESTERLIMIT=40
JUDGELIMIT=30
LOGGERLIMIT=20
SCOUTLIMIT=120
WATCHDOGLIMIT=150

Пути к файлам и каталогам
Путь к скрипту ipfw.

FIRESCRIPT=/etc/firewall.sh

Настройки ipfw. Если настройки ipfw не вынесены в отдельный файл, то FIRESCRIPT=FIRESETDEF.

FIRESETDEF=/etc/firewall/config

Путь к настройкам ipfw для основного внешнего канала:

FIRESET_0=/etc/firewall/config_0

Путь к настройкам ipfw для резервного внешнего канала, при необходимости можно продолжать далее FIRESET_2 и т.д.:

FIRESET_1=/etc/firewall/config_1

Пути к настройкам bind

BINDSETDEF=/usr/local/etc/namedb/named.conf

Настройки bind для основного внешнего канала:

BINDSET_0=/usr/local/etc/namedb/named.conf.0

Настройки bind для резервного внешнего канала, при необходимости можно продолжать далее BINDSET_2 и т.д.:

BINDSET_1=/usr/local/etc/namedb/named.conf.1

Пути ко всем исполняемым файлам ToFoIn:

DAEMON=/local/sbin/tofoin/daemon.sh
TESTER=/usr/local/sbin/tofoin/tester.sh
JUDGE=/usr/local/sbin/tofoin/judge.sh
LOGGER=/usr/local/sbin/tofoin/logger.sh
SCOUT=/usr/local/sbin/tofoin/scout.sh
WATCHDOG=/usr/local/sbin/tofoin/watchdog.sh

Журнал событий. Этот файл сейчас НЕ создаётся при установке:

LOGFILE=/var/log/tofoin.log

Временные файлы и каталоги, создаются при запуске соответствующих модулей, некоторые удаляются при остановке:

DIR_TMP=/tmp/tofoin
DIR_PID=/var/run/tofoin
JUDGEMETER=/tmp/tofoin/judgemeter
PREVSTATE=/tmp/tofoin/prevstate
SCOUTGATE=/tmp/tofoin/scoutgate
LOGTMP=/tmp/tofoin/logger.tmp
LOGMETER=/tmp/tofoin/logmeter
DAEMON_PID=/var/run/tofoin/daemon.pid
TESTER_PID=/var/run/tofoin
SCOUT_PID=/var/run/tofoin/scout.pid
JUDGE_PID=/var/run/tofoin/judge.pid
LOGGER_PID=/var/run/tofoin/logger.pid
WATCHDOG_PID=/var/run/tofoin_watchdog.pid

Итог

Получился вполне работоспособный и надежный набор скриптов, который хорошо справляется с задачей переключения на рабочий канал в случае 2 роутеров с 2 внешними каналами связи.

Планы

Мои планы относительно этого проекта касаются, разве что, переписывания с bash на чистый sh, чтобы избавиться от лишнего ПО на сервере. С другой стороны — сейчас всё работает изумительно и вмешиваться в данный процесс крайне не хочется, к тому же переход на sh чреват более страшными языковыми конструкциями, необходимыми для достижения того же результата.
В остальном, наверное, стоило бы подумать над лучшей реализацией тестовых модулей.

Ссылки:

Предыдущая статья
Страница проекта ToFoIn на gitlab-е

Автор: LordNicky

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js