В документации к OpenVPN отыщется и много других интересных возможностей. К примеру, если указать несколько опций remote
в конфиге клиента, клиент автоматически переключится на следующий адрес сервера, если не сможет подключиться к первому.
Именно гибкость настройки и разнообразие опций позволяют OpenVPN успешно конкурировать с другими похожими проектами.
В интернете полно статей о настройке сервера OpenVPN для подключения пользовательских компьютеров к удаленной сети, и создается впечатление, что это его единственное применение. Кто-то даже считает, что с появлением WireGuard он вовсе утратил свою актуальность. Действительно, встроенный роуминг и другие возможности WireGuard создают старым проектам серьезную конкуренцию.
Однако в OpenVPN есть много менее известных возможностей, и он все еще остается одним из самых гибких, многофункциональных и простых в настройке решений.
Я предполагаю, что у тебя уже есть опыт настройки OpenVPN, поэтому пропустим основы и сразу перейдем к малоизвестным полезным фичам.
Site to site
OpenVPN обычно ассоциируется с настройками типа клиент — сервер. Большинство статей посвящены именно этому варианту, и многие сетевые дистрибутивы вроде OpenWRT и OPNSense предоставляют пользовательский интерфейс только для него. Но это большое упущение! OpenVPN — очень простой способ соединить два хоста или сети безопасным туннелем типа точка — точка.
Еще менее известный факт: в этом режиме OpenVPN может работать со статическими ключами (pre-shared key), без сертификатов. Более того, чтобы сгенерировать такой ключ, тебе не понадобится никаких инструментов, кроме самого OpenVPN.
Предположим, что мы хотим постоянное подключение к удаленной машине с адресом 203.0.113.100. Сгенерируем ключ:
$ openvpn --genkey --secret /etc/openvpn/shared.key
Настроим локальную сторону:
dev-type tun
dev tun0
ifconfig 192.168.0.2 192.168.0.1
rport 1194
remote 203.0.113.100
secret /etc/openvpn/shared.key
Теперь скопируем файл ключа на удаленную машину и напишем там противоположный конфиг:
dev-type tun
dev tun0
ifconfig 192.168.0.1 192.168.0.2
lport 1194
local 203.0.113.100
secret /etc/openvpn/shared.key
По желанию можно добавить туда опции для работы в режиме демона:
daemon openvpn-tun0
writepid /var/run/openvpn-tun0.pid
Осталось сохранить конфиг в файл вроде /etc/openvpn/s2s.conf
и запустить openvpn --config /etc/openvpn/s2s.conf
.
Идентификация соединений происходит только по ключу, адреса хостов никак не учитываются. Указать опцию remote
нужно только на одной стороне — «клиенте». Сторона без опции remote
будет ждать подключения. Из-за этого OpenVPN удобен для соединений site to site с хостами, у которых нет статического адреса, и даже хостами за NAT.
Опция local
не обязательна, но полезна для маршрутизаторов с несколькими внешними интерфейсами. Если ее не указывать, OpenVPN будет слушать на 0.0.0.0.
OpenVPN работает поверх UDP, если не указано обратное. Если хочешь использовать TCP, на стороне «клиента» нужно добавить в конфиг proto tcp-client
, а на стороне «сервера» — proto tcp-server
.
Насколько плохо использование статических ключей? С одной стороны, утечка такого ключа куда более опасна, поскольку злоумышленник сможет расшифровать весь перехваченный трафик, и в прошлом, и в будущем. TLS решает эту проблему использованием сессионных ключей. Но с другой стороны, такой трафик сложнее идентифицировать как зашифрованный туннель.
Режим site to site можно использовать с TLS и сертификатами, но настройка этого сложнее и требует больше времени. Если тебе нужно поднять соединение между двумя машинами с минимальными затратами усилий, OpenVPN site to site со статическим ключом — оптимальный вариант.
Раздаем разные настройки разным клиентам
Динамическая маршрутизация — это здорово, но совершенно непрактично для клиентских соединений. Даже для туннелей к маршрутизаторам это может быть непрактично, если они работают на какой-нибудь OpenWRT, где нет удобного механизма для ее настройки. Вручную настроить FRRouting или BIRD можно на любой UNIX-подобной ОС, но нужно ли?
С OpenVPN решить эту проблему просто. Все слышали про опции push
в конфиге самого сервера, вроде push "route 172.16.20.0 255.255.255.0"
. Меньше людей знают, что эти настройки могут быть не только глобальными. Более того, почти любые опции можно указать на уровне клиентов.
Чтобы иметь возможность задавать настройки отдельным клиентам, нужно указать каталог для файлов с этими настройками:
client-config-dir /etc/openvpn/client-configs/
Для идентификации клиентов используется поле CN (Common Name) из их сертификатов. Предположим, у тебя есть клиент с CN=jrandomuser и ему нужен доступ к сети 172.16.19.0/24.
Достаточно создать файл /etc/openvpn/client-configs/jrandomuser
и прописать туда опцию
push "route 172.16.19.0 255.255.255.0"
Таким же образом можно избирательно отключить split tunneling для некоторых клиентов, если прописать им push "route 0.0.0.0 0.0.0.0"
.
Кроме маршрутов, можно раздавать множество других опций. Например, задать клиентам свой сервер DNS: push "dhcp-option DNS 172.16.0.10"
. Можно указать и сервер WINS, если клиенту нужен доступ к SMB: push "dhcp-option WINS 172.16.0.10"
.
Возможность указывать все это как глобально, так и для отдельных клиентов позволяет весьма гибко управлять доступом пользователей к внутренней сети без особых усилий.
Подключаем удаленные сети
Режим site to site хорош для небольшого числа соединений. Если у тебя десятки или сотни удаленных сетей, настраивать туннель к каждой крайне утомительно. Можно автоматизировать этот процесс с помощью Ansible или чего-то еще, но в OpenVPN есть встроенный механизм для этой задачи.
Предположим, что сеть твоей организации — 10.0.0.0/16, а для удаленных клиентов у тебя выделена 10.0.0.0/21
. Пусть твой сервер OpenVPN использует сетевой интерфейс tun0
.
Прежде всего нужно создать маршрут ко всей сети через этот интерфейс. В Linux команда будет такой:
$ sudo ip route add 10.0.0.0/21 dev tun0
Тонкий момент: ядро Linux удаляет маршруты, когда интерфейс исчезает или уходит в down. По этой причине нужно сделать, чтобы этот маршрут создавался при перезапуске OpenVPN. Самое чистое решение — использовать демон маршрутизации вроде Quagga/FRRouting или BIRD, который сам следит за состояниями сетевых интерфейсов и пересоздает маршруты по необходимости. Самое простое — добавить команду в скрипт запуска.
Одну подсеть мы выделим для клиентских интерфейсов и укажем ее в опции server
. Кроме того, нужно указать в конфиге topology subnet
. Чтобы указывать, какая сеть какому клиенту принадлежит, нам снова понадобится client-config-dir
.
Разумеется, клиентам потребуется доступ к корпоративной сети, поэтому мы выдадим им маршрут к 10.0.0.0/16 с помощью push
.
Добавим следующее в конфиг сервера:
server 10.0.0.0 255.255.255.0
topology subnet
persist-tun
client-config-dir /etc/openvpn/client-configs/
push "route 10.0.0.0 255.255.0.0"
Теперь создадим конфиг клиента. Предположим, ты хочешь подключить офис с сетью 10.0.1.0/24 и ты создал для его маршрутизатора сертификат с CN=my-remote-office
.
Пропиши следующее в /etc/openvpn/client-configs/my-remote-office
:
iroute 10.0.1.0 255.255.255.0
Теперь, когда этот клиент подключится, OpenVPN ассоциирует сеть 10.0.1.0/24 с его подключением.
Храним все настройки клиента в одном файле
Очень часто можно получить от удаленной стороны конфиг OpenVPN для подключения к серверу и отдельные файлы с CA, клиентским сертификатом и ключом. Это приемлемо для админов, но крайне неудобно для конечных пользователей.
К счастью, ключи и сертификаты можно хранить вместе с настройками. Например:
client
dev tun
proto udp
remote 203.0.113.20 1194
<ca>
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
</ca>
<cert>
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
</cert>
<key>
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
</key>
Пользователю достаточно сохранить этот файл (к примеру, в my-vpn.ovpn
) и запустить openvpn --config my-vpn.ovpn
или указать этот файл в графическом интерфейсе клиента.
Общаемся с процессом OpenVPN через сокет
OpenVPN предоставляет интерфейс, через который ты можешь просмотреть информацию о подключениях и выполнить ряд административных задач. Взаимодействовать с ним можно через сокет: либо TCP/IP, либо UNIX.
Есть вариант подключаться к сокету IP через Telnet. Это удобно, но нужно следить за безопасностью доступа к этому сокету. Обеспечить безопасность UNIX domain socket куда проще, поэтому мы выберем именно этот способ.
Нужно добавить в конфиг сервера следующее:
management /tmp/openvpn-mgmt unix
Подключиться к этому сокету можно с помощью утилиты socat:
$ sudo socat - UNIX-CONNECT:/tmp/openvpn-mgmt
>INFO:OpenVPN Management Interface Version 1 -- type 'help' for more info
В выводе help
ты найдешь целый ряд полезных команд. К примеру, kill jrandomuser
принудительно разорвет все подключения клиента с CN=jrandomuser. В выводе команды status
можно увидеть информацию о всех текущих подключениях.
Если тебя интересует именно информация о клиентских туннелях и ты хочешь получать ее без необходимости каждый раз подключаться к сокету, можешь добавить вот эту опцию в конфиг:
status /tmp/openvpn.status
Данные в файл /tmp/openvpn.status
будут писаться в таком же формате, как вывод команды status
, вроде:
OpenVPN CLIENT LIST
Updated,Tue Apr 21 12:45:47 2020
Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since
jrandomuser,192.0.2.57:56422,51036248,505028963,Tue Apr 21 08:04:02 2020