воскресенье, 7 марта 2021 г.

Ubuntu: как настроить OpenVPN

С недавних пор заинтересовал меня вопрос, как можно попадать в свою домашнюю сеть из большого Интернета. Нет, конечно, проброшенные ssh и rdp-порты выручают, но всё-таки это как-то неизящно. Есть, правда, ещё pptp, он он, говорят, небезопасный... В общем, захотелось что-нибудь посолиднее, и выбор пал на OpenVPN.

Сама установка трудностей не представляет:

sudo apt install openvpn
Основная интрига - как этот самый openvpn-сервер сконфигурировать. В сети полно инструкций, как это сделать с использованием easy-rsa, но на поверку оказалось, что этот самый easy-rsa - просто набор bash-скриптов для работы с openssl, так что особого смысла в использовании этой дополнительной прослойки я не увидел и насоздавал ключи и сертификаты прямо с использованием openssl.

Делается это в два присеста. Во-первых, организуется удостоверяющий центр, и, во-вторых, создаются и с его помощью подписываются сертификаты для сервера и для клиента. В результате образуется следующая структура:

Удостоверяющий центр:
-- приватный ключ УЦ (бережно хранится и никому не показывается)
-- сертификат УЦ (раздаётся всем кому попало)
эта пара используется для подписи сертификатов сервера и клиента

Сервер OpenVPN:
-- сертификат УЦ
-- приватный ключ сервера (бережно хранится и никому не показывается)
-- сертификат сервера, подписанный УЦ (передаётся клиентам при установлении подключения)

Клиент, подключающийся к серверу:
-- сертификат УЦ
-- приватный ключ клиента (бережно хранится и никому не показывается)
-- сертификат клиента, подписанный УЦ (передаётся серверу при установлении подключения)
При подключении клиент и сервер обмениваются своими сертификатами. Сервер проверяет, что сертификат клиента подписан тем же УЦ, что и его собственный, а клиент делает такую же проверку с сертификатом сервера. Если оба сертификата прошли проверку, соединение устанавливается, сервер вытаскивает из сертификата клиента ComnmonName и использует его для идентификации клиента.

Создание удостоверяющего центра (УЦ)

Можно, конечно, использовать сторонние УЦ, но там, как правило, за работу требуют денежку. Поэтому логично поднять свой УЦ. Фактически, весь УЦ - это пара файлов, ключ и самоподписанный сертификат. Единственная тонкость - из соображений безопасности эту пару файлов желательно запрятать поглубже: вынести в docker, на отдельную машину, отправить на Луну... Так или иначе, создаются эти файлы следующим образом.

1. Создаём приватный ключ УЦ:

openssl genrsa    -des3   -out huhmuh-CA.key    2048

Поскольку тут фигурирует опция "-des3", то ключ будет зашифрован, поэтому потребуется ввести пароль. В дальнейшем этот пароль потребуется вводить при использовании этого ключа, например, при подписывании сертификатов, так что пароль лучше сделать помудрёнее и не забыть.

2. Создаём самоподписанный сертификат УЦ:

openssl req \
    -x509 \
    -days 3650 \
    -key huhmuh-CA.key \
    -out huhmuh-CA.crt \
    -subj "/emailAddress=huhmuh@example.com/C=RU/ST=Oblastnaya oblast/L=Default City/O=Home/OU=Living room/CN=example.com" \
    -addext "subjectAltName = DNS:example.com"

Можно "-subj" и не указывать, тогда при создании сертификата потребуется ответить на кучку вопросов, кто вы и откуда. Ну, и, поскольку используется huhmuh-CA.key, программа спросит для него пароль.

В результате этих манипуляций у нас на руках окажутся два файла, huhmuh-CA.key и huhmuh-CA.crt - это и будет наш удостоверяющий центр.

Создание сертификата сервера

1. Создаём приватный ключ сервера:

openssl genrsa    -out huhmuh-openvpn.key    2048

Этот ключ получается незашифрованным. Наверно, это не очень хорошо, но дело в том, что сервер openvpn будет этот ключ активно использовать, и для того, чтобы ему рассказать пароль, потребуются дополнительные телодвижения. Так что в данном случае победила лень.

2. Создаём запрос сертификата для сервера:

openssl req \
    -new \
    -key huhmuh-openvpn.key \
    -out huhmuh-openvpn.csr \
    -subj "/emailAddress=huhmuh@example.com/C=RU/ST=Oblastnaya oblast/L=Default City/O=Home/OU=Living room/CN=server"

Тут стоит обратить внимание на поле /CN=server. Его значение отличается от соответствующего поля, использовавшегося при создании сертификата УЦ. Если эти поля совпадают, то клиенты почему-то считают сертификат сервера самоподписанным, насколько я понял. Поэтому лучше CommonName сертификатов УЦ и сервера сделать различными, во избежание недоразумений.

3. Подписываем сертификат в удостоверяющем центре:

Как выяснилось в процессе эксплуатации, некоторые клиенты openvpn, в частности, 2.3, требуют, чтобы сертификат содержал расширения x509v3, в которых было бы явно указано, для чего его можно использовать. Делается это при помощи следующих расширений: keyUsage и extendedKeyUsage. Можно было бы их указывать на этапе создания запроса сертификата, но почему-то на этапе подписания они теряются. Но есть положительный момент: при подписании можно добавить нужные расширения. И момент отрицательный: соответствующий параметр командной строки "-addext" срабатывает только в openssl начиная с версии 1.1.1а.

Поэтому создадим на УЦ два файла с расширениями для серверных и клиентских сертификатов.

Файл server-extensions.txt:

[default]
keyUsage = digitalSignature,keyAgreement
extendedKeyUsage = serverAuth

Файл client-extensions.txt:

[default]
keyUsage = digitalSignature,keyAgreement
extendedKeyUsage = clientAuth

И будем их использовать при подписании.

Сам процесс подписания прост: передаём на Луну файл *.csr, подписываем его в УЦ командой:

openssl x509 \
    -req \
    -days 3650 \
    -in    huhmuh-openvpn.csr \
    -out   huhmuh-openvpn.crt \
    -CA    huhmuh-CA.crt \
    -CAkey huhmuh-CA.key \
    -CAcreateserial \
    -extfile server-extensions.txt

И возвращаем готовый сертификат huhmuh-openvpn.crt с Луны обратно на сервер, где будет жить OpenVPN.

4. Дальше потребуется сгенерировать ещё два файла, они нужны при настройке сервера. Эти файлы создаются так:

Создаём файл с параметрами для протокола Диффи-Хеллмана:

openssl dhparam    -out dh.pem     2048

Создаём приватный ключ для tls-аутентификации:

openvpn --genkey --secret ta.key

Тут интересно, что используется не команда openssl, а сама программа openvpn. У этой самой openvpn оказалось вообще много всяких параметров командной строки, посмотреть их можно командой openvpn --help. Программа живёт в каталоге /sbin или /usr/sbin, эти каталоги в $PATH простых смертных не прописаны, так что если вызывать её из-под непривилегированного пользователя, возможно потребуется указать полный путь: /sbin/openvpn.

Итак, по завершении этого этапа, в нашем распоряжении будут файлы:

huhmuh-CA.crt
huhmuh-openvpn.key
huhmuh-openvpn.crt
dh.pem
ta.key

Эти файлы потребуется сгрузить в каталог /etc/openvpn, туда же скопировать из /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz образец конфигурации, распаковать его командой

gunzip server.conf.gz

При этом получится файл server.conf, и этот файл уже можно править под свои нужды.

Файл server.conf богат на комментарии, между ними запрятаны настройки, которые должны быть примерно такими:

# тут можно указать, какой адрес использовать,
# но, поскольку мой сервер находится "в глубине" сети,
# то у него один адрес, поэтому я оставил эту настройку закомментированной
;local a.b.c.d

# порт по умолчанию 1194, но можно и сменить
port 32194

# протокол рекомендуют udp, но пусть уж так
proto tcp

dev tun

# наши ключи и сертификаты
ca       huhmuh-CA.crt
cert     huhmuh-openvpn.crt
key      huhmuh-openvpn.key  # This file should be kept secret
dh       dh.pem
tls-auth ta.key     0

topology subnet

# задаём, какой диапазон адресов будет использоваться клиентскими подключениями
# первый адрес, 10.8.0.1, сервер openvpn заберёт себе
server 10.8.0.0 255.255.255.0

# логи и прочая полезная информация.
# в частности, в ipp.txt показываются сопоставления CN клиентских сертификатов и выделенные для них адреса
ifconfig-pool-persist /var/log/openvpn/ipp.txt
status                /var/log/openvpn/openvpn-status.log
log-append            /var/log/openvpn/openvpn.log

# насколько подробным будет лог
verb 6

# с этой опцией клиент будет вынужден весь свой трафик завернуть через openvpn
push "redirect-gateway def1 bypass-dhcp"

keepalive 10 120

# версия 2.3 знает лишь AES-256-CBC, с версии 2.4 лучше применять AES-256-GCM, он надёжнее, говорят
;cipher AES-256-CBC
cipher AES-256-GCM

comp-lzo

# чтобы сервер не работал под рутом - для пущей безопасности
user nobody
group nogroup

persist-key
persist-tun

С готовым файлом конфигурации можно попытаться, наконец, взлететь. Под убунтой это делается командой:

systemctl start openvpn@server

Вот это дополнение, @server, задаёт имя файла конфигурации, который будет использован, т.е., server.conf. Если же взлететь с первого раза не получилось, может выручить запуск сервера командой:

openvpn --config /etc/openvpn/server.conf

И медитация над логами из /var/log/openvpn/ в попытке разобраться, что же пошло не так...

Создание файла подключения клиента

Если сервер, наконец, удалось каким-то чудом запустить, пришло время для клиента. Все настройки клиента - и конфигурация, и ключ, и сертификаты - удобно сливаются в единый файл *.ovpn.

1. Создаём приватный ключ клиента:

openssl genrsa    -des3    -out huhmuh-alice.key    2048

Ключ шифруем (опция "-des3") - пусть клиент при установлении подключения вводит пароль.

2. Создаём запрос сертификата для клиента:

openssl req \
    -new \
    -key huhmuh-alice.key \
    -out huhmuh-alice.csr \
    -subj "/emailAddress=alice@example.com/C=RU/ST=Oblastnaya oblast/L=Default City/O=Home/OU=Kitchen/CN=alice"

3. Подписываем сертификат клиента в удостоверяющем центре:

openssl x509 \
    -req \
    -days 3650 \
    -in    huhmuh-alice.csr \
    -out   huhmuh-alice.crt \
    -CA    huhmuh-CA.crt \
    -CAkey huhmuh-CA.key \
    -CAcreateserial \
    -extfile client-extensions.txt

В итоге для клиентского подключения мы приготовили файлы:

huhmuh-CA.crt
huhmuh-alice.key
huhmuh-alice.crt

Из этих файлов и файла /etc/openvpn/ta.key можно собрать файл подключения. Проще всего это сделать примерно таким скриптом:

#!/bin/sh

FNAME=huhmuh-alice.ovpn

echo "client
dev tun
proto tcp
remote example.com 32194
resolv-retry infinite
remote-cert-tls server
auth SHA1
cipher AES-256-GCM
tls-client
key-direction 1
persist-key
persist-tun
resolv-retry infinite
nobind
comp-lzo
verb 3"				>  $FNAME

echo "<ca>"			>> $FNAME
cat ./huhmuh-CA.crt		>> $FNAME
echo "</ca>"			>> $FNAME

echo "<cert>"			>> $FNAME
cat ./huhmuh-alice.crt		>> $FNAME
echo "</cert>"			>> $FNAME

echo "<key>"			>> $FNAME
cat ./huhmuh-alice.key		>> $FNAME
echo "</key>"			>> $FNAME

echo "<tls-auth>"		>> $FNAME
cat /etc/openvpn/ta.key		>> $FNAME
echo "</tls-auth>"		>> $FNAME

Тут надо помнить пару моментов. Во-первых, /etc/openvpn/ta.key - секретный (хотя как секретный, клиенту же передаётся?), поэтому доступен лишь под рутом. И, во-вторых, соответствующие настройки должны совпадать с настройками сервера. Например, если в конфиге сервера стоит "cipher AES-256-CBC", то и у клиента должно быть то же самое, "cipher AES-256-CBC". Про "remote example.com 32194", думаю, упоминать смысла нет - и так понятно, что это адрес сервера и открытый на нем порт.

Если всё прошло нормально, то в нашем распоряжении оказывается файл huhmuh-alice.ovpn, который можно отдавать клиенту, чтобы он с ним подключался к нашему серверу. Клиентов для OpenVPN хватает, даже под андроидом есть некий OpenVPN Connect, так что тип операционной системы использование этого инструмента не лимитирует.

(NB: для WindowsXP, как оказалось, последняя версия клиента: 2.3.18. При её настройке из скопированного в C:\Program Files\OpenVPN\config файла .ovpn пришлось удалить строку set CLIENT_CERT 0. Также эта версия начала кочевряжиться при отсутствии Key Usage в расширениях серверного сертификата, о чем было упомянуто выше. Помогает либо добавление требуемого расширения, либо убирание строки remote-cert-tls server в файле *.ovpn)

Настройка netfilter на сервере

В случае успеха клиенты, подключенные к серверу, смогут добраться лишь до этого самого сервера. Попытка попасть на какие-то другие хосты в локальной сети окажется неудачной. Для того, чтобы клиенты получили возможность пройти дальше сервера, требуется использовать nat. В принципе, можно сконфигурировать openvpn так, чтобы он позволил своим клиентам получать адреса из диапазона локальной сети. Но, поскольку в вышеприведенной конфигурации клиенты получают адреса из диапазона 10.8.0.0/24, а остальная сеть живёт в диапазоне 192.168.0.0/24, то в данном случае придётся воспользоваться iptables. Минимальная конфигурация, позволяющая это осуществить, выглядит так:

#!/bin/sh

# подключаем nf_conntrack и разрешаем проброс пакетов
modprobe nf_conntrack
sysctl net.ipv4.ip_forward=1


# очищаем таблицы nat и filter
iptables -F -t nat
iptables -F -t filter

iptables -X -t nat
iptables -X -t filter


# разрешаем работать исходящим и уже установленным подключениям
iptables -t filter -A INPUT   -m conntrack --ctstate     ESTABLISHED,RELATED -j ACCEPT
iptables -t filter -A FORWARD -m conntrack --ctstate     ESTABLISHED,RELATED -j ACCEPT
iptables -t filter -A OUTPUT  -m conntrack --ctstate NEW,ESTABLISHED,RELATED -j ACCEPT


# разрешаем доступ к серверу по перечисленным портам (не один же openvpn у нас тут крутится)
iptables -t filter -A INPUT -p tcp -m multiport --dports 22,67,68,80,443,32194 -j ACCEPT
iptables -t filter -A INPUT -p udp -m multiport --dports 67,68                 -j ACCEPT
# 22 -ssh, 67 - dhcp, 80,443 - https, 32194 - openvpn


# в трафик из подсети 10.8.0.0/24 подставляем адрес сервера в локальной сети, 192.168.0.2, в качестве источника
iptables -t filter -A FORWARD     -s 10.8.0.0/24  -j ACCEPT
iptables -t nat    -A POSTROUTING -s 10.8.0.0/24  -j SNAT    --to-source 192.168.0.2


# остальные пакеты, не прошедшие правила, отбрасываем (кроме исходящих)
iptables -t filter -P INPUT   DROP
iptables -t filter -P FORWARD DROP
iptables -t filter -P OUTPUT  ACCEPT

Настройка клиента на ubuntu

Подключение к серверу с использованием имеющегося файла huhmuh-alice.ovpn выглядит так:

sudo openvpn --config ./huhmuh-alice.ovpn

При этом, если на сервере прописана опция redirect-gateway, весь трафик пойдёт через этот vpn-канал. Иногда это не очень удобно - например, мы сидим в большом инете и хотим подцепться к корпоративной сети, но при этом не желаем, чтобы развлекательные сайты заворачивались через служебную прокси. Это решается следующим образом.

Оказывается, в openvpn есть две внутренние константы, vpn_gateway и net_gateway. Они содержат адрес vpn-шлюза и шлюза локальной сети соответственно, и с их помощью можно задавать нужные маршруты. В файл *.ovpn добавляем строку:

route 192.168.0.0 255.255.255.0 vpn_gateway

Клиента запускаем командой:

sudo openvpn --pull-filter ignore redirect-gateway --config ./huhmuh-alice.ovpn

И весь трафик будет ходить по-старому, за исключением сети 192.168.0.0/24 - общение с ней пойдёт через vpn.

Литература

Установка и настройка OpenVPN-сервера в Debian
Конспект по установке OpenVPN
Установка и настройка сервера OpenVPN в Ubuntu 20.04
OpenSSL Quick Reference Guide
Create SSL certificate non-interactively
Генерирование сертификатов для OpenVPN с помощью Easy-RSA 3
x509v3_config
OpenVPN Certificate does not have key usage extension
OpenSSL CA keyUsage extension
IX509ExtensionEnhancedKeyUsage interface (certenroll.h)
Class ExtendedKeyUsage
Connecting to Access Server with Linux
Ignoring redirect-gateway

Комментариев нет:

Отправить комментарий