13 January

Настройка динамической маршрутизации (в частности BGP) поверх туннеля OpenVPN на Linux (и вероятно *BSD)

Network technologies
Sandbox

Зачем и о чём эта статья?


Если погуглить на тему «openvpn bgp», то можно найти несколько интересных и полезных с практической точки зрения статей (например раз или два). Но начиная решать задачку вынесенную в заголовок, я по многим причинам даже не удосужился погуглить. Идея пришла как-то сама собой в процессе долгой работы с OpenVPN вообще (в рамках вполне типовых задач, с фиксированным набором сетей с обеих сторон), работы с реализацией OpenVPN на системе RouterOS от MikroTik и стыковки между собой систем на Linux и RouterOS. Собственно в процессе осознания причин написания собственной реализации OpenVPN в RouterOS и пришло «озарение» как можно такую задачу решить в рамках вполне себе штатной редакции OpenVPN. Далее была короткая экспериментальная проверка, показавшая полную работоспособность идеи и запуск этого решения в «промышленную» эксплуатацию.


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



Суть проблемы («кто виноват?»)


Чем же так существенно различаются штатная версия OpenVPN и та, что реализована в RouterOS? Отличий вероятно несколько, но в данной статье мы рассмотрим только одно: штатный OpenVPN в системах кроме RouterOS (и возможно некоторых других) является комбайном, который содержит в себе и транспортную часть (то есть собственно передачу пакетов, она же forwarding, он же data plane) и маршрутизирующую (то есть обмен информацией о маршрутах, он же routing, он же control plane), а в RouterOS сервис OpenVPN отвечает только за транспортную часть, а маршрутизацией занимается другой процесс системы, что позволяет с одной стороны не дублировать функциональность маршрутизирующей подсистемы (и тем более не держать несколько одинаковых таблиц маршрутов в разных сервисах и постоянно синхронизировать их между собой), а с другой стороны, позволяет поверх такого транспорта прозрачно осуществлять передачу таблиц маршрутов и изменять таблицы маршрутов с обеих сторон на лету.


Кроме того, штатная реализация OpenVPN имеет ещё один недостаток: передача маршрутов происходит только в одну сторону (от сервера к клиенту) и только в момент установления сессии (то есть поднятия туннеля). Никакого штатного способа добавить маршрут во внутреннюю таблицу маршрутов OpenVPN на ходу в процессе работы туннеля нет, так же как и передать маршруты с одной стороны на другую. Более того, невозможно даже получить саму таблицу маршрутов.


Решение проблемы («Что делать»)


Анализируя свои скрипты, автоматизирующие назначение маршрутов разным клиентам, я обратил внимание, что у OpenVPN есть две различных опции, задающих маршруты:


  • iroute — задаёт маршруты внутри таблицы маршрутизации процесса OpenVPN.
  • route — задаёт маршруты, которые процесс OpenVPN передаёт в системную таблицу маршрутов (то есть добавляет в таблицу маршруты через свой туннельный интерфейс при подключении и удаляет их же при отключении).

Возник очевидный вопрос: а что будет, если с помощью iroute добавить маршрут 0.0.0.0/0 с обеих сторон, а после этого нужные маршруты (в том числе динамически возникающие или пропадающие) добавлять или удалять на самом туннельном интерфейсе средствами например сервиса маршрутизации (routed, zebra/quagga, bird и т. п.)?


Эксперимент показал, что такая схема действительно работает с небольшим ограничением-неудобством: на один серверный туннель можно подключить только одного клиента. В остальном схема оказалась полностью работоспособной.


Схема работает в режим TLS-over-TCP, то есть для настройки предварительно необходимо сгенерировать ключи и сертификаты SSL.


Ниже привожу примерную конфигурацию OpenVPN для серверной и клиентской стороны.


Конфигурация серверной стороны (по одной для каждого клиента).


Файл server_dyn_rt.conf (сторона сервера)


daemon
compress
ping-timer-rem
persist-tun
persist-key
tls-server
proto tcp-server
topology net30
mode server
script-security 3
keepalive 15 45
tun-mtu 1500
remote-cert-tls client
verify-x509-name <CLIENT_DISTINGUISHED_NAME>	name
auth <TLS_AUTH_ALGORITHM>
cipher <CIPHER_ALGORITHM>
local <SERVER_PUBLIC_IP>
lport <SERVER_PUBLIC_PORT>
dev-type tun
dev <TUNNEL_INTERFACE_NAME>
ifconfig <TUNNEL_SERVER_SIDE_IP> <TUNNEL_CLIENT_SIDE_IP>
client-connect client_connect.sh
push "route-gateway <TUNNEL_SERVER_SIDE_IP>"
push "topology net30"
push&nbsp"persist-tun"
push&nbsp"persist-key"
<dh>
... Diffie-Hellman data
<</dh>
<ca>
... Certificate Authority certificate data
</ca>
<cert>
... Server certificate data
</cert>
<key>
... Server Private Key data
</key>

Файл client_connect.sh (сторона сервера)


#!/bin/sh
echo 'ifconfig-push TUNNEL_CLIENT_SIDE_IP TUNNEL_SERVER_SIDE_IP' >> ${1}
echo 'push "iroute 0.0.0.0 0.0.0.0"' >> ${1}
echo 'iroute 0.0.0.0 0.0.0.0' >> ${1}
exit 0

Файл client_dyn_rt.conf (сторона клиента)


daemon
compress
tls-client
auth <TLS_AUTH_ALGORITHM>
cipher <CIPHER_ALGORITHM>
client
dev-type tun
dev <TUNNEL_INTERFACE_NAME>
script-security 3
remote-cert-tls server
verify-x509-name <SERVER_DISTINGUISHED_NAME> name
remote <SERVER_PUBLIC_IP> <SERVER_PUBLIC_PORT> tcp
<ca>
... Certificate Authority certificate data
</ca>
<cert>
... Client certificate data
</cert>
<key>
... Client Private Key data
</key>

Настройки пакетов и протоколов маршрутизации не привожу как из-за многообразия пакетов, так и из-за многообразия самих настроек (собственно в качестве источника примеров настройки можно использовать вторую из статей, ссылки на которые приведены в начале статьи). Хочу лишь заметить, что вышеприведённая настройка позволяет использовать в частности BGP (который лично мне нравится как своей «управляемостью», так и способностью передавать маршруты различных протоколов в рамках одной сессии). В случае BGP в качестве адреса соседа (neighbor) на стороне «сервера» следует использовать адрес <TUNNEL_CLIENT_SIDE_IP>, а на стороне «клиента» соответственно адрес <TUNNEL_SERVER_SIDE_IP> или же «внутренние» адреса соответствующих сторон, но тогда надо добавлять соответствующие маршруты в конфигурацию сервера и/или клиента.



Плюсы и минусы вышеприведённого решения


Минусы:

  1. На один сервер должен приходится строго один клиент, поэтому для нескольких клиентов придётся держать активными несколько процессов OpenVPN. Как следствие — некоторый перерасход памяти и всякое такое.
  2. Нельзя использовать режим общего ключа (preshared key) в OpenVPN, потому что в этом режиме запрещена динамическая передача параметров от сервера клиенту (push/pull). Из-за этого требуется более сложная конфигурация, включающая генерацию набора ключей и сертификатов, а также скрипт генерации куска конфигурации клиента на стороне сервера (который правда можно заменить каталогом статических файлов, заменив опцию client-connect /path/to/script на опцию client-connect-dir /path/to/config/dir, что повышает уровень безопасности серверной стороны.

Плюсы:

  1. В отличие от таких протоколов как GRE/IPIP туннели OpenVPN могут иметь MTU равное 1500 байт (потому что процесс OpenVPN скрывает «под капотом» всю фрагментацию/дефрагментацию, отдавая в туннельный интерфейс пакеты полной длины). Это упрощает настройку всяких вторичных туннелей поверх туннеля OpenVPN.
  2. Туннель OpenVPN одновременно поддерживает передачу как протокола IPv4, так и IPv6, что позволяет сократить количество туннелей между парами узлов, затраты на их настройку и администрирование, а также передавать маршруты IPv6 в рамках той же сессии BGP, что и маршруты IPv4.
  3. Все плюсы протокола OpenVPN, такие как простота настройки промежуточного сетевого оборудования (или вообще полное отсутствие необходимости в таковой), возможность маскировки трафика под HTTPS, наличие реализации под большинство платформ et cetera, et cetera.

Надеюсь кому-то вышеприведённое руководство окажется полезным.

Tags:OpenVPNBGPdynamic routing
Hubs: Network technologies
+8
4.7k 66
Comments 14