// DevOps

Направляем трафик из локальной подсети через удалённый сервер (IPIP + Policy Routing)

Опубликовано 22.09.2026

Руководство показывает, как настроить два Linux-сервера так, чтобы весь интернет-трафик из определённой локальной подсети (например, 10.100.10.0/24) уходил не через её обычный шлюз, а через IPIP-туннель на удалённый сервер, который выпускает его в интернет.

Схема нужна, когда сервисы одной подсети должны выходить в интернет с IP-адреса другого сервера: для централизованного NAT, работы с партнёрами по белому списку адресов или единой точки выхода для нескольких площадок. Остальной трафик самого шлюза при этом идёт обычным путём.

IPIP не шифрует трафик. Если туннель проходит через интернет и данные нужно защищать, используйте WireGuard или IPsec — о вариантах связи между площадками см. статью «Резервирование каналов связи: между офисами».


Исходные данные (условные адреса)

РольИмяВнешний (WAN) IPИнтерфейс WANАдрес в туннелеПримечание
Сервер АЛокальный шлюз198.51.100.10eth010.254.0.1/30Шлюз подсети 10.100.10.0/24
Сервер БУдалённый узел выхода203.0.113.20eth010.254.0.2/30Выпускает трафик в интернет
  • Подсеть для маршрутизации: 10.100.10.0/24
  • Имя туннеля: ipip0
  • Сеть туннеля: 10.254.0.0/30

Шаг 1: Настройка сервера Б (узел выхода)

Задача: принять IPIP-туннель от сервера А и выпускать трафик подсети 10.100.10.0/24 в интернет через NAT с адресом 203.0.113.20.

1.1. Включаем пересылку пакетов

bash
sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward = 1" > /etc/sysctl.d/90-ipip-forward.conf

1.2. Туннель, обратный маршрут, межсетевой экран и NAT

bash
#!/bin/sh
set -e

TUN_NAME="ipip0"
REMOTE_IP="198.51.100.10"
LOCAL_IP="203.0.113.20"
TUN_NET="10.254.0.2/30"
TUN_PEER="10.254.0.1"
WAN_IFACE="eth0"
CLIENT_NET="10.100.10.0/24"

echo "[*] Создаю туннель $TUN_NAME"
ip tunnel del "$TUN_NAME" 2>/dev/null || true
ip tunnel add "$TUN_NAME" mode ipip remote "$REMOTE_IP" local "$LOCAL_IP" ttl 64
ip addr add "$TUN_NET" dev "$TUN_NAME"
ip link set "$TUN_NAME" up mtu 1480

echo "[*] Обратный маршрут к подсети клиентов через туннель"
ip route replace "$CLIENT_NET" via "$TUN_PEER" dev "$TUN_NAME"

echo "[*] Настраиваю iptables"
iptables -I INPUT -p 4 -s "$REMOTE_IP" -j ACCEPT
iptables -A FORWARD -i "$TUN_NAME" -s "$CLIENT_NET" -o "$WAN_IFACE" -j ACCEPT
iptables -A FORWARD -i "$WAN_IFACE" -d "$CLIENT_NET" -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

iptables -t nat -A POSTROUTING -s "$CLIENT_NET" -o "$WAN_IFACE" -j SNAT --to-source "$LOCAL_IP"

iptables -t mangle -A FORWARD -o "$WAN_IFACE" -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

echo "[OK] Сервер Б готов."

Обратный маршрут обязателен: после обратного преобразования адресов ответы адресованы машинам подсети 10.100.10.0/24, и без этого маршрута сервер Б отправит их в интернет, а не в туннель.

IPIP добавляет к каждому пакету 20 байт заголовка, поэтому MTU туннеля — 1480. Правило TCPMSS --clamp-mss-to-pmtu уменьшает размер TCP-сегментов под этот MTU, чтобы соединения не зависали из-за проблем с PMTU Discovery.


Шаг 2: Настройка сервера А (локальный шлюз)

Задача: направлять трафик подсети 10.100.10.0/24, идущий в интернет, в туннель к серверу Б.

2.1. Пересылка пакетов и rp_filter

bash
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv4.conf.all.rp_filter=2
sysctl -w net.ipv4.conf.default.rp_filter=2

cat <<EOF > /etc/sysctl.d/90-ipip-gw.conf
net.ipv4.ip_forward = 1
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2
EOF

Режим rp_filter=2 (loose) нужен, потому что маршрутизация здесь асимметричная: ответы из интернета приходят через туннель, а не через интерфейс, в который смотрит маршрут по умолчанию. Ядро применяет максимальное из значений all и интерфейса — почему так и как это проверить, разобрано в статье «Что такое rp_filter».


2.2. Создаём IPIP-туннель

bash
#!/bin/sh
set -e

TUN_NAME="ipip0"
REMOTE_IP="203.0.113.20"
LOCAL_IP="198.51.100.10"
TUN_NET="10.254.0.1/30"
TUN_PEER="10.254.0.2"

echo "[*] Поднимаю туннель $TUN_NAME"
ip tunnel del "$TUN_NAME" 2>/dev/null || true
ip tunnel add "$TUN_NAME" mode ipip remote "$REMOTE_IP" local "$LOCAL_IP" ttl 64
ip addr add "$TUN_NET" dev "$TUN_NAME"
ip link set "$TUN_NAME" up mtu 1480

echo "[OK] Туннель поднят."

2.3. Policy routing

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

bash
grep -q "100[[:space:]]tunnel_route" /etc/iproute2/rt_tables || echo "100 tunnel_route" >> /etc/iproute2/rt_tables

Если файла /etc/iproute2/rt_tables в системе нет, создайте его вручную либо используйте в командах номер таблицы 100 вместо имени.

Добавляем маршрут по умолчанию в эту таблицу

bash
ip route add default via 10.254.0.2 dev ipip0 table tunnel_route

Создаём правило

bash
ip rule add from 10.100.10.0/24 table tunnel_route pref 1000
ip route flush cache

Правило отправляет в туннель весь трафик подсети, включая обращения к другим локальным сетям. Если подсеть должна обращаться к соседним сетям напрямую, добавьте перед ним правило с меньшим номером, например ip rule add from 10.100.10.0/24 to 10.0.0.0/8 table main pref 900.


2.4. Межсетевой экран

bash
CLIENT_NET="10.100.10.0/24"
TUN_NAME="ipip0"

iptables -A FORWARD -s "$CLIENT_NET" -o "$TUN_NAME" -j ACCEPT
iptables -A FORWARD -i "$TUN_NAME" -d "$CLIENT_NET" -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -t mangle -A FORWARD -o "$TUN_NAME" -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

echo "[OK] Межсетевой экран и policy routing настроены."

Шаг 3: Проверка

Маршрут для трафика самого сервера А

bash
ip route get 8.8.8.8

Ожидается обычный маршрут:

8.8.8.8 via <ваш_шлюз> dev eth0 src 198.51.100.10 ...

Маршрут для пакета из подсети

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

bash
ip route get 8.8.8.8 from 10.100.10.50 iif eth1

Здесь eth1 — интерфейс сервера А в сторону подсети 10.100.10.0/24. Ожидается маршрут через туннель:

8.8.8.8 from 10.100.10.50 via 10.254.0.2 dev ipip0 ...

Диагностика

1. Туннель

bash
ip addr show dev ipip0
ip -s tunnel show
ping -I ipip0 10.254.0.2

Если ping не проходит, проверьте, что межсетевые экраны обоих серверов и провайдеров пропускают протокол IPIP (IP-протокол 4).


2. Policy routing

bash
ip rule show
ip route show table tunnel_route

В выводе ip rule show должна быть строка:

1000: from 10.100.10.0/24 lookup tunnel_route

3. NAT на сервере Б

bash
iptables -t nat -L POSTROUTING -v -n

При активном трафике счётчики правила SNAT должны расти.


4. Внешний адрес для машин подсети

На любой машине из подсети 10.100.10.0/24:

bash
curl -4 ifconfig.me

Ожидается 203.0.113.20.


5. MTU и MSS

Если TCP-соединения зависают:

bash
ip link show ipip0
ping -M do -s 1452 8.8.8.8

Пакет в 1452 байта данных плюс 28 байт заголовков ICMP и IP как раз укладывается в MTU туннеля 1480. Проверьте, что у туннеля mtu 1480 и что правило --clamp-mss-to-pmtu на месте.


6. tcpdump

bash
tcpdump -ni ipip0
tcpdump -ni eth0 host 8.8.8.8

7. Временное журналирование

bash
iptables -I FORWARD 1 -j LOG --log-prefix "[FORWARD] "
tail -f /var/log/kern.log

После диагностики удалите это правило: оно пишет в журнал каждый пересылаемый пакет.


8. Обратный путь

С машины в подсети:

bash
traceroute 8.8.8.8

Первым хопом должен быть сервер А, следующим — адрес сервера Б в туннеле 10.254.0.2. С сервера Б:

bash
traceroute -s 10.254.0.2 10.100.10.10

Если трассировка с сервера Б уходит в интернет, а не в туннель, проверьте обратный маршрут из шага 1.2.


9. Сохранение настроек

Команды выше действуют до перезагрузки. Правила iptables сохраняются так (в Debian и Ubuntu — пакет iptables-persistent):

bash
iptables-save > /etc/iptables/rules.v4

Туннель, маршруты и правила policy routing после перезагрузки нужно создавать заново: удобнее всего оформить скрипты из шагов 1.2, 2.2 и 2.3 в службу systemd. Вывод ip rule show, сохранённый в файл, сам по себе ничего не восстанавливает.


Дополнительные рекомендации

ПунктРекомендация
MTU1480 для туннеля и TCPMSS clamp на обоих серверах
Межсетевой экранНа новых системах удобнее nftables
АвтозапускЗапуск туннеля и маршрутов через systemd
Мониторингtcpdump -i ipip0 показывает трафик в туннеле
БезопасностьРазрешайте IPIP только с адреса второго сервера; для шифрования — WireGuard или IPsec

Похожая задача на MikroTik — вернуть ответ через тот же шлюз, через который пришёл запрос, — разобрана в статье «MikroTik: возврат трафика через тот же шлюз».


Итог

Весь интернет-трафик подсети 10.100.10.0/24 выходит в интернет с адреса сервера Б (203.0.113.20), а сервер А продолжает использовать свой обычный маршрут. Для работы схемы нужны три вещи: туннель с правильным MTU, правило policy routing на сервере А и обратный маршрут к подсети на сервере Б.

// Contact

Нужна помощь?

Свяжись со мной и я помогу решить проблему

Написать в Telegram

Отвечаю в течение рабочего дня (03:00–13:00 GMT)

Или оставьте заявку здесь:

Подтвердите, что вы не бот.

Написать и получить быстрый ответ