Linux. Маршрутизация некоторых сайтов через VPN.

Уровень сложности материала — средний.

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

У нас есть Linux сервер, который, помимо прочего, функционирует в качестве маршрутизатора. Весьма распространенная ситуация, ее и будем рассматривать, как пример. На схеме изображена типичная структура сети основного офиса и филиала:

Задача настроить Linux роутер таким образом, чтобы обращения к сайту example.org шли через VPN.

Нам понадобятся:

  1. Dnsmasq — рекурсивный кэширующий DNS сервер для обслуживания запросов клиентов (сотрудников)
  2. ipset — утилита и поддержка одноименного модуля в ядре
  3. iptables – утилита для конфигурирования netfilter, c поддержкой ipset
  4. iproute2 – пакет для расширенного управления маршрутизацией и QoS

Будем исходить из того, что VPN соединение уже настроено; при подключении создается устройство соответствующего типа (ppp или tun).

Логически решение выглядит так:

  • Сотрудник набирает адрес example.org в браузере
  • Dnsmasq «видит» домен в конфигурационном файле, резолвит, как обычно, + заносит IP адрес(-а) в таблицу ipset с названием VIAVPN
  • Правилом iptables, пакеты с соответствующим «тэгом» ipset — VIAVPN, помечаются меткой 1 (—set-mark 1)
  • В таблице маршрутизации, все пакеты с меткой 1, отправляются через интерфейс VPN

Рассказывать как установить пакеты на вашей версии Linux я не буду, надеюсь вы уже можете это сделать самостоятельно. Тем более дистрибутивы разные, и приводить в качестве примера какой-то конкретный, считаю неправильным.

Для начала настроим dnsmasq.

В первую очередь, нас интересуют вышестоящие (upstream) DNS серверы, к которым будут направляться запросы. Сам по себе, dnsmasq является, своего рода, прокси сервером. По умолчанию данная информация берется из системных настроек, файла resolv.conf. Однако можно указать альтернативный файл с помощью строки (dnsmasq.conf)

resolv-file=/etc/resolv.dnsmasq

Укажем список доменов для занесения соответствующих им IP адресов в ipset list с именем VIAVPN

ipset=/example.org/example2.com/example3.edu/VIAVPN

Через разделитель «/» можно указать любое количество доменов. Обращаю внимание, что все субдомены тоже попадут в ipset. В конце строки указывается название листа, в нашем случае VIAVPN.

Теперь самое время завести тот самый список VIAVPN

# ipset create VIAVPN hash:ip family inet

Указывается тип списка – ip адреса со структурой хранения hash. То есть, с очень быстрым, фактически мгновенным, поиском IP адреса в огромном списке. Именно по этой причине используется ipset, а не напрямую правила iptables. Family inet говорит об адресах протокола IPv4.

Правило iptables, которое будет маркировать пакеты, к IP адресам доменов (то есть с пометкой VIAVPN), маркером 1. По сути, мы меняем одну метку на другую, но беда в том, что селкторы ip rule не могут работать с ipset, лишь с fwmark. Данное правило как раз служит исключительно для этого:

# iptables -I PREROUTING -t mangle -m set --match-set VIAVPN dst -j MARK --set-mark 1

Команда не требует каких-то особых разъяснений, всё достаточно очевидно. Единственное, dst говорит о направлении трафика. То есть, извлекаем из пакета адрес назначения и проверяем его наличие в списке VIAVPN. Если присутствует, помечаем такой пакет меткой 1.

Переходим к маршрутизации

Ну, первое, конечно, заводим отдельную таблицу, назовем ее vpnrouting. Идем в файлик /etc/iproute2/rt_tables и добавляем туда строку

252 vpnrouting

ID может быть любым свободным положительным числом до 255

Добавляем маршрут по умолчанию (default gateway) в созданную таблицу. Кроме него, в нашем случае, больше ничего не понадобится, однако, сюда можно добавить другие, более точные маршруты при необходимости

# ip route add table vpnrouting default dev tun0

Поскольку, интерфейс VPN, как правило, point-to-point — можно указать вместо адреса соседнего маршрутизатора, устройство, с которого следует отправлять пакеты. Что мы и сделали.

Почти всё готово! Осталось указать, в какой таблице искать маршруты для пакетов с меткой (fwmark) 1. Если добавлять маршруты через упрощенную утилиту route, то они, на самом деле, прописываются, в таблицу main. А правило для всех пакетов, по умолчанию, ищет маршруты именно в ней.

Выведем список всех правил поиска маршрута для наглядности:

# ip rule show
0: from all lookup local
32766: from all lookup main
32767: from all lookup default

Тут всё просто, число слева — это приоритет. Для поступившего пакета ищется маршрут, начиная с наименьшего, по возрастанию. Сначала, в таблице local, там содержится информация о широковещании и адреса локальных интерфейсов. Таким образом ядро распознает широковещательные пакеты, а так же те, которые адресованы данной машине. Если маршрут для пакета не найден, проверяется следующая таблица – main. В ней всё то, что мы видим по команде «route –n». В том числе маршрут по умолчанию.

Задача указать правило поиска ДО таблицы main. По умолчанию, оно будет добавлено с приоритетом 32765. Нам это вполне подходит (хотя можем указать приоритет явным образом, например 100):

# ip rule add prio 100 fwmark 1 lookup vpnrouting

ВАЖНОЕ ЗАМЕЧАНИЕ!
Не забудьте проверить состояние rp_filter

# cat /proc/sys/net/ipv4/conf/tun0/rp_filter

Для нормальной работы, результат должен быть либо 0, либо 2. Смысл данного фильтра в следующем. Когда пакет приходит на интерфейс, ядро проверяет для него обратный маршрут. И если ответ предполагает отправку через другой интерфейс, а не тот же самый, то он считается мусорным и сбрасывается. Структура, которую мы настроили, не позволяет ядру корректно определить маршрут через тот же интерфейс. Поскольку на данном этапе он еще не маркирован, и предполагает отправку через default gateway таблицы main.

Для более полной информации, стоит прочитать что такое rp_filter, для чего он нужен и смысл значений его параметров. Я рекомендую переводить его в значение 2 для интерфейса tun0, и 1 для всех остальных.

Удачного администрирования!

Добавить комментарий