OpenBSD PF - создание резервной группы хостов Firewall'а при помощи CARP и pfsync [FAQ PF - На главную]



Введение в CARP

CARP (Common Address Redundancy Protocol) - протокол дупликации общего адреса. Его основная цель - позволить нескольким хостам в одном сегменте сети совместно использовать одинаковый IP-адрес. CARP является безопасной, свободной альтернативой Virtual Router Redundancy Protocol (VRRP) и Hot Standby Router Protocol (HSRP).

CARP позволяет группе хостов в одном сегменте сети совместно использовать один и тот же IP-адрес. Эта группа хостов называется «группой резервирования». Ей назначается IP-адрес, который является общим для всех хостов в группе. Внутри группы один из хостов обозначен как «главный», а остальные как его «резервные копии». Главный хост - тот, который в настоящее время «держит» общий IP; он отвечает на любой трафик или запросы ARP, направленные на этот IP-адрес. Каждый хост может принадлежать более чем одной группе резервирования одновременно.

Одним из распространенных применений CARP является создание группы хостов резервирования для firewall'ов. Виртуальный IP-адрес, назначенный группе резервирования, настраивается на клиентских компьютерах в качестве шлюза по умолчанию. В случае сбоя главного хоста firewall'а или перехода его в автономный режим, IP-адрес будет использоваться одним из резервных хостов. При этом это никак не отразится на работе самого firewall'а как сервиса.

CARP поддерживает IPv4 и IPv6.

Использование CARP

Главный (master) хост в группе регулярно отправляет сообщения в локальную сеть, чтобы резервные хосты знали, что он все еще жив. Если резервные хосты не слышат этих сообщений в течение установленного периода времени, то один из них берет на себя обязанности главного (это делает тот хост, у которого установленно наименьшее значение advbase и advskew).

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

Чтобы злоумышленник в сегменте сети не мог подделать эти CARP-сообщения, каждая группа может быть настроена с паролем. Каждый CARP-пакет, отправленный группе, защищен при помощи SHA1 HMAC.

Поскольку CARP является отдельным независимым протоколом, для него должно быть указано pf pass-правило:

pass out on $carp_dev proto carp
$carp_dev это имя физического интерфейса, который будет использоваться CARP'ом для связи с остальными хостами из резервной группы.

Настройка CARP

Каждая резервная группа хостов представлена виртуальным сетевым интерфейсом carp(4). В связи с этим, CARP может быть сконфигурирован при помощи ifconfig(8).
# ifconfig carpN create
# ifconfig carpN vhid vhid [pass password] [carpdev carpdev] \
   [advbase advbase] [advskew advskew] [state state] [group|-group group] \
   ipaddress netmask mask
carpN
Имя виртуального интерфейса carp(4), где N это целое число, представляющее номер интерфейса (например, carp10).

vhid
Virtual Host ID или виртуальный ID хоста. Это уникальный номер, который используется для идентификации резервной группы для других узлов в группе и для различения групп в одной сети. Допустимые значения: от 1 до 255. Должно быть одинаковым для всех хостов в группе.

password
Пароль аутентификации, используемый при общении с другими CARP-узлами в своей группе. Он должен быть одинаковым для всех хостов в группе.

carpdev
Этот необязательный параметр указывает физический сетевой интерфейс, который будет использоваться CARP'ом. По умолчанию CARP пытается определить, какой интерфейс использовать, ища физический интерфейс, который находится в той же подсети, что и комбинация ipaddress и mask, заданная для carp(4) интерфейса.

advbase
Этот необязательный параметр указывает, как часто в секундах отправляется сообщение в сеть с информации о том, что хост является членом резервной группы. По умолчанию это 1 секунда. Допустимые значения от 1 до 255.

advskew
Этот необязательный параметр указывает, насколько сильно искажается advbase при отправке CARP-сообщений. Управляя advskew, можно выбрать главный хост CARP. Чем выше число, тем менее предпочтительным будет хост при выборе мастера. По умолчанию 0. Допустимые значения от 0 до 254.

state
Переводит carp(4) интерфейс в определенное состояние. Допустимые состояния: init, backup, и master.

group, -group
Добавляет или удаляет carp интерфейс из определенной группы интерфейсов. По умолчанию все carp интерфейсы добавляются в группу carp. Каждая группа имеет счетчик carpdemote, влияющий на все carp(4) интерфейсы, принадлежащие этой группе. Если один CARP-интерфейс выйдет из строя, CARP увеличит счетчик понижения уровня на 1 в группах интерфейсов, членом которых является интерфейс carp(4), в результате чего все члены группы будут переключаться при сбое одновременно.

ipaddress
Это общий IP-адрес, назначенный резервной группе. Этот адрес не обязательно должен находиться в той же подсети, что и IP-адрес физического интерфейса (если он есть). Однако этот адрес должен быть одинаковым на всех хостах в группе.

mask
Маска подсети общего IP.

Поведение и состояние CARP можно контролировать при помощи sysctl(8).

net.inet.carp.allow
Принимать входящие CARP-пакеты или нет. По умолчанию 1 (да, принимать).

net.inet.carp.preempt
Разрешить хостам в резервной группе, которые имеют лучшие advbase и advskew, заменить мастера. Значение net.inet.carp.preempt равно 0 (отключено) по умолчанию.

net.inet.carp.log
Изменения состояния журнала, плохие пакеты и другие ошибки. Это значение может быть между 0 и 7, что соответствует приоритетам syslog(3). По умолчанию 2 (только для изменений состояния).

Пример использования CARP

Пример конфигурации CARP может выглядеть следующим образом:
# sysctl net.inet.carp.allow=1
# echo 'net.inet.carp.allow=1' >> /etc/sysctl.conf
# ifconfig carp1 create
# ifconfig carp1 vhid 1 pass mekmitasdigoat carpdev em0 advskew 100 10.0.0.1 netmask 255.255.255.0
Эти команды создают конфигурацию, а именно: Общий IP-адрес, назначенный этой группе: 10.0.0.1/255.255.255.0.

Вывод команды ifconfig для carp1 интерфейса покажет следующее состояние.

# ifconfig carp1
carp1: flags=8802<UP,BROADCAST,SIMPLEX,MULTICAST> mtu 1500
     carp: BACKUP carpdev em0 vhid 1 advbase 1 advskew 100
     groups: carp
     inet 10.0.0.1 netmask 0xffffff00 broadcast 10.0.0.255

Введение в pfsync

Сетевой интерфейс pfsync(4) следит за всеми изменениями, внесенными в таблицу состояний pf(4). Наблюдая за этим интерфейсом при помощи tcpdump(8), можно заметить изменения таблицы состояний в режиме реального времени. Кроме того, интерфейс pfsync может отправлять сообщения об изменении состояния в сеть, чтобы другие узлы, работающие с PF, могли внести изменения в свои собственные таблицы состояний. Аналогично, pfsync также может прослушивать в сети входящие сообщения.

Управление pfsync

По умолчанию, pfsync не отправляет и не получает обновления таблицы состояний в сети; однако обновления можно отслеживать при помощи tcpdump или других подобных инструментов на локальном компьютере.

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

  1. Соедините два узла, которые будут обмениваться обновлениями, при помощи перекрестного (crossover) кабеля и используйте этот интерфейс в качестве syncdev (см. ниже)
  2. Используйте ifconfig опцию syncpeer (см. ниже), чтобы обновления выполнялись unicast-передачей напрямую одноранговому узлу, затем настройте ipsec(4) между хостами для защиты pfsync трафика
Когда обновления отправляются и принимаются из сети, для pfsync-пакетов должно быть созданно соответствующее PF правило:
pass on $sync_if proto pfsync
$sync_if это имя физического интерфейса, который будет использовать pfsync.

Настройка pfsync

pfsync является виртуальным сетевым интерфейсом и настраивается при помощи ifconfig(8).
# ifconfig pfsyncN syncdev syncdev [syncpeer syncpeer] [defer|-defer]
pfsyncN
Имя pfsync интерфейса.

syncdev
Имя физического интерфейса, используемого pfsync для отправки и получения обновлений

syncpeer
Этот необязательный параметр указывает IP-адрес хоста для обмена обновлениями pfsync. По умолчанию это multicast-обновления в локальной сети. Эта опция переопределяет это поведение и вместо этого направляет обновление указанному syncpeer.

defer
Если используется флаг defer, первый initial-пакет нового соединения, проходящего через firewall, не будет передаваться, пока либо другая система pfsync не подтвердит добавление таблицы состояний, либо не истечет время ожидания. Это добавляет небольшие задержки, но позволяет трафику проходить, когда несколько firewall'ов могут активно обрабатывать пакеты («активный/активный»), например, с некоторыми конфигурациями ospfd(8), bgpd(8), или carp(4).

Пример pfsync

Тут пример настройки pfsync:
# ifconfig pfsync0 syncdev em1 up
Эта команда активирует(enables) pfsync на интерфейсе em1. Исходящие обновления будут многоадресными (multicast) в сети, что позволит любому другому узлу, на котором запущен pfsync, получать их.

Совместное использование CARP и pfsync с целью отказоустойчивости

При комбинировании возможностей CARP и pfsync, группу из двух или более firewall'ов можно использовать для создания высокодоступного, отказоустройчивого firewall-кластера. CARP берет на себя задачу автоматического переключения с одного firewall'а на другой. в то время как pfsync синхронизирует таблицу состояний между ними. В случае аварийного переключения или просто сбоя, трафик сражу же перенаправляется на новый (master) firewall.

Пример сценария: два firewall'а, fw1 и fw2.

         +----| WAN/internet |----+
         |                        |
      em2|                        |em2
      +-----+                  +-----+
      | fw1 |-em1----------em1-| fw2 |
      +-----+                  +-----+
      em0|                        |em0
         |                        |
      ---+-------Shared LAN-------+---
Firewall'ы подключены друг к другу при помощи перекрестного (crossover) кабеля с em1. Оба подключены к локальной сети с em0 и к WAN/интернет с em2. IP-адреса следующие: Политика сети такова, что fw1 будет предпочтительным мастером.

Чтобы настроить fw1, начните с включения приоритетного прерывания (preemption) и переключения группового интерфейса при отказе (group interface failover):

# sysctl net.inet.carp.preempt=1
# echo 'net.inet.carp.preempt=1' >> /etc/sysctl.conf
Настройка pfsync:
! configure pfsync
# ifconfig em1 10.10.10.1 netmask 255.255.255.0
# ifconfig pfsync0 syncdev em1
# ifconfig pfsync0 up
Настройка CARP на стороне LAN:
# ifconfig carp1 create
# ifconfig carp1 vhid 1 carpdev em0 pass lanpasswd \
     172.16.0.100 netmask 255.255.255.0
Настройка CARP на стороне WAN/интернет:
# ifconfig carp2 create
# ifconfig carp2 vhid 2 carpdev em2 pass netpasswd \
     192.0.2.100 netmask 255.255.255.0
Затем настраиваем соответственно fw2:
# sysctl net.inet.carp.preempt=1
# echo 'net.inet.carp.preempt=1' >> /etc/sysctl.conf
# ifconfig em1 10.10.10.2 netmask 255.255.255.0
# ifconfig pfsync0 syncdev em1
# ifconfig pfsync0 up
# ifconfig carp1 create
# ifconfig carp1 vhid 1 carpdev em0 pass lanpasswd \
     advskew 128 172.16.0.100 netmask 255.255.255.0
# ifconfig carp2 create
# ifconfig carp2 vhid 2 carpdev em2 pass netpasswd \
     advskew 128 192.0.2.100 netmask 255.255.255.0

Operational Issues

Настройка CARP и pfsync во время загрузки

Так как carp и pfsync являются сетевыми интерфейсами, они могут быть сконфигурированны во время загрузки при помощи hostname.if(5) файла. Загрузочный скрипт netstart позаботиться о создании интерфейсов и их настройке.

Пример содержания этих файлов:

/etc/hostname.carp1
inet 172.16.0.100 255.255.255.0 172.16.0.255 vhid 1 carpdev em0 pass lanpasswd
/etc/hostname.pfsync0
syncdev em1 up

Принудительное переключение мастера

Могут быть случаи, когда необходимо переключение или демонирование главного master-узла. Напримеры могут включать отключение с той или иной целью или просто устранения неполадок (maintenance). Задачей здесь является постепенное переключение трафика на один из резервных хостов, чтобы пользователи ничего не заметили, и у них не возникло никаких проблем.

Для восстановления определенной CARP-группы выключите carp интерфейс на главном master-узле. Это приведет к тому, что сообщения мастера станут с «бесконечной» advbase and advskew. Резервные хосты увидят это, и роль мастера возьмет на себе кто-то другой.

# ifconfig carp1 down
Альтернативой является увеличение advskew до значения, превышающего advskew на резервных хостах. Это приведет к аварийному переключению, но все же позволит мастеру участвовать в CRAP-группе.

Другой метод восстановления после сбоя - настроить счетчик понижения CARP. Счетчик понижения - это мера того, насколько «готов» хост стать master'ом CARP-группы. Например, в то время как хост находится в процессе загрузки, плохой идеей будет стать мастером, пока не будут настроены все интерфейсы, запущены все сетевые демоны и т.д. Хосты, сообщающие о себе высокое значение понижения, будут менее предпочтительными в качестве мастера.

Счетчик понижения хранится в каждой группе интерфейсов, которой принадлежит CARP-интерфейс. По умолчанию все CARP-интерфейсы являются членами группы интерфейсов «carp». Текущее значение счетчика понижения можно просмотреть при помощи ifconfig:

# ifconfig -g carp
carp: carp demote count 0
В этом примере показан счетчик, связанный с группой интерфейсов «carp». Когда CARP-хост объявляет себя в сети, он принимает сумму счетчиков понижения для каждой группы интерфейсов, к которой принадлежит и интерфейс carp, и объявляет это значение в качестве значения понижения (demotion value).

Теперь рассмотрим следующий пример. Два firewall'а, которые объеденены в CARP-кластер со следующими CARP-интерфейсами:

Задача состоит в том, чтобы переключить только группы carp1 и carp2 на вторичном firewall'е.

Во-первых, присвойте каждому интерфейсу новую группу, в данном случае с именем «internal»:

# ifconfig carp1 group internal
# ifconfig carp2 group internal
# ifconfig internal
carp1: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
     carp: MASTER carpdev em0 vhid 1 advbase 1 advskew 100
     groups: carp internal
     inet 10.0.0.1 netmask 0xffffff00 broadcast 10.0.0.255
carp2: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
     carp: MASTER carpdev em1 vhid 2 advbase 1 advskew 100
     groups: carp internal
     inet 10.0.1.1 netmask 0xffffff00 broadcast 10.0.1.255
Теперь увеличьте счетчик понижений для «internal» группы при помощи ifconfig(8):
# ifconfig -g internal
internal: carp demote count 0
# ifconfig -g internal carpdemote 50
# ifconfig -g internal
internal: carp demote count 50
Firewall теперь будет корректно переключаться при сбое на группы carp1 и carp2 на другом firewall'е в кластере, оставаясь при этом основным на carp3 и carp4. Если другой firewall начал отправлять сообщения о себе со значением понижения, превышающим 50, или если другой firewall вообще прекратил отправку этих сообщений, то этот firewall снова перенял бы роль мастера на carp1 и carp2.

Чтобы вернуться к основному firewall'у, отмените изменения:

# ifconfig -g internal -carpdemote 50
# ifconfig -g internal
internal: carp demote count 0
Сетевые демоны, такие как OpenBGPD и sasyncd(8), используют счетчик понижений, чтобы гарантировать, что firewall не станет главным, пока не будут установлены BGP-сеансы и не синхронизированы SA IPsec.

Совет по поводу PF правил

Фильтруйте физический интерфейс. Что касается PF, сетевой трафик поступает от физического, а не виртуального CARP-интерфейса (то есть, carp0). Таким образом, учитывайте это при написании правил. Не забывайте, что имя интерфейса в правиле PF может быть именем физического интерфейса или адресом, связанным с этим интерфейсом. Например, это правило может быть правильным:
pass in on fxp0 inet proto tcp from any to carp0 port 22
Но замена fxp0 на carp0 не будет работать.

Не забудьте разрешить proto carp и proto pfsync!

Другие источники информации

В нижеперечисленнх man-страницах также можно найти много полезной информации: