OpenVPN - 2fa через PrivacyIdea

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

Для интеграции взаимодействия OpenVPN c PrivacyIdea, на стороне сервера OpenVPN собирается radius-плагин. Через этот плагин происходит взаимодействие с сервером Radius, который в свою очередь валидирует пользователей через http-запрос в PrivacyIdea.

Ниже представлена схемка взаимосвязи сервисов:

Установка OpenVPN

OpenVPN сервер будет разворачивать на базе Almalinux8, поэтому ставим пакеты - openvpn и easy-rsa.

Пакет easy-rsa нужен нам для создания цепочек сертификатов и упрощения работы выпуска сертификатов.

[root@ovpn ~]# yum install openvpn easy-rsa

В каталоге /usr/share/easy-rsa/3 будет содержаться бинарный файл с утилитой и ее конфиг.

Создадим симлинк на бинарь в каталоге для исполняемых файлов:

[root@ovpn ~]# ln -s /usr/share/easy-rsa/3/easyrsa /usr/local/sbin/easyrsa

Под хранение объектов нашей маленькой PKI инфраструктуры создадим отдельный каталог:

[root@ovpn ~]# mkdir /etc/openvpn/keys

Проваливаемся внутрь этого каталога:

[root@ovpn ~]# cd /etc/openvpn/keys/

Проинициализируем новый PKI инстанс:

[root@ovpn keys]# easyrsa init-pkiinit-pki complete; you may now create a CA or requests.Your newly created PKI dir is: /etc/openvpn/keys/pki

Теперь нужно сгенерить сертификат нашего рутового CA. Что бы упростить процесс создания цепочки ключей в переменные окружение импортнем переменные с объектами сертификата:

[root@ovpn keys]# vi .vars---export KEY_COUNTRY="RU"export KEY_PROVINCE="MOSCOW"export KEY_CITY="MOSCOW"export KEY_ORG="NIXHUB"export KEY_EMAIL="Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра."export KEY_CN="OVPN.NIXHUB.RU"export KEY_OU="ADMINS"export KEY_NAME="ovpn.nixhub.ru"export KEY_ALTNAMES="openvpn.nixhub.ru"

И импортируем переменные в наше окружение:

[root@ovpn keys]# . ./.vars

Далее генерим рутой сертификат:

[root@ovpn keys]# easyrsa build-ca

После запуска утилита запросит password фразу (Passphrase), вводим ее. В запрос ввода Common Name я указываю имя vpn-сервера.

Теперь сгенерим ключ Диффи-Хеллмана:

[root@ovpn keys]# easyrsa gen-dh

Для выпуска сертификата vpn-сервера сначала нужно сгенерить для него запрос на выпуск сертификата:

[root@ovpn keys]# easyrsa gen-req ovpn-server nopass

В запросе ввода Common Name опционально указываем имя нашего vpn-сервера.

Подписываем запрос и выпускаем сертификат:

[root@ovpn keys]# easyrsa sign-req server ovpn-server

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

Confirm request details: yes

Далее будет представлено окно для ввода секретной фразы ключа, которую мы указывали при создании корневого сертификата. Указываем ее и жмем enter.

Создадим дополнительный сертификат для усиления безопасности, который будет использоваться для TLS Control channel.:

[root@ovpn keys]# openvpn --genkey --secret /etc/openvpn/server/tc.key

По итогу мы получаем базовый состав объектов, необходимых для работы openvpn-сервера:

  • pki/ca.crt - Публичный ключ нашего CA, им можно делиться с нашими клиентами;
  • pki/private/ca.key - Закрытый ключ нашего CA, ни с кем не делимся им;
  • pki/dh.pem - ключ Диффи Хелмана;
  • pki/issued/ovpn-server.crt - открытый сертификат для нашего vpn-сервера;
  • pki/private/ovpn-server.key - закрый ключ от сертфиката vpn-сервера.

Настройка сервера почти завершена, остается только написать конфиг vpn-сервера:

[root@ovpn ~]# vim /etc/openvpn/server/server.conf---port 59182proto udpdev tunca /etc/openvpn/keys/pki/ca.crtcert /etc/openvpn/keys/pki/issued/ovpn-server.crtkey /etc/openvpn/keys/pki/private/ovpn-server.keydh /etc/openvpn/keys/pki/dh.pemauth SHA256cipher AES-256-CBCtls-version-min 1.2tls-crypt /etc/openvpn/server/tc.keyserver 10.8.1.0 255.255.255.0route 10.8.2.0 255.255.255.0client-to-clientkeepalive 10 120comp-lzoexplicit-exit-notify 1persist-keypersist-tunstatus /var/log/openvpn/openvpn-status.loglog /var/log/openvpn/openvpn.loguser nobodygroup nobodyverb 3

Конфигурационный файл содержит директивы:

  • port - в значение указывается порт, на котором слушает сервер;
  • proto - используемый протокол - tcp/udp;
  • ca/cert/key/dh - в значении этих директив указываются соответствующие сертификаты;
  • auth - тип алгоритма шифрования;
  • cipher - тип алгоритма шифрования данных, передаваемых через vpn;
  • tls-version-min - указывается минимальная используемая версия;
  • tls-crypt - ключ шифрования TLS control channel;
  • server - тут в формате указывается подсеть туннеля;
  • route - тут в аналогичном формате - указывается маршрут к подсети, к которой будем ходить из под vpn.
  • client-to-client - эта опция разрешает взаимодействие клиентов, друг с другом;

Создаем каталог под хранение логов:

[root@ovpn]# mkdir /var/log/openvpn

И открываем порт на фаерволе:

[root@ovpn keys]# firewall-cmd --add-port=59182/udp --permanent[root@ovpn keys]# firewall-cmd --reload

Ну и запускаем openvpn-сервер:

[root@ovpn]# systemctl enable --now openvpn-server@server

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

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

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

#!/bin/bashproto="udp"port="59182"server="ovpn.nixhub.ru"confdir="/etc/openvpn/client"echo -n "Enter user name: "read userecho "Protect the private key with a password?"echo " 1) No, passwordless client"echo " 2) Yes, use a password for the client"until [[ $pass =~ ^[1-2]$ ]]; do     read -rp "Select an option [1-2]: " -e passdoneclientexist=$(tail -n +2 /etc/openvpn/keys/pki/index.txt | grep -c -E "/CN=$user\$")     if [[ $clientexist == ‘1’ ]]; then       echo ""       echo "The specified client name was already found in easy-rsa"     exit     else       cd /etc/openvpn/keys/ || return       case $pass in            1)                easyrsa build-client-full "$user" nopass                ;;            2)                easyrsa build-client-full "$user"                ;;            esac            echo "Client $user added."     fitouch /etc/openvpn/ccd/$usermkdir -p $confdirecho "dev tunproto $protoremote $server $portclientresolv-retry infiniteremote-cert-tls serverauth SHA256cipher AES-256-CBCpersist-keypersist-tunresolv-retry infinitenobindcomp-lzoverb 3" > $confdir/$user.ovpn{ echo "<ca>"  cat "/etc/openvpn/keys/pki/ca.crt"  echo "</ca>"  echo "<cert>"  awk ‘/BEGIN/,/END/’ "/etc/openvpn/keys/pki/issued/$user.crt"  echo "</cert>"  echo "<key>"  cat "/etc/openvpn/keys/pki/private/$user.key"  echo "</key>"  echo "<tls-crypt>"  cat "/etc/openvpn/server/tc.key"  echo "</tls-crypt>"} >> $confdir/$user.ovpn

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

Сохраняемся и выходим.

Делаем скрипт исполняемым:

[root@ovpn]# chmod +x /etc/openvpn/add_vpn_user.sh

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

[root@ovpn]# mkdir /etc/openvpn/client/

Ну и запускаем скриптец:

[root@ovpn openvpn]# ./add_vpn_user.sh

После запуска, по условию этого скриптика у нас попросят ввести имя пользователя, и выбрать действие которое добавляет секретный ключ. Ну и последним, нужно будет ввести phrase от рутового CA.

Готовая конфига будет лежать в каталоге - /etc/openvpn/client. Берем ее, и переносим на устройство нашего клиента. Затем пробуем подключится.

Настройка Radius-плагина

К сожалению, готового билда я не нашел. Единственный выход это самостоятельная сборка. Для сборки требуется предварительно поставить пакет libgcrypt/libgcript-devel:

[root@ovpn openvpn]# yum install libgcrypt libgcrypt-devel gcc-c++ make

Затем с гитхаба качаем исходники:

[root@ovpn openvpn]# cd /tmp[root@ovpn tmp]# wget https://github.com/ValdikSS/openvpn-radiusplugin/archive/refs/tags/v2.2.tar.gz

Распаковываем архив:

[root@ovpn tmp]# tar -zxvf v2.2.tar.gz

Переходим в каталог с исходниками и запускаем сборку:

[root@ovpn tmp]# cd openvpn-radiusplugin-2.2[root@ovpn openvpn-radiusplugin-2.2]# make

Плагин собирается буквально за 3 минуты. Далее готовый исходник либы и конфигу плагина, закидываем в каталог - /etc/openvpn/:

[root@ovpn openvpn-radiusplugin-2.2]# cp radiusplugin.so /etc/openvpn/[root@ovpn openvpn-radiusplugin-2.2]# cp radiusplugin.cnf /etc/openvpn/

Теперь нужно перенастроить работу openvpn–сервера на использование плагина. Открываем конфиг и добавляем:

[root@ovpn openvpn-radiusplugin-2.2]# vim /etc/openvpn/server/server.conf---plugin /etc/openvpn/radiusplugin.so /etc/openvpn/radiusplugin.cnf

Настраиваем конфиг плагина:

[root@ovpn openvpn-radiusplugin-2.2]# vim /etc/openvpn/radiusplugin.cnf---...NAS-IP-Address=10.8.5.10 ...OpenVPNConfig=/etc/openvpn/server/server.conf...nonfatalaccounting=trueserver{        acctport=1813        authport=1812        name=10.8.5.45        retry=1        wait=5        sharedsecret=PASSWORD}

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

  • NAS-IP-Address - в значении указывается ip адрес радиус-клиента;
  • OpenVPNConfig - тут прописывается путь к конфигу openvpn-сервера;
  • nonfatalaccounting - значение true, включает игнорирование ошибок при radius accounting. В нашем случаи accounting не нужен;
  • server - в этом блоке описываются параметры подключения к радиус-серверу;acctport - тут указывается порт, для accounting событий (но в нашем случаи можно упустить);
  • authport - тут указывается порт сервера для авторизации
  • name - ip адрес сервера
  • wait/retry - опции указывающие количество повторных запросов на radius сервер. И время ожидания;
  • sharedsecret - пароль для подключения клиента.

Сохраняемся и перезапускаем openvpn-сервер:

[root@ovpn openvpn-radiusplugin-2.2]# systemctl restart openvpn-server@server

На данном этапе идем на радиус сервер, который крутиться на том же серваке что и сервис PrivacyIdea.

На сервере редактируем файл с клиентами:

[root@mfa01 ~]# vim /etc/raddb/clients.conf---client OpenVPN1 {        ipaddr  = 10.8.5.10 # OpenVPN Server tablets        netmask = 32        secret  = ‘PASSWORD’ #shared secret}

И перезапускаем radius-сервер:

[root@mfa01 ~]# systemctl restart radiusd

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

auth-user-pass

После добавления этой опции, при подключении у пользователя будет отображаться окно с вводом otp-кода. Также не забываем в скриптец add_vpn_user.sh тоже добавить эту директиру.

Тестируем связку

Подключаемся в WebUI PrivacyIdea и создаем новый токен для тестового пользователя:

Полученный qr-code сканируем в приложении google-authenticator.

Далее создаем новый клиентский конфиг для тестового пользователя. Напомню конфиг генерим через ранее предоставленный мной скриптец:

[root@ovpn ~]# cd /etc/openvpn/[root@ovpn openvpn]# ./add_vpn_user.shEnter user name: testtest

Полученный конфиг переносим на клиентское устройство, затем добавляем новый профиль в openvpn-клиенте:

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

В окне ввода пароля вводим пинкод+код из приложения Google Authenticator:

После ввода пина, мы подключаемся:

В аудит логах на стороне PrivacyIdea, будут события об успешности авторизации пользователя:

Если мы удаляем токен пользователя или блокируем, то доступ к vpn у пользователя закрывается. Что полезно..