Настройка системы Linux

Чтобы была возможность раздавать интернет внутри VPN сети, необходимо разрешить перенаправление пакетов внутри ядра Linux. Без этих параметров не получится настроить маршрутизацию.

Открываем файл /etc/sysctl.d/99-custom.conf

nano /etc/sysctl.d/99-custom.conf

Добавляем следующие строки в конец файла (или их можно раскомментировать внутри файла, если они там есть)

net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
#Включить многопутевую маршрутизацию
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.default.rp_filter=0

Выполняем команду применения изменений

sysctl -p

добовляем таблицы маршрутизации

mkdir /etc/iproute2/
touch /etc/iproute2/rt_tables
chmod 644 /etc/iproute2/rt_tables
echo "1001 isp1_table" >> /etc/iproute2/rt_tables
echo "1002 isp2_table" >> /etc/iproute2/rt_tables

Настраиваем сетевые интерфейсы ip адреса получаются автоматически nano /etc/network/interfaces

#isp1
auto ens19
iface ens19 inet dhcp
metric 5

#isp2
auto ens20
iface ens20 inet dhcp
metric 10

post-up ip route add default via 10.0.23.1 dev ens19 table isp1_table
post-up ip route add default via 10.0.17.1 dev ens20 table isp2_table
post-up ip rule add from 10.0.23.1 lookup isp1_table
post-up ip rule add from 10.0.17.1 lookup isp2_table
post-up ip rule add fwmark 0x1 lookup isp1_table
post-up ip rule add fwmark 0x2 lookup isp2_table
post-up ip route add table isp1_table 10.0.23.0/24 dev ens19
post-up ip route add table isp2_table 10.0.17.0/24 dev ens20

настраиваем nftables что бы он отвечал с интерфейса на который пришол пакет

table inet mangle {
    chain prerouting {
        type filter hook prerouting priority mangle; policy accept;

        # Маркировка соединений
        iifname "ens19" ct mark set 1
        iifname "ens20" ct mark set 2

        # Применение маршрутной метки
        ct mark 1 meta mark set 1
        ct mark 2 meta mark set 2
    }

    chain output {
        type filter hook output priority mangle; policy accept;

        # Для WireGuard что бы он шёл через второго провайдера
        udp dport 55625 ip daddr (ip адрес филиала) meta mark set 2
    }
}

table ip nat {
    chain postrouting {
        type nat hook postrouting priority srcnat; policy accept;
        oifname "ens20" masquerade
        oifname "ens19" masquerade
    }
}

Скрипт перелючение каналов

#!/bin/bash

# Основной и резервный шлюзы
GW1=10.0.23.1
GW2=10.0.17.1

# Интерфейсы
iface1=ens19
iface2=ens20

# IP-адреса на интерфейсах (src для пинга)
SRC1=10.0.23.127
SRC2=10.0.17.121

# Адрес для проверки
srctest=8.8.8.8

logfile="/var/log/channel_switch.log"

# Проверка основного канала (по src IP)
result1=$(ping -c 3 -I $SRC1 $srctest -W 1 2>&1 | grep -icE 'unknown|expired|unreachable|time out|100% packet loss')

# Проверка резервного канала (по src IP)
result2=$(ping -c 3 -I $SRC2 $srctest -W 1 2>&1 | grep -icE 'unknown|expired|unreachable|time out|100% packet loss')

# Текущий маршрут по умолчанию
current_gw=$(ip route show default 2>/dev/null | awk '{print $3}')

# --- Логика переключения ---
if [ $result1 -eq 0 ]; then
    # Основной работает
    if [ "$current_gw" != "$GW1" ]; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') — Переключение на основной канал ($GW1)" | tee -a "$logfile"
        ip route del default || true
        ip route add default via $GW1 dev $iface1 src $SRC1 metric 100
        ip route add default via $GW2 dev $iface2 src $SRC2 metric 200
        ip route flush cache
        conntrack -F
    fi
else
    # Основной не работает
    if [ $result2 -eq 0 ]; then
        if [ "$current_gw" != "$GW2" ]; then
            echo "$(date '+%Y-%m-%d %H:%M:%S') — Переключение на резервный канал ($GW2)" | tee -a "$logfile"
            ip route del default || true
            ip route add default via $GW2 dev $iface2 src $SRC2 metric 100
            ip route add default via $GW1 dev $iface1 src $SRC1 metric 200
            ip route flush cache
            conntrack -F
        fi
    else
        echo "$(date '+%Y-%m-%d %H:%M:%S') — ВНИМАНИЕ! Оба канала недоступны" | tee -a "$logfile"
    fi
fi

exit 0




Сделаем запуск скрипта через systemd

Создаем systemd service

nano /etc/systemd/system/check_gatawey.service

[Unit]
Description=Check Gateway Script
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/scripts/check_gatawey.sh
Restart=on-failure
RestartSec=10s
StandardOutput=journal
StandardError=journal
SyslogIdentifier=check_gatawey

[Install]
WantedBy=multi-user.target

Создаем systemd timer

nano /etc/systemd/system/check_gatawey.timer

[Unit]
Description=Run Check Gateway Script every 1 minute

[Timer]
OnBootSec=1min
OnUnitActiveSec=1min
Unit=check_gatawey.service

[Install]
WantedBy=timers.target

Включаем службу

systemctl daemon-reload
systemctl  enable check_gatawey.service

перезапускаем ее

systemctl  restart  check_gatawey.service

Проверка работы

systemctl  status   check_gatawey.service
systemctl list-timers | grep check_gatawey
journalctl -u check_gatawey.service -f

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

Установка WireGuard

На всех узлах выполните:

sudo apt update && sudo apt install wireguard -y

Настройка WireGuard

Пример для Node1 соеденяется через isp1 (10.0.0.1/30):

# /etc/wireguard/wg0.conf
[Interface]
Address = 10.0.0.1/30
PrivateKey = <Node1_private_key>
ListenPort = 51820

[Peer]
PublicKey = <Node2_public_key>
AllowedIPs = 10.0.0.2/32
Endpoint = <Node2_public_IP>:51820
PersistentKeepalive = 25

Пример для соеденяется через isp1 Node2 (10.0.0.2/30):

# /etc/wireguard/wg0.conf
[Interface]
Address = 10.0.0.2/30
PrivateKey = <Node2_private_key>
ListenPort = 51820

[Peer]
PublicKey = <Node1_public_key>
AllowedIPs = 10.0.0.1/32
Endpoint = <Node1_public_IP_isp1>:51820
PersistentKeepalive = 25

Пример для Node1 соеденяется через isp2 (10.0.1.1/30):

# /etc/wireguard/wg1.conf
[Interface]
Address = 10.0.1.1/30
PrivateKey = <Node1_private_key>
ListenPort = 55625

[Peer]
PublicKey = <Node2_public_key>
AllowedIPs = 10.0.1.2/32
Endpoint = <Node2_public_IP>:55625
PersistentKeepalive = 25

Пример для соеденяется через isp2 Node2 (10.0.1.2/30):

# /etc/wireguard/wg1.conf
[Interface]
Address = 10.0.0.2/30
PrivateKey = <Node2_private_key>
ListenPort = 55625

[Peer]
PublicKey = <Node1_public_key>
AllowedIPs = 10.0.0.1/32
Endpoint = <Node1_public_IP_isp2>:55625
PersistentKeepalive = 25

Запуск WireGuard

На всех узлах:

sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0

sudo systemctl enable wg-quick@wg1
sudo systemctl start wg-quick@wg1

Создание очередей на интерфейсах wg (при необходимости)

Надо добавть в конфигкрацию wireguar в секцию [Interface] PostUp и PostDown

PostUp = /etc/wireguard/wg0_tc.sh
PostDown = tc qdisc del dev wg0 root

Сам скрипт /etc/wireguard/wg0_tc.sh

#!/bin/bash
# Настройка cake для wg0 (egress + ingress)
WG_IF="wg0"
WG_SPEED="185mbit"  # внутренняя скорость в туннеле
IFB_ID="ifb1"       # виртуальный интерфейс для входящего трафика

set -e

# Определяем MTU интерфейса wg0
WG_MTU=$(ip link show dev $WG_IF | awk '/mtu [0-9]+/ {print $5}')
if [ -z "$WG_MTU" ]; then
    echo "Ошибка: не удалось определить MTU для интерфейса $WG_IF"
    exit 1
fi

# Рассчитываем оптимальный r2q для HTB
# r2q = rate / (10 * MTU). MTU в битах = MTU * 8
R2Q=$((185000000 / (10 * $WG_MTU * 8)))
R2Q=${R2Q:-10}  # значение по умолчанию, если расчет не удался

# Ограничиваем r2q разумными пределами (1-65535)
if [ $R2Q -lt 1 ]; then
    R2Q=1
elif [ $R2Q -gt 65535 ]; then
    R2Q=65535
fi

# Создаём ifb для входящего трафика
ip link del $IFB_ID 2>/dev/null || true
modprobe ifb numifbs=2
ip link add $IFB_ID type ifb 2>/dev/null || true

# Устанавливаем такой же MTU для IFB, как у WG_IF
ip link set dev $IFB_ID mtu $WG_MTU
ip link set dev $IFB_ID up

# Чистим старые правила
tc qdisc del dev $WG_IF root 2>/dev/null || true
tc qdisc del dev $WG_IF ingress 2>/dev/null || true
tc qdisc del dev $IFB_ID root 2>/dev/null || true

# ===== Исходящий трафик wg0 =====
tc qdisc add dev $WG_IF root handle 1: htb r2q $R2Q
tc class add dev $WG_IF parent 1: classid 1:1 htb rate $WG_SPEED ceil $WG_SPEED
tc class add dev $WG_IF parent 1:1 classid 1:10 htb rate $WG_SPEED ceil $WG_SPEED
tc qdisc add dev $WG_IF parent 1:10 cake

# ===== Входящий трафик wg0 =====
tc qdisc add dev $WG_IF handle ffff: ingress
tc filter add dev $WG_IF parent ffff: protocol all u32 match u32 0 0 \
    action mirred egress redirect dev $IFB_ID

tc qdisc add dev $IFB_ID root handle 1: htb r2q $R2Q
tc class add dev $IFB_ID parent 1: classid 1:1 htb rate $WG_SPEED ceil $WG_SPEED
tc class add dev $IFB_ID parent 1:1 classid 1:10 htb rate $WG_SPEED ceil $WG_SPEED
tc qdisc add dev $IFB_ID parent 1:10 cake

echo "Настройка завершена. Использован MTU: $WG_MTU, r2q: $R2Q"

Установка FRRouting (OSPF)

На всех узлах:

sudo apt install frr -y

Включите OSPF в /etc/frr/daemons:

ospfd=yes

Перезапустите FRR:

sudo systemctl restart frr

Настройка OSPF

Используйте vtysh для настройки OSPF.

Пример через провайдера isp1 для Node1:

vtysh

configure terminal
interface wg0
 ip ospf cost 1000
 ip ospf dead-interval 40
exit
router ospf
 network 10.0.0.0/24 area 0  # Туннельная сеть
 network <LAN_Node1_подсеть> area 0  # Например, 192.168.1.0/24
exit
exit
write

Пример через провайдера isp1 для Node2:

vtysh

configure terminal
interface wg0
 ip ospf cost 1000
 ip ospf dead-interval 40
exit
router ospf
 network 10.0.0.0/24 area 0  # Туннельная сеть
 network <LAN_Node2_подсеть> area 0  # Например, 192.168.2.0/24
exit
exit
write

Пример через провайдера isp2 для Node1:

vtysh

configure terminal
interface wg1
 ip ospf cost 2000
 ip ospf dead-interval 40
exit
router ospf
 network 10.0.0.0/24 area 0  # Туннельная сеть
 network <LAN_Node1_подсеть> area 0  # Например, 192.168.1.0/24
exit
exit
write

Пример через провайдера isp2 для Node2:

vtysh

configure terminal
interface wg1
 ip ospf cost 2000
 ip ospf dead-interval 40
exit
router ospf
 network 10.0.0.0/24 area 0  # Туннельная сеть
 network <LAN_Node2_подсеть> area 0  # Например, 192.168.2.0/24
exit
exit
write

Проверка OSPF

Проверьте соседей:

vtysh -c "show ip ospf neighbor"

Посмотрите таблицу маршрутизации:

vtysh -c "show ip route ospf"