Policy-Based Routing (PBR) в Linux: Настройка изоляции трафика Docker и приложений через VPN | AdminWiki
Timeweb Cloud — сервера, Kubernetes, S3, Terraform. Лучшие цены IaaS.
Попробовать

Policy-Based Routing (PBR) в Linux: Настройка изоляции трафика Docker и приложений через VPN

03 апреля 2026 11 мин. чтения

Policy-Based Routing (PBR) — это механизм маршрутизации в Linux, который позволяет принимать решения о пути следования пакетов не только на основе адреса назначения, но и по другим критериям: исходному IP-адресу, порту, интерфейсу или специальной метке (fwmark). Это ключевой инструмент для решения задач, где стандартной маршрутизации через один шлюз по умолчанию недостаточно. В этой статье вы получите пошаговую инструкцию по настройке PBR для принудительного направления трафика от конкретных Docker-контейнеров, системных сервисов или приложений через выделенный сетевой интерфейс, например, VPN-туннель WireGuard или OpenVPN. Мы разберем работу с цепочкой ip rule, ip route, iptables/nftables и маркировкой пакетов, а также методы проверки и обеспечения надежности конфигурации.

Зачем нужна маршрутизация на основе политик (PBR) и в чем её сила

Стандартная маршрутизация в Linux использует таблицу маршрутизации (чаще всего основную, main), где для каждого адреса назначения определен следующий хоп (шлюз). Когда у системы один интернет-канал, этого достаточно. Проблемы начинаются, когда появляется второй канал (например, VPN-туннель) и возникает потребность в гибком разделении трафика: один сервис должен общаться через VPN, а остальные — через основной канал. Именно здесь PBR перестает быть абстракцией и становится практическим инструментом для решения конкретных рабочих задач, связанных с безопасностью, compliance и построением сложных сетевых архитектур.

Типичные задачи, которые решает PBR: от безопасности до обхода блокировок

PBR позволяет реализовать сценарии, которые сложно или невозможно осуществить другими методами:

  • Обеспечение compliance: Весь трафик определенного критичного сервиса (например, системы бэкапов в S3-совместимое облако) должен идти исключительно через корпоративный VPN для шифрования и аудита.
  • Гео-таргетинг и обход блокировок для отдельных приложений: Контейнер с веб-скрапером или медиа-клиентом должен получать данные через VPN-сервер в определенной стране, в то время как остальной трафик сервера идет напрямую. Учитывая, что, по данным из открытых источников, до 90% бесплатных VPN-клиентов могут блокироваться провайдерами в короткие сроки, важно направлять через VPN только целевой трафик, минимизируя риски.
  • Повышение безопасности: Изоляция «грязного» или подозрительного трафика (например, от honeypot-системы или sandbox-окружения) в отдельный, контролируемый туннель для анализа.
  • Оптимизация качества обслуживания (QoS): Маршрутизация чувствительного к задержкам трафика (VoIP, видеоконференции по UDP) через низколатентный или более стабильный канал.
  • Разделение трафика управления и данных: Трафик для администрирования сервера (SSH, мониторинг) идет через основную, защищенную сеть, а пользовательский трафик — через выделенный канал.

PBR против других методов: когда выбирать именно этот подход

Прежде чем погружаться в настройку, важно понять, почему PBR может быть оптимальнее альтернатив. Вот краткое сравнение:

  • VPN Kill-Switch: Пассивная защита, которая лишь блокирует весь трафик при падении туннеля. Не дает гибкости для маршрутизации только части трафика.
  • Прокси (SOCKS5/HTTP): Решение на уровне приложения. Подходит для программ с явной поддержкой прокси (браузеры, некоторые CLI-утилиты). Однако многие системные сервисы, Docker-демон или низкоуровневые TCP/UDP-соединения не работают через прокси. Хотя современный SOCKS5 и поддерживает UDP (что важно для стриминга или игр), его настройка не является прозрачной для всей системы.
  • Сетевые неймспейсы (ip netns): Обеспечивают максимальную изоляцию на уровне ядра, создавая виртуальную копию стека сетевых интерфейсов, таблиц маршрутизации и правил. Мощный, но более сложный в настройке и интеграции с существующими приложениями (требует запуска процессов в конкретном неймспейсе).
  • Policy-Based Routing (PBR): Работает на уровне ядра (netfilter, iproute2), прозрачно для приложений. Позволяет гибко маршрутизировать трафик на основе множества критериев (IP, порт, интерфейс, пользователь) через отдельные таблицы маршрутизации. Оптимален, когда требуется стабильная, гибкая и прозрачная маршрутизация части трафика без изоляции всего процесса.

Вывод: Если вам нужно заставить определенный трафик (по любому критерию) всегда идти через конкретный интерфейс (VPN), и при этом все должно работать «из коробки» для любых приложений, PBR — ваш выбор.

Архитектура решения: как Linux заставляет пакеты идти нужным путём

Чтобы понимать, какие команды вы вводите, нужно представлять себе общую схему работы PBR в Linux. Это не магия, а четко определенный конвейер обработки пакетов.

Принцип работы можно описать следующей цепочкой:

  1. Приложение отправляет пакет (например, запрос на api.external-service.com).
  2. Маркировка пакета (fwmark): Правило в iptables или nftables (чаще всего в цепочке OUTPUT или PREROUTING) «помечает» этот пакет, основываясь на заданном критерии (исходный IP контейнера, порт и т.д.). Метка — это просто число (например, 100 или 0x64 в hex), хранящееся в метаданных пакета.
  3. Правило (ip rule): Ядро проверяет набор правил политик маршрутизации. Одно из правил гласит: «Если у пакета есть метка 0x64, ищи маршрут для него в таблице с номером 100».
  4. Таблица маршрутизации (ip route table 100): Это отдельная, изолированная «карта дорог». В ней, скорее всего, прописан маршрут по умолчанию (default via 10.8.0.1 dev tun0), ведущий через VPN-интерфейс, а не через основной шлюз.
  5. Сетевой интерфейс: Пакет отправляется через указанный в таблице интерфейс (tun0, wg0 и т.д.).

Ключевые концепции:

  • fwmark (firewall mark) — метка пакета, которую ставит iptables/nftables.
  • ip rule — правило выбора таблицы маршрутизации на основе метки, исходного адреса или других атрибутов.
  • ip route table <N> — отдельный набор маршрутов, существующий независимо от основной таблицы (main).

Важное замечание о DNS: Маршрутизация работает на сетевом уровне (IP). Если приложение, трафик которого направлен через VPN, будет использовать DNS-серверы из основной системы (прописанные в /etc/resolv.conf), DNS-запросы пойдут через основной канал, что приведет к утечке информации о ваших запросах. Поэтому настройка DNS для изолированного трафика — обязательный шаг.

Пошаговая настройка PBR для изоляции трафика через VPN

Перейдем к практике. Предполагается, что у вас уже настроен и работает VPN-клиент (например, WireGuard или OpenVPN), создавший интерфейс (например, wg0 или tun0). Вам известен его IP-адрес и шлюз (gateway).

Шаг 0: Проверка текущей конфигурации

Перед началом работы посмотрите на текущие маршруты и правила:

ip route show
ip rule list

Создание и настройка отдельной таблицы маршрутизации для VPN

Сначала создадим «альтернативную карту дорог» для нашего изолированного трафика. Для этого нужно дать имя новой таблице маршрутизации, отредактировав файл /etc/iproute2/rt_tables.

echo "100 custom_vpn" >> /etc/iproute2/rt_tables

Теперь добавим маршрут по умолчанию в эту таблицу, который будет указывать на шлюз VPN-интерфейса. Замените 10.8.0.1 и wg0 на актуальные для вас значения.

ip route add default via 10.8.0.1 dev wg0 table custom_vpn

Важно: Если ваш VPN-сервер находится в локальной сети и доступен через основной интерфейс (например, eth0), нужно добавить в таблицу custom_vpn явный маршрут до сервера VPN через основной шлюз, иначе установить соединение с самим сервером для изолированного трафика не получится. Это предотвратит проблему с асимметричной маршрутизацией и reverse path filtering.

# Пример: VPN-сервер имеет IP 192.168.1.254
ip route add 192.168.1.254/32 dev eth0 via 192.168.1.1 table custom_vpn

Маркировка пакетов: от простого правила к сложной логике

Теперь научим систему отличать «особый» трафик, который нужно направить в новую таблицу. Делается это с помощью маркировки пакетов в iptables (или nftables).

Сначала создадим правило, которое будет смотреть на метку 0x64 (100 в десятичной системе) и направлять пакет на поиск маршрута в таблицу custom_vpn.

ip rule add fwmark 0x64 table custom_vpn

Теперь настроим iptables для маркировки. Вот несколько примеров для разных критериев (выберите подходящий):

Пример 1: Маркировка по исходному IP-адресу (подходит, если у приложения или контейнера фиксированный IP).

iptables -t mangle -A OUTPUT -s 172.20.0.10 -j MARK --set-mark 0x64

Пример 2: Маркировка по исходной подсети (удобно для целой Docker-сети).

iptables -t mangle -A OUTPUT -s 172.20.0.0/24 -j MARK --set-mark 0x64

Пример 3: Маркировка по пользователю (UID) (если приложение запущено от определенного пользователя).

iptables -t mangle -A OUTPUT -m owner --uid-owner 1001 -j MARK --set-mark 0x64

Правила добавляются в таблицу mangle, которая предназначена для модификации пакетов. Цепочка OUTPUT перехватывает пакеты, исходящие с самого хоста.

Для nftables эквивалентное правило будет выглядеть так (для подсети):

nft add rule ip mangle OUTPUT ip saddr 172.20.0.0/24 meta mark set 0x64

Шаг 4: Настройка DNS для помеченного трафика

Чтобы DNS-запросы от изолированного трафика тоже шли через VPN, их нужно либо маркировать аналогичным образом (если они исходят от того же источника), либо явно настроить DNS-серверы для процессов, использующих изолированный маршрут. Самый надежный способ — использовать systemd-resolved и привязать DNS-серверы к конкретному интерфейсу:

resolvectl dns wg0 1.1.1.1 8.8.8.8

Если приложение, чей трафик маркируется, использует стандартный механизм разрешения имен, запросы теперь будут отправляться через интерфейс wg0.

PBR для Docker-контейнеров: изоляция трафика контейнера в VPN

Один из самых востребованных сценариев — направить трафик конкретного Docker-контейнера через VPN, не затрагивая другие контейнеры и хост-систему. Проблема в том, что трафик контейнера по умолчанию выходит через bridge-интерфейс Docker (docker0 или пользовательский bridge), и маркировка по IP хоста не сработает.

Настройка отдельной Docker-сети и привязка правил маршрутизации

Наиболее простое и эффективное решение — создать для контейнера отдельную сеть Docker с известной нам подсетью, а затем маркировать весь трафик, исходящий с этой подсети.

1. Создаем пользовательскую сеть Docker с фиксированной подсетью:

docker network create --subnet=172.20.0.0/24 --opt com.docker.network.bridge.name=br-vpn vpn_net

Опция --opt задает имя bridge-интерфейса на хосте для удобства.

2. Запускаем контейнер в этой сети, при желании зафиксировав за ним IP-адрес:

docker run -d --name my-vpn-container \
  --network=vpn_net \
  --ip=172.20.0.2 \
  nginx:alpine

3. Теперь добавляем правило iptables для маркировки всего трафика с подсети 172.20.0.0/24. Важно: так как трафик из контейнера сначала попадает на bridge-интерфейс, а затем на хост, правило нужно применять в цепочке PREROUTING таблицы mangle (или FORWARD, если контейнер взаимодействует с внешним миром через NAT).

iptables -t mangle -A PREROUTING -s 172.20.0.0/24 -j MARK --set-mark 0x64

Это правило будет маркировать пакеты еще до того, как они попадут в цепочку OUTPUT хоста, что гарантирует их обработку нашим правилом ip rule.

Для более глубокой настройки сети Docker, включая создание пользовательских мостов и управление статическими IP, обратитесь к нашему полному руководству по пользовательским Docker-сетям.

Проверка работы и диагностика проблем

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

Как убедиться, что трафик идет через VPN, а не основной канал

Самый простой способ — проверить внешний IP-адрес, с которого видны запросы из изолированного окружения.

Для теста с хоста можно использовать curl с указанием маркировки (требуются права root):

curl --max-time 5 --interface wg0 ifconfig.me  # Должен показать IP VPN
curl --max-time 5 ifconfig.me                   # Должен показать основной IP

Более точный метод — использовать команду ip route get с указанием метки. Она покажет, какой маршрут и через какой интерфейс будет выбран для целевого адреса с данной меткой.

ip route get 8.8.8.8 mark 0x64

Для прямого наблюдения за трафиком в туннеле используйте tcpdump:

tcpdump -i wg0 -n -c 5

Запустите в изолированном контейнере команду (например, apt update или curl ifconfig.me) и убедитесь, что пакеты проходят через интерфейс wg0.

Тестирование на утечку DNS: обязательный этап

Утечка DNS — одна из самых частых и критичных проблем. Если DNS-запросы уходят через основной канал, вся конфиденциальность, ради которой настраивался VPN, сводится на нет.

1. Проверка из контейнера: Зайдите в контейнер, работающий через изолированную сеть, и выполните:

docker exec my-vpn-container apk add bind-tools  # Установка dig для Alpine
docker exec my-vpn-container dig +short TXT whoami.ds.akahelp.net

Эта команда вернет IP-адрес DNS-резолвера, который использовался для запроса. Он должен принадлежать вашему VPN-провайдеру или указанному вами DNS-серверу (например, 1.1.1.1), но не вашему основному провайдеру.

2. Использование онлайн-сервисов: Запустите в изолированном контейнере браузер в текстовом режиме (lynx или curl) и откройте страницу типа dnsleaktest.com. Результат должен показывать серверы вашего VPN, а не локального провайдера.

Обеспечение постоянства конфигурации (Persistence)

Настройки, выполненные командами ip и iptables, сбрасываются после перезагрузки. Для production-среды это недопустимо.

  • Правила и маршруты (ip rule, ip route):
    Для систем с netplan (Ubuntu 18.04+): Добавьте конфигурацию в файл /etc/netplan/99-pbr.yaml.
    Для систем с /etc/network/interfaces (Debian): Создайте скрипт в /etc/network/if-up.d/, который будет применять правила при поднятии интерфейса.
    Универсальный способ: Создать systemd-сервис с зависимостью от сетевого таргета и VPN-интерфейса.
  • Правила iptables: Используйте пакет iptables-persistent (Debian/Ubuntu) или сохраните правила вручную:
    iptables-save > /etc/iptables/rules.v4
    ip6tables-save > /etc/iptables/rules.v6
  • Правила nftables: Сохраните текущий набор правил в конфигурационный файл:
    nft list ruleset > /etc/nftables.conf
  • Docker-сети: Помните, что пользовательские сети Docker не сохраняются после перезагрузки демона Docker. Их нужно создавать заново при старте системы, например, через тот же systemd-сервис или скрипт инициализации.

Для комплексного подхода к управлению Docker в production, включая надежный деплой и мониторинг, изучите наше полное руководство по Docker в production.

Доводка безопасности: предотвращаем утечки при падении VPN

Базовая настройка PBR направляет трафик через VPN, но что произойдет, если туннель внезапно упадет? В этом случае маршрут по умолчанию в таблице custom_vpn станет недостижимым, и ядро может отправить помеченные пакеты по основному маршруту (утечка). Чтобы этого не произошло, нужно добавить второй уровень защиты — фаервол.

Добавьте правило в iptables, которое будет отбрасывать весь помеченный трафик, если он попытается выйти не через VPN-интерфейс. Это правило ставится в таблице filter, цепочке OUTPUT (или FORWARD для контейнеров).

iptables -A OUTPUT -m mark --mark 0x64 ! -o wg0 -j DROP

Это правило гласит: «Все пакеты с меткой 0x64, которые пытаются выйти через интерфейс, отличный от wg0, должны быть отброшены». Таким образом, даже если маршрут через wg0 исчезнет, трафик не «вытечет» в открытую сеть, а будет заблокирован.

Дополнительно рекомендуется использовать стабильные и современные протоколы VPN, такие как WireGuard, которые меньше подвержены нестабильности по сравнению с некоторыми реализациями OpenVPN. Это минимизирует риск самого факта падения туннеля.

Итоговая архитектура безопасности получается двухуровневой: 1) PBR направляет трафик по нужному пути, 2) Фаервол блокирует любые попытки этого трафика пойти другим путем.

Поделиться:
Сохранить гайд? В закладки браузера