Объединяем 5 офисов в единую сеть: site-to-site VPN на IPsec и WireGuard

Задача: связать 5 офисов без MPLS

Консалтинговая компания «БизнесПартнёр» имеет 5 офисов: Москва (центральный), Санкт-Петербург, Казань, Новосибирск и Краснодар. До нашего прихода офисы были связаны через MPLS-каналы провайдера — 5 каналов по 100 Мбит/с стоимостью 180 000 ₽ в месяц суммарно. При этом интернет-каналы по 500 Мбит/с в каждом офисе стоили всего 15 000 ₽.

Задача: построить защищённую корпоративную сеть через интернет, обеспечить отказоустойчивость (два провайдера в каждом офисе), приоритет для VoIP-трафика и прозрачную маршрутизацию между филиалами.

Требования:

  • Шифрование — весь межофисный трафик должен быть зашифрован (IKEv2 + AES-256)
  • Отказоустойчивость — автоматическое переключение при падении одного ISP
  • QoS — VoIP (Asterisk) должен работать без потерь и джиттера
  • Масштабируемость — добавление нового офиса без перенастройки всех точек
  • L2-связность — некоторые приложения требуют broadcast-домен между офисами

Архитектура: hub-and-spoke + mesh overlay

Мы выбрали гибридную архитектуру. IPsec (strongSwan) — основной транспортный уровень с IKEv2 для связи каждого офиса с Москвой (hub-and-spoke). Поверх него — WireGuard mesh для прямого обмена между филиалами. VXLAN через WireGuard для L2-сегментов.

Адресный план:

ОфисLAN-подсетьWireGuard IPВнешний IP (ISP1)Внешний IP (ISP2)
Москва (hub)10.10.0.0/1610.255.0.1/24203.0.113.10198.51.100.10
Санкт-Петербург10.20.0.0/1610.255.0.2/24203.0.113.20198.51.100.20
Казань10.30.0.0/1610.255.0.3/24203.0.113.30198.51.100.30
Новосибирск10.40.0.0/1610.255.0.4/24203.0.113.40198.51.100.40
Краснодар10.50.0.0/1610.255.0.5/24203.0.113.50198.51.100.50

IPsec: strongSwan с IKEv2

На каждом шлюзе — Ubuntu 22.04 с установленным strongSwan. IPsec обеспечивает базовый зашифрованный туннель между каждым филиалом и Москвой.

Конфигурация хаба (Москва), файл /etc/swanctl/conf.d/offices.conf:

connections {
    spb {
        version = 2
        local_addrs = 203.0.113.10
        remote_addrs = 203.0.113.20

        local {
            auth = psk
            id = moscow-hub
        }
        remote {
            auth = psk
            id = spb-spoke
        }

        children {
            spb-tunnel {
                local_ts = 10.10.0.0/16,10.255.0.0/24
                remote_ts = 10.20.0.0/16
                esp_proposals = aes256gcm128-sha256-modp2048
                dpd_action = restart
                start_action = start
                close_action = restart
            }
        }
        proposals = aes256-sha256-modp2048
        dpd_delay = 10s
    }
    # Аналогичные блоки для kazan, novosibirsk, krasnodar
}

secrets {
    ike-spb {
        id = spb-spoke
        secret = "сгенерированный-ключ-64-символа"
    }
}

Проверка работоспособности туннеля:

# Статус всех туннелей
sudo swanctl --list-sas

# Инициировать переподключение
sudo swanctl --initiate --child spb-tunnel

# Проверить счётчики пакетов
sudo swanctl --list-sas --ike spb | grep -E 'bytes_in|bytes_out'

WireGuard mesh: прямая связь между филиалами

IPsec через hub — это надёжно, но трафик между Казанью и Новосибирском идёт через Москву. Для прямого обмена мы подняли WireGuard mesh — каждый офис имеет прямой туннель с каждым.

Конфигурация WireGuard на шлюзе Казани (/etc/wireguard/wg0.conf):

[Interface]
PrivateKey = kKazanPrivateKeyBase64=
Address = 10.255.0.3/24
ListenPort = 51820
# MTU с учётом IPsec overhead
MTU = 1380
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT

# Москва
[Peer]
PublicKey = MoscowPublicKeyBase64=
AllowedIPs = 10.255.0.1/32, 10.10.0.0/16
Endpoint = 203.0.113.10:51820
PersistentKeepalive = 25

# Санкт-Петербург
[Peer]
PublicKey = SpbPublicKeyBase64=
AllowedIPs = 10.255.0.2/32, 10.20.0.0/16
Endpoint = 203.0.113.20:51820
PersistentKeepalive = 25

# Новосибирск
[Peer]
PublicKey = NovosibirskPublicKeyBase64=
AllowedIPs = 10.255.0.4/32, 10.40.0.0/16
Endpoint = 203.0.113.40:51820
PersistentKeepalive = 25

# Краснодар
[Peer]
PublicKey = KrasnodarPublicKeyBase64=
AllowedIPs = 10.255.0.5/32, 10.50.0.0/16
Endpoint = 203.0.113.50:51820
PersistentKeepalive = 25

Для автоматического управления mesh-конфигурацией при добавлении новых офисов мы написали скрипт, генерирующий конфиги из YAML-описания сети.

OSPF с FRR: динамическая маршрутизация

Статические маршруты в сети из 5 офисов — это 20 записей на каждом шлюзе. При добавлении нового офиса нужно обновить все 5. Мы развернули FRRouting (FRR) с OSPF на каждом шлюзе.

Конфигурация FRR на Московском хабе (/etc/frr/frr.conf):

frr version 8.5
frr defaults traditional
hostname moscow-gw
log syslog informational

router ospf
 ospf router-id 10.255.0.1
 # WireGuard mesh — backbone area
 network 10.255.0.0/24 area 0.0.0.0
 # Локальная сеть Москвы
 network 10.10.0.0/16 area 0.0.0.0
 # Таймеры для быстрого failover
 timers throttle spf 50 100 5000
 # Перераспределяем connected маршруты
 redistribute connected route-map CONNECTED-TO-OSPF

interface wg0
 ip ospf hello-interval 5
 ip ospf dead-interval 15
 ip ospf cost 10

! Route-map для фильтрации
route-map CONNECTED-TO-OSPF permit 10
 match interface wg0
route-map CONNECTED-TO-OSPF permit 20
 match interface eth1
route-map CONNECTED-TO-OSPF deny 30

Проверка маршрутов:

# Таблица OSPF-соседей
sudo vtysh -c "show ip ospf neighbor"

# OSPF-маршруты
sudo vtysh -c "show ip route ospf"

# Пример вывода:
# O   10.20.0.0/16 [110/20] via 10.255.0.2, wg0, weight 1, 01:23:45
# O   10.30.0.0/16 [110/20] via 10.255.0.3, wg0, weight 1, 01:23:45
# O   10.40.0.0/16 [110/20] via 10.255.0.4, wg0, weight 1, 01:23:40
# O   10.50.0.0/16 [110/20] via 10.255.0.5, wg0, weight 1, 01:23:38

При добавлении нового офиса достаточно поднять WireGuard peer и FRR — OSPF автоматически распространит маршруты по всей сети за секунды.

Failover: два ISP с автопереключением

В каждом офисе два интернет-провайдера. Если основной канал падает, VPN-туннели автоматически переключаются на резервный. Мы реализовали это через скрипт мониторинга и systemd-таймер:

#!/bin/bash
# /usr/local/bin/vpn-failover.sh — мониторинг и переключение ISP

PRIMARY_GW="203.0.113.1"   # Шлюз ISP1
BACKUP_GW="198.51.100.1"   # Шлюз ISP2
PRIMARY_IF="eth0"           # Интерфейс ISP1
BACKUP_IF="eth1"            # Интерфейс ISP2
CHECK_HOSTS=("8.8.8.8" "1.1.1.1" "77.88.8.8")  # Хосты для проверки
FAIL_THRESHOLD=3
CURRENT_STATE_FILE="/var/run/vpn-isp-state"

check_connectivity() {
    local iface=$1
    local failures=0
    for host in "${CHECK_HOSTS[@]}"; do
        if ! ping -c 1 -W 2 -I "$iface" "$host" &>/dev/null; then
            ((failures++))
        fi
    done
    echo $failures
}

current_state=$(cat "$CURRENT_STATE_FILE" 2>/dev/null || echo "primary")
primary_fails=$(check_connectivity "$PRIMARY_IF")

if [ "$current_state" = "primary" ] && [ "$primary_fails" -ge "$FAIL_THRESHOLD" ]; then
    logger -t vpn-failover "ISP1 недоступен ($primary_fails/$FAIL_THRESHOLD), переключаемся на ISP2"
    # Обновляем endpoint WireGuard
    for peer_pubkey in $(wg show wg0 peers); do
        current_endpoint=$(wg show wg0 endpoints | grep "$peer_pubkey" | awk '{print $2}')
        wg set wg0 peer "$peer_pubkey" endpoint "$current_endpoint"
    done
    # Переключаем маршрут по умолчанию
    ip route replace default via "$BACKUP_GW" dev "$BACKUP_IF" metric 10
    ip route replace default via "$PRIMARY_GW" dev "$PRIMARY_IF" metric 100
    # Перезапускаем strongSwan с резервным IP
    swanctl --terminate --ike spb 2>/dev/null
    echo "backup" > "$CURRENT_STATE_FILE"
    # Уведомление в Telegram
    curl -s "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
      -d chat_id="${TG_CHAT}" -d text="⚠️ VPN failover: ISP1→ISP2 ($(hostname))"
elif [ "$current_state" = "backup" ] && [ "$primary_fails" -eq 0 ]; then
    logger -t vpn-failover "ISP1 восстановлен, возвращаемся на основной канал"
    ip route replace default via "$PRIMARY_GW" dev "$PRIMARY_IF" metric 10
    ip route replace default via "$BACKUP_GW" dev "$BACKUP_IF" metric 100
    echo "primary" > "$CURRENT_STATE_FILE"
    curl -s "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
      -d chat_id="${TG_CHAT}" -d text="✅ VPN failback: ISP2→ISP1 ($(hostname))"
fi

Скрипт запускается каждые 15 секунд через systemd-таймер. Среднее время переключения — 20-30 секунд, что приемлемо для корпоративного трафика.

QoS для VoIP и мониторинг

Без QoS VoIP-трафик конкурирует с файловыми копированиями и резервным копированием. Мы настроили приоритизацию на уровне tc (traffic control) и iptables:

# Маркировка VoIP-трафика (SIP + RTP)
iptables -t mangle -A FORWARD -p udp --dport 5060:5061 -j DSCP --set-dscp-class EF
iptables -t mangle -A FORWARD -p udp --dport 10000:20000 -j DSCP --set-dscp-class EF

# Настройка tc — HTB с приоритетными классами
tc qdisc add dev wg0 root handle 1: htb default 30
tc class add dev wg0 parent 1: classid 1:1 htb rate 100mbit

# VoIP: гарантия 10 Мбит, приоритет, минимальная задержка
tc class add dev wg0 parent 1:1 classid 1:10 htb rate 10mbit ceil 20mbit prio 1
tc qdisc add dev wg0 parent 1:10 handle 10: sfq perturb 10

# Интерактивный (SSH, HTTP): 30 Мбит
tc class add dev wg0 parent 1:1 classid 1:20 htb rate 30mbit ceil 80mbit prio 2

# Bulk (копирование, бэкапы): остальное
tc class add dev wg0 parent 1:1 classid 1:30 htb rate 60mbit ceil 100mbit prio 3

# Фильтры: DSCP EF → класс VoIP
tc filter add dev wg0 parent 1: protocol ip prio 1 u32 \
  match ip tos 0xb8 0xfc flowid 1:10

Мониторинг VPN-туннелей через Prometheus + blackbox_exporter. Каждые 30 секунд проверяем доступность каждого офиса через ICMP и HTTP:

# /etc/prometheus/blackbox.yml
modules:
  icmp_check:
    prober: icmp
    timeout: 3s
    icmp:
      preferred_ip_protocol: ip4

# prometheus.yml — probe targets
- job_name: 'vpn-tunnels'
  metrics_path: /probe
  params:
    module: [icmp_check]
  static_configs:
    - targets:
      - 10.10.0.1   # Москва
      - 10.20.0.1   # СПб
      - 10.30.0.1   # Казань
      - 10.40.0.1   # Новосибирск
      - 10.50.0.1   # Краснодар
  relabel_configs:
    - source_labels: [__address__]
      target_label: __param_target
    - source_labels: [__param_target]
      target_label: instance
    - target_label: __address__
      replacement: blackbox-exporter:9115

Сравнение с MPLS: стоимость решения — 75 000 ₽/мес (5 вторых ISP) против 180 000 ₽/мес за MPLS. Экономия — 1.26 млн ₽ в год. При этом пропускная способность выросла с 100 Мбит/с до 500 Мбит/с. Подробнее о настройке корпоративных сетей — на itfresh.ru.

Часто задаваемые вопросы

Оптимально — комбинация. IPsec (strongSwan с IKEv2) обеспечивает совместимость с корпоративным оборудованием и проверенную десятилетиями криптографию. WireGuard даёт высокую производительность и простоту настройки mesh-топологии. Мы используем IPsec как основной транспорт, а WireGuard — для прямых межфилиальных соединений.
Три компонента: маркировка трафика (DSCP EF через iptables), приоритизация на уровне tc (HTB с отдельным классом для VoIP, гарантированная полоса), и мониторинг джиттера через blackbox_exporter. Также важно правильно настроить MTU на WireGuard-интерфейсах, чтобы избежать фрагментации.
При нашей конфигурации — 20-30 секунд. Скрипт мониторинга запускается каждые 15 секунд, проверяет доступность 3 хостов через основной интерфейс. При 3 из 3 неудачных проверок переключает маршрут по умолчанию и перезапускает VPN-туннели. OSPF сходится за 15 секунд благодаря настроенным таймерам.
С OSPF через FRR — почти да. Нужно: добавить WireGuard peer на каждом существующем шлюзе (одна команда wg set) и настроить FRR на новом шлюзе. OSPF автоматически распространит маршруты. Полная автоматизация достигается через Ansible-плейбук, который обновляет все конфиги за 2 минуты.
Некоторые приложения (например, кластеры Windows с общими ресурсами) требуют L2-связности — broadcast и ARP между офисами. VXLAN создаёт виртуальный Ethernet-сегмент поверх L3-туннелей WireGuard. Это нужно не всем, но когда нужно — альтернатив мало.

Нужна помощь с проектом?

Специалисты АйТи Фреш помогут с архитектурой, DevOps, безопасностью и разработкой — 15+ лет опыта

📞 Связаться с нами
#VPN#site-to-site#IPsec#WireGuard#strongSwan#OSPF#FRR#VXLAN
Комментарии 0

Оставить комментарий

загрузка...