Корпоративный OpenVPN, или как сделать тигра из кошки

от автора

Говорят, из кошки не сделать тигра, но сегодня мы попробуем добавить «корпоративности» очень простому и популярному ВПН-решению. OpenVPN Community Edition является решением с открытым кодом, очень популярным в мире и довольно безопасным. В корпоративной среде его использование часто бывает проблематично – отсутствуют важные функции, позволяющие разворачивать решение большому количеству пользователей с минимальными затратами.

Не буду ставить OpenVPN на место идеального решения ВПН. В этой статье разберу, как, используя сертификаты и ldap-аутентификацию в экосистеме Microsoft, организовать доступ разных групп пользователей в определенные закрытые сегменты корпоративной сети (авторизация). Никаких Easy-RSA и самоподписных сертификатов – максимальная корпоративность и автоматизация.

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

В рассмотренной в статье схеме присутствует 3 фактора проверяемых при входе пользователя:

  • Сертификат пользователя («фактор владения», расположен в хранилище сертификатов компьютера пользователя, можно сделать не экспортируемым)

  • Имя/пароль пользователя («фактор знания», проверяется в Microsoft Active Directory)

  • Единый ключ («фактор владения», часть конфига клиента, в основном защита от DDoS)

Таким образом, при правильной реализации системы распространения сертификатов сможем получить полноценную многофакторную аутентификацию с неплохой защитой от DDoS-атак.

Рисунок 1 показывает схему аутентификации и авторизации, рассмотренную в статье:

Для реализации данной схемы нам понадобятся:

— Домен Microsoft Active Directory (MS AD)

o   Пользователи и компьютеры в домене

o   Три группы безопасности OpenVPN_sg1_users, OpenVPN_sg2_users, OpenVPN_sg3_users пользователями

o   Сервисная учетная запись для чтения списка пользователей

— Сервер Microsoft Active Directory Certificate Server (ADCS, PKI)

o   Система PKI (AD CS) должна быть внедрена в домен.

— Виртуальная машина Linux для сервера OpenVPN с выходом/входом в интернет

— Сетевая связность с контролером домена

— Межсетевой экран

Конфигурирование Microsoft Active Directory, межсетевых экранов и PKI выходит за рамки статьи, хотя важные комментарии обязательно приведу. Будем считать, что вы уже имеете корпоративную инфраструктуру Microsoft с работающими сервисами из списка выше. Данная статья фокусируется только на конфигурации OpenVPN.

Установка приложений и OpenVPN

В целом установка OpenVPN сервера на большинстве веток Linux не вызывает каких-то особенных трудностей. В моем случае была использована Ubuntu 22.04 LTS.

sudo apt update -y

sudo apt install openvpn openssl ldap-utils openvpn-auth-ldap dos2unix -y

Сервер готов к конфигурированию и дальнейшей работе.

Если вы используете «усиленные» версии Linux, возможно вам понадобится добавить разрешения локальных межсетевых экранов.

Настройка маршрутизации

Делаем роутер из виртуальной машины:

sudo nano /etc/sysctl.conf

добавляем строчку:

net.ipv4.ip_forward=1

Перезапускаем сервис:

sudo sysctl -p

Мы настраиваем доступ к корпоративной сети, поэтому выход в интернет с виртуальной машины (а это SNAT, iptables и т.п.) мы не настраиваем. (это НЕ обход блокировок и т.п., это корпоративный ВПН)

Не забываем про роутинг. В данном примере будем использовать пул адресов клиентов 10.10.0.0/16. Трафик должен маршрутизироваться через нашу виртуальную машину.

Готовим сертификат сервера

Дисклеймер: Конфигурирование PKI выходит за рамки данной статьи. Далее скриншоты и комментарии приведены только относительно важных параметров.

Каждый клиент проверяет сертификат сервера при подключении. Эта мера не позволит злоумышленнику использовать поддельный сервер, например, для сбора корпоративных паролей. Сертификат сервера подписывается корневым центром сертификации и таким образом встраивается в систему PKI от Microsoft.

Итак, выпускаем сертификат для OpenVPN-сервера вручную.

Для сервера нет каких-то специфических требований, за исключением EKU, который мы «заставим» проверять всех клиентов в их конфигурационном файле.

Шаблон

Подготовьте шаблон для выпуска сертификатов OpenVPN-сервера, если у вас его нет. Шаблон можно сделать на основе шаблона «Computer», или воспользоваться уже существующим, подходящим для этих целей.

Определите срок действия сертификата.

Считаю, что задавать длинные сроки не безопасно. Один год – рекомендуемый вариант. Учитываем, что чуть раньше, чем через год, придется вспомнить процедуру установки сертификата и перезагрузить OpenVPN-сервер.

Очень часто об устаревшем сертификате вспоминают после того, как ВПН перестал работать. Лучше заранее поставить себе напоминание или подключить мониторинг сертификатов.

Сервер OpenVPN не может получать сертификат автоматически, поэтому придется либо экспортировать ключ и сертификат, либо создавать запрос для подписи. В этой статье буду запрашивать и выпускать сертификат прямо с PKI-сервера, а затем его экспортировать и переносить на OpenVPN-сервер.

Этот скриншот показывает поле, разрешающее выполнять такой экспорт.

В этом случае мы должны будем заполнить поле «subject» вручную:

Для процесса аутентификации OpenVPN поле «subject» значения не имеет (к сожалению). Клиенты будут устанавливать подключение с любым сервером, прописанным в сертификате, заданном в своем конфигурационном файле. То же самое касается полей «alt»: они не используются. Важный параметр, который мы будем проверять на стороне клиента:

С этого сервера я буду запрашивать сертификат, поэтому установил соответствующее разрешение:

Выпускаем и экспортируем сертификат:

Заполняем «Subject»:

Запрашиваем и выпускаем сертификат. Если все прошло успешно, вы увидите сертификат в хранилище компьютера:

Экспортируем сертификат:

Обязательно с приватным ключом.

Включаем всю цепочку:

Задаем пароль, путь и имя файла. Сертификат сервера и ключ готов. Полученный файл копируем на OpenVPN-сервер.

Дополнительно, приготовьте корневой сертификат в формате РЕМ и также скопируйте его на сервер OpenVPN:

Если у вас многоуровневая система PKI, сохраните цепочку в одном файле. Обратите внимание на поле CN (Common Name). Это поле нам пригодится, когда мы будем настраивать конфигурационный файл клиента. Запоминаем. В моем случае имя банально простое.

Корневой сертификат можно скопировать, например, нажав на советующую кнопку:

Таким образом мы приготовили и скопировали на сервер OpenVPN 2 файла:

  • vm-a-openvpn-01.pfx (ключ, сертификат в формате pfx)

  • ca.crt (Корневой сертификат в формате PEM)

Конфигурируем приложение OpenVPN

1.       Генерируем обязательный файл для работы криптографии:

sudo openssl dhparam -out /etc/openvpn/server/dh.pem 2048

2.       Готовим дополнительный ключ аутентификации:

sudo openvpn —genkey secret /etc/openvpn/server/ta.key

3.       Копируем на OpenVPN сервер подготовленный сертификат: vm-a-openvpn-01.pfx (имя по вашему вкусу)

4.       Делаем из pfx-файла формат пригодный для сервера OpenVPN (PEM):

sudo openssl pkcs12 -in vm-a-openvpn-01.pfx -nocerts -nodes -out /etc/openvpn/server/vm-a-openvpn-01.key (Ключ к сертификату, без пароля, хранить в секрете)

sudo openssl pkcs12 -in ./vm-a-openvpn-01.pfx -clcerts -nokeys -out /etc/openvpn/server/vm-a-openvpn-01.crt (сертификат сервера)

sudo cp ca.cer /etc/openvpn/server/root-ca.crt (просто копируем корневой сертификат в удобное место для сервера без преобразований)

Файл конфигурирования OpenVPN сервера /etc/openvpn/server/example-simple.conf

1.       Создаем файл:

sudo nano /etc/openvpn/server/example-simple.conf

2.       Вставляем текст:

mode server

tls-server

port 443

proto tcp-server

script-security 2

dev tun

ca root-ca.crt

cert vm-a-openvpn-01.crt

key vm-a-openvpn-01.key

dh dh.pem

tls-crypt ta.key

verify-client-cert require

topology subnet

#Наш общий пул адресов на ВСЕ пулы связанные с группами безопасности. Все «подпулы» должны входить сюда

server 10.10.0.0 255.255.0.0

push «route 192.168.3.0 255.255.255.0»

push «dhcp-option DNS 192.168.3.10»

keepalive 10 120

cipher AES-256-CBC

persist-key

persist-tun

verb 3

client-connect client_connect.sh

client-disconnect client_disconnect.sh

remote-cert-tls client

plugin /usr/lib/openvpn/openvpn-auth-ldap.so ldap.conf

Файл ссылается на следующие важные файлы (должны быть в папке /etc/openvpn/server):

·       root-ca.crt (скопировали выше)

·       vm-a-openvpn-01.crt (скопировали выше)

·       vm-a-openvpn-01.key (скопировали выше)

·       dh.pem (сгенерирован выше)

·       ta.key (сгенерирован выше)

·       client_connect.sh (основной файл сопоставления пула адресов и групп безопасности, описан ниже)

·       client_disconnect.sh (завершение соединения и очистка базы использованных IP адресов)

·       ldap.conf (конфиг AD аутентификации в OpenVPN)

Файл client_connect.sh

Файл содержит основной скрипт сопоставления групп безопасности AD и подсетей. Именно он будет назначать правильный адрес клиенту. Перед использованием требуется внести в файл список групп и используемые пулы адресов в секции в начале. В нашем примере три пула 10.10.10, 10.10.20 и 10.10.30. Помните, что пулы должны попадать в общий диапазон, настроенный в главном конфигурационном файле OpenVPN сервера (server 10.10.0.0 255.255.0.0).

1.       Создаем файл:

sudo nano /etc/openvpn/server/client_connect.sh

2.       Вставляем содержимое:

#!/bin/sh

######################################################

#Microsoft AD Security Group name|subnet|type

grouplist=’OpenVPN_sg1_users|10.10.10|sg1

OpenVPN_sg2_PowerUsers|10.10.20|sg2

OpenVPN_sg3_admins|10.10.30|sg3′

#####################################################

Файл с занятыми адресами хранится тут:

reservedipfile=/tmp/reserved_ip.txt

echo «CLIENT CONNECTED»

echo «DEBUG: 0=$0 1=$1 2=$2 common_name=$common_name username=$username TempIP=$ifconfig_pool_remote_ip»

3.       Проверяем чтобы имя, введенное пользователем (Login) и сертификат (common name), совпадали.

if [ ! «$username» = «$common_name» ]; then

  echo «Access deny. Username (LDAP) is not the same as common name (Cetrificate)»

  return 1

fi

grp=`ldapsearch -x -H ldaps://hatter2.demo.land -D «sp_openvpn» -w P@ssword -b «DC=demo,DC=land» «(sAMAccountName=$username)» | grep «memberOf\|pager»`

username=`echo $username | grep ^[a-zA-Z0-9_\.\-]*$`

        for a in $grouplist

        do

          groupname=`echo $a | cut -d ‘|’ -f 1`

          groupsubnet=`echo $a | cut -d ‘|’ -f 2`

          grouptype=`echo $a | cut -d ‘|’ -f 3`

          isingroup=`echo $grp | grep $groupname`

                if [ ! -z «$isingroup» ]

                  then

                        echo «User is a member of $groupname»

                fi

                 for b in `seq 2 254`

                        do

                          if [ -z «`grep ${groupsubnet}.${b} $reservedipfile`» ]

                          then

                            oc4=$b

                            break

                          fi

                        done

        echo ifconfig-push ${groupsubnet}.${oc4} 255.255.0.0> $1

        echo ${groupsubnet}.${oc4} >> $reservedipfile

        return 0

        done

return 1

4.       Файл для хранения занятых IP-адресов необходимо создать:

sudo touch /tmp/reserved_ip.txt

5.       Не помешает очистка файла при перезагрузке OpenVPN-сервера. В данном примере конфиг не приводится.

 

ldapsearch

Для проверки членства в группах безопасности в файле client_connect.sh используется утилита ldapsearch (установлена на этапе выше). Она будет вызываться из скрипта, но предварительно стоит проверить ее работу и сконфигурировать LDAPS.

1.       Задайте в файле /etc/ldap/ldap.conf параметр TLS_CACERT /etc/openvpn/server/root-ca.crt. Это позволит подключаться к контролеру домена с помощью безопасного способа ldapS.

sudo nano /etc/ldap/ldap.conf

#TLS_CACERT     /etc/ssl/certs/ca-certificates.crt

TLS_CACERT /etc/openvpn/server/root-ca.crt

2.       Убедитесь, что команда работает и мы получаем ответ:

ldapsearch -x -H ldaps://hatter2.demo.land -D «sp_openvpn» -w P@ssword -b «DC=demo,DC=land» «(sAMAccountName=alice)»

В моем случае:

·       hatter2.demo.land – контролер домена

·       sp_openvpn – сервисная учетная запись с правами чтения списка пользователей домена + пароль.

·       DC=demo,DC=land – ограничение для поиска учетных записей

·       alice – пример доменной учетной записи, параметры которой мы выводим.

В будущем скрипте (и ответе) нас будет интересовать параметр memberOf (Группа безопасности MS AD).

Файл client_disconnect.sh

После отключения пользователя надо освободить IP-адрес. Данный скрипт занимается этой очисткой.

1.       Создаем файл:

sudo nano /etc/openvpn/server/client_disconnect.sh

2.       Вставляем содержимое:

#!/bin/sh

#Выберите место и создайте файл для базы с адресами:

reservedipfile=/tmp/reserved_ip.txt

    echo «CLIENT DISCONNECTED»

    echo «0=$0 1=$1 2=$2 common_name=$common_name username=$username TempIP=$ifconfig_pool_remote_ip»

    echo «Removing $ifconfig_pool_remote_ip from $reservedipfile»

    sed -i «/${ifconfig_pool_remote_ip}$/d» $reservedipfile

    [ -z «`grep $ifconfig_pool_remote_ip $reservedipfile`» ] && echo «Success: User IP was removed from DB» || echo «Something went wrong»

3.       Делаем файлы выполнимыми:

sudo chmod +x /etc/openvpn/server/client_connect.sh

sudo chmod +x /etc/openvpn/server/client_disconnect.sh

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

sudo dos2unix /etc/openvpn/server/client_disconnect.sh

sudo dos2unix /etc/openvpn/server/client_connect.sh

Файл ldap.conf

Файл содержит конфигурацию openvpn-auth-ldap.so (LDAP аутентификация). Не перепутайте файл с /etc/ldap/ldap.conf – это другой файл, он описан выше.

sudo nano /etc/openvpn/server/ldap.conf

1.       Вставляем содержимое:

<LDAP>

#Адрес вашего контролера домена. При использовании LDAPS нужно использовать имя, заданное в сертификате на сервере.:

  URL             ldap://hatter2.demo.land

#Сервисная учетная запись + пароль с правами чтения списка пользователей.

  BindDN          sp_openvpn

  Password        P@ssword

  Timeout         5

  TLSEnable       yes

  TLSCACertFile   /etc/openvpn/server/root-ca.crt

</LDAP>

 

<Authorization>

#Путь и фильтр для поиска пользователей в домене:

  BaseDN          «DC=demo,DC=land»

  SearchFilter  «(&(objectClass=user)(sAMAccountName=%u))»

</Authorization>

 

2.       Когда все файлы скопированы и отредактированы, запускаем сервис.

sudo systemctl start openvpn-server@example-simple

3.       Убеждаемся, что сервис запущен:

sudo systemctl status openvpn-server@example-simple

4.       Включаем сервис:

sudo systemctl enable openvpn-server@example-simple

 

Аудит

Для аудита событий в моей версии Linux используется файл/var/log/syslog. Просмотр:

sudo tail -f /var/log/syslog

Детализацию аудита можно повысить поменяв значение verb 3, в главном конфигурационном фале example-simple.conf, например, на 9. Возможно, это поможет найти проблему.

 

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

cd /etc/openvpn/server/

sudo openvpn —config ./example-simple.conf

Сервер готов, переходим к конфигурированию клиентов. Не забудьте сохранить/скопировать содержимое файлов:

sudo cat /etc/openvpn/server/ta.key

sudo cat /etc/openvpn/server/root-ca.crt

Это содержимое нам понадобится при генерации конфигурационного файла клиента (профиля).

Клиент

Скачиваем клиент OpenVPN:

https://openvpn.net/community-downloads/

В моем случае OpenVPN 2.6.12 и OpenSSL 3.3.1.

Конфигурация клиента:

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

client

dev tun

proto tcp

# Адрес вашего сервера

remote 51.250.68.123 443

resolv-retry infinite

nobind

remote-cert-tls server

auth-user-pass

persist-key

persist-tun

# cryptoapicert и ISSUER указывает программе-клиенту какой сертификат выбрать для аутентификации. В нашем случае имя корневого сервера (Common Name) банально простое.

cryptoapicert «ISSUER:CA»

auth-nocache

cipher AES-256-GCM

auth SHA256

verb 3

<ca>

——BEGIN CERTIFICATE——

на этом месте должен быть корневой сертификат, мы его скопировали на сервер с именем /etc/openvpn/server/root-ca.crt. Любой сервер подписанный этим корневым центром сертификации и имеющий нужный EKU будет считаться нашими клиентами доверенным. —

——END CERTIFICATE——

</ca>

<tls-crypt>

——BEGIN OpenVPN Static key V1——

на этом месте должен быть ключ, мы его скопировали на сервер с именем /etc/openvpn/server/ta.key. Ключ один на всех клиентов, секретность невысокая. Ключ является дополнительной защитой, в том числе защищающий от DDoS атак. —

——END OpenVPN Static key V1——

</tls-crypt>

Сертификат клиента

Надеюсь, очевидный факт: сертификат клиента должен быть получен до подключения к ВПН, пока клиент находится в сети и «видит» контролер домена и PKI-сервер.

OpenVPN-сервер не требует особых полей и специальной подготовки сертификата пользователя. Более того, клиент даже не проверяет CRL своего сертификата и сертификата сервера. Фактически проверяется только то, что сертификат был выпущен «правильным выпускальщиком». В нашем примере мы дополнительно проверяем, чтобы в сертификате клиента имел EKU Client Authentication, а в сертификате сервера EKU Server Authentication (опция remote-cert-tls конфига). Без этой директивы любой клиент с действующим сертификатом сможет стать сервером и совершить атаку Men-in-the-Middle. И, конечно, помним, что выдавать сертификаты с EKU Server Authentication не стоит всем подряд и на длительный срок.

Если у пользователя не найдется подходящего сертификата, он увидит сообщение:

В этом случае сертификат необходимо установить, и в инфраструктуре Microsoft есть удобное решение, позволяющее максимально автоматизировать этот процесс – PKI и AD CS.

Подготовка шаблона сертификата пользователя

Шаблон можно сделать на основе любого пользовательского шаблона, например, «User».

Определите на ваше усмотрение срок действия сертификата. Не стоит делать длинные сертификаты. Безопаснее включить автоматический перевыпуск и выпускать сертификаты по мере необходимости.

Сертификат должен обязательно содержать это поле, иначе сервер не разрешит пользователю подключение:

Обратите внимание на 2 важных параметра:

  • В случае разрешения «экспорта ключа» сертификат может быть скопирован пользователем на любой другой компьютер. Использовать или нет эту опцию, зависит от политик вашей организации. По моему мнению, для корпоративных устройств запрет должен быть установлен. Дополнительно усилить защиту можно, разметив сертификат в ТРМ-модуле (в статье это не описано, но очень рекомендуется).  В случае необходимости выпуска экспортируемого сертификата (для недоменных пользователей) просто сделайте другой шаблон, доступный только администраторам PKI.

  • Проверьте, чтобы поле «subject» заполнялось автоматически. Нельзя разрешать пользователю заполнять это поле самостоятельно.

Не усложняем конфигурацию – в моем примере все пользователи домена смогут запросить сертификат вручную или через «автовыпуск» при входе в домен:

Для работы «autoenrollment» должна быть дополнительно настроена политика GPO для пользователей «Certificate Services Client – Auto-Enrollment». Тут подробно описано, как это сделать. Следующий рисунок показывает пример конфигурации для формирования поле «Subject» в сертификате. Поле будет являться идентификатором пользователя – на сервере в логах вы сможете определять, какой именно пользователь установил соединение.

В проверках сервера OpenVPN участвует только Common name (CN), полное имя указывать не обязательно. Альтернативные имена заполнять нет необходимости, более того, например, отсутствие поля e-mail в профиле пользователя вызовет ошибку выдачи сертификата.

Если вы настроили PKI правильно, каждый пользователь должен иметь сертификат, выпущенный по вашему шаблону:

На изображении показано имя из сертификата (common name), которое должно совпадать с доменным именем. В скрипте client_connect.sh мы дополнительно проверяем это условие.

Дисклеймер:

Приведенный конфиг PKI не является единственно правильным и исчерпывающим. Обычно в корпоративной среде уже работает достаточно экспертов в области AD + PKI и приведенной информации им достаточно для подготовки правильных шаблонов. Инфраструктура открытых ключей от Microsoft вполне совместима и с «недоменными» машинами, но эти инструменты в статье не описаны. Возможно, стоит посвятить этой теме отдельную статью если будет достаточно отзывов.

Вместо заключения

В данной статье рассмотрен вариант подключения к ВПН для доменного компьютера и пользователя. Решение в такой конфигурации позволяет снизить нагрузку на поддержку ВПН за счет использования экосистемы Microsoft, при этом не снижая безопасность. Причем подключать недоменные компьютеры можно. Организовать выпуск и экспорт сертификата можно с помощью порталов и любых других средств, доступных в экосистеме Microsoft.

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

Дальше вас ждет борьба с MTU и роутингом, но это уже совсем другая история.

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

  • Для лучшей защиты от DDoS OpenVPN рекомендует использовать протокол UDP. TCP удобен в случае организации отказоустойчивого кластера active-active. Кроме, того TCP/443 блокируется внешними провайдерами реже других протоколов.

  • Пароль от сервисной учетной записи ldap сохранен в конфигурационных файлах

  • Клиент может сохранить пароль от ldap на своем компьютере. Необходимо решать организационными мерами.

  • Приложение OpenVPN запускается с правами супер-пользователя (root). Рекомендуется запускать сервис от имени user nobody, group nobody. В данной статье root доступ приводится для упрощения конфигурации.

  • Не включена проверка CRL. Механизм реализован неудобно и по сути бесполезно в нашей схеме. Списки отзывов надо загружать в виде файла-конфига OpenVPN и перезагружать сервер. Сам сервер не «ходит» проверять отозван ли сертификат по ссылке CRL из сертификата. Мы можем этой функций «пожертвовать», так как в дополнение к сертификату мы имеем рабочую интеграцию с ldap. Если пользователь «заблокирован» в домене, даже несмотря на действующий сертификат он не получит доступ в ВПН.

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

  • Нет возможности реализовать условный доступ.

  • Нет возможности реализовать оценку состояния компьютера пользователя перед входом и во время работы.


ссылка на оригинал статьи https://habr.com/ru/articles/843996/