Двухфакторная аутентификация (OTP) в OpenVPN с использованием FreeRADIUS и LDAP

2 августа 2024

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

Существующие варианты реализации 2FA для OpenVPN основываются на модуле google-authenticator-libpam для OTP-кодов и плагинов аутентификации OpenVPN libpam-radius-auth, openvpn-plugin-auth-pam, openvpn-auth-ldap.

Все эти варианты имеют ряд недостатков:

  • передача пароля в открытом виде по сети.
  • невозможно ввести логин, пароль и одноразовый код в отдельных полях. Предлагается добавлять OTP-код после пароля, что ухудшает пользовательский опыт. Либо не использовать пароль, а использовать сертификат + OTP-код. Этот вариант усложняет администрирование.
  • невозможность использовать несколько серверов для аутентификации.
  • модуль google-authenticator-libpam использует локальные файлы на сервере для хранения секретов. Это усложняет организацию отказоустойчивости сервиса VPN. А так же ограничивает возможность передать техподдержке генерацию нового секрета или его обновление для TOTP при регистрации пользователей, утере или смене телефона с установленным приложением.

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

  • использовать для аутентификации пользователей логин и пароль из LDAP-каталога.
  • удобно вводить логин, пароль, OTP в отдельные независимые поля интерфейса, а не после пароля или вместо пароля.
  • реализовать различные комбинации использования логина, пароля, OTP в зависимости от задач.
  • обеспечить отказоустойчивость, возможность использования нескольких серверов аутентификации.
  • использовать LDAP-каталог для централизованного управления пользователями.
  • хранить seed-значения для генератора OTP независимо от того, какой LDAP-каталог используется (ActiveDirectory, FreeIPA, lldap или другие).
  • передать функцию создания, обновления seed-значений для генератора OTP техподдержке без необходимости подключаться к серверам OpenVPN.
  • использовать на смартфонах любое приложение для генерации OTP (Яндекс.Ключ, FreeOTP Authenticator и др.).
  • не зависеть от работоспособности облачных провайдеров 2FA (при неработоспособности сервиса 2FA или отсутствии связи между вашими серверами и сервисом 2FA вы автоматически теряете удаленный доступ в свою инфраструктуру).

Рассматриваемый вариант предполагает следующие сценарии использования:

  • доступ пользователей через OpenVPN в инфраструктуру компании.
  • резервный самодостаточный безопасный удаленный доступ в инфраструктуру для администраторов на случай аварий или сбоев.

В обоих сценариях:

  • не предполагается использование персональных сертификатов для каждого пользователя. Однако, это не исключает использования сертификатов в качестве дополнительной меры защиты.
  • для аутентификации используется логин, пароль и OTP.

Исходный код и пример полностью настроенной системы размещены на https://gitverse.ru/strongpass/openvpn-2fa-otp-freeradius-ldap.

Архитектура

Система состоит из двух частей:

  1. Сервер OpenVPN с плагином аутентификации.
  2. Сервер FreeRADIUS с плагином проверки OTP и отправки логина/пароля LDAP-серверу. А так же сервер PostgreSQL для хранения начальных значений генераторов OTP пользователей.

Хранение пользователей и их аутентификацию выполняет любой LDAP-сервер (контроллер домена Active Directory, FreeIPA, lldap, 389ds и другие).

Нужный нам плагин для OpenVPN уже существует и называется openvpn-multi-authentication-plugin. Остается только написать плагин для FreeRADIUS.

Обе части системы можно использовать независимо, например:

  • OpenVPN с плагином можно использовать в сочетании с любыми другими сервисами 2FA.
  • Freeradius и плагин к нему вместе с VPN шлюзами, способными аутентифицировать пользователей по протоколу RADIUS.

Схема работы

  1. Пользователь в клиенте OpenVPN вводит свой логин, пароль и в отдельном поле шестизначный код второго фактора.
  2. Учетные данные с помощью плагина openvpn-multi-authentication-plugin передаются на сервер FreeRADIUS.
  3. Плагин для FreeRADIUS декодирует переданные учетные данные. Сначала проверяет одноразовый код и, в случае успешной проверки, отправляет логин и пароль пользователя на LDAP-сервер .
  4. Если LDAP-сервер успешно аутентифицировал пользователя, FreeRADIUS отправляет положительный ответ.

Проверка OTP перед аутентификацией по логину и паролю позволяет снизить нагрузку на LDAP-сервер при попытках подбора пароля.

Настройка

Покажем настройку системы на одном сервере для сценария резервного самодостаточного безопасного удаленного доступа в инфраструктуру для администраторов на случай аварий или сбоев. В качестве LDAP-сервера используется lldap. FreeRADIUS, lldap, PosgreSQL работают в Docker-контейнерах. OpenVPN и плагин openvpn-multi-authentication-plugin - непосредственно в операционной системе. Предполагается, что на сервере установлен Debian 12. Все необходимые файлы будут располагаться в каталоге /opt/ovpn-freeradius-lldap. Настройка системы выполняется в порядке, облегчающим диагностику ошибок и позволяющим тестировать работу компонентов по отдельности. Поэтому рекомендуется не нарушать порядок настройки.

Устанавливаем Docker.

apt install docker.io docker-compose

Клонируем репозиторий.

git clone https://gitverse.ru/strongpass/openvpn-2fa-otp-freeradius-ldap.git
cd openvpn-2fa-otp-freeradius-ldap

lldap

Создаем нужные каталоги.

sudo mkdir -p /opt/ovpn-freeradius-lldap/lldap

Создаем файл /opt/ovpn-freeradius-lldap/docker-compose.yaml следующего содержания:

networks:
  ovpn-freeradius-lldap_net:
    name: ovpn-freeradius-lldap_net
    ipam:
      config:
        - subnet: 172.21.2.0/24
services:
  lldap:
    image: mirror.gcr.io/lldap/lldap:stable
    container_name: lldap
    networks:
      ovpn-freeradius-lldap_net:
        ipv4_address: 172.21.2.2
    ports:
      - "389:3890"
      # For the web front-end
      - "17170:17170"
    volumes:
      - "./lldap/data:/data"
    environment:
      - UID=1000
      - GID=1000
      - TZ=Europe/Moscow
      - LLDAP_JWT_SECRET=REPLACE_WITH_RANDOM
      - LLDAP_KEY_SEED=REPLACE_WITH_RANDOM
      - LLDAP_LDAP_BASE_DN=dc=acme,dc=corp

Запускаем контейнер.

sudo docker-compose -f /opt/ovpn-freeradius-lldap/docker-compose.yaml up

Создание пользователей

Подключаемся к веб-интерфейсу lldap по адресу http://127.0.0.1:17170/login. Реквизиты по умолчанию для подключения: admin/password.

Создаем пользователей:

  • user-test1 для тестирования аутентификации без OTP.
  • user-test2 для тестирования аутентификации с OTP.
  • freeradius-ldap-user - для подключения плагина FreeRADIUS к lldap.

Создаем группу otp-vpn-users. Пользователям только этой группы будет разрешено подключаться по VPN. Добавьте в эту группу пользователей user-test1, user-test2.

Важно! Добавьте пользователя freeradius-ldap-user в группу lldap_strict_readonly. В противном случае поиск по LDAP-каталогу не будет разрешен.

Останавливаем контейнер.

sudo docker-compose -f /opt/ovpn-freeradius-lldap/docker-compose.yaml down

PostgreSQL

Копируем файл инициализации БД из репозитория.

sudo cp -R src/postgresql /opt/ovpn-freeradius-lldap

В БД у нас будет 2 пользователя:

  • для плагина FreeRADIUS. Пользователь может только читать seed-коды.
  • для технической поддержки. Пользователь может только создавать, обновлять, удалять seed-коды генератора OTP, но не может их читать.

В файле /opt/ovpn-freeradius-lldap/postgresql/init_db/init_db.sql замените логины и пароли пользователей на ваши значения.

Добавляем в файл /opt/ovpn-freeradius-lldap/docker-compose.yaml раздел касающийся PosgreSQL. Содержание файла /opt/ovpn-freeradius-lldap/docker-compose.yaml на этом этапе выглядит так:

networks:
  ovpn-freeradius-lldap_net:
    name: ovpn-freeradius-lldap_net
    ipam:
      config:
        - subnet: 172.21.2.0/24
services:
  lldap:
    image: mirror.gcr.io/lldap/lldap:stable
    container_name: lldap
    networks:
      ovpn-freeradius-lldap_net:
        ipv4_address: 172.21.2.2
    ports:
      - "389:3890"
      # For the web front-end
      - "17170:17170"
    volumes:
      - "./lldap/data:/data"
    environment:
      - UID=1000
      - GID=1000
      - TZ=Europe/Moscow
      - LLDAP_JWT_SECRET=REPLACE_WITH_RANDOM
      - LLDAP_KEY_SEED=REPLACE_WITH_RANDOM
      - LLDAP_LDAP_BASE_DN=dc=acme,dc=corp
  postgres:
    image: mirror.gcr.io/postgres:16.3-bookworm
    container_name: postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: "otpdb"
      POSTGRES_USER: "db_admin"
      POSTGRES_PASSWORD: "IrcfRqB0cQ2G"
      TZ: "Europe/Moscow"
    networks:
      ovpn-freeradius-lldap_net:
        ipv4_address: 172.21.2.3
    ports:
      - "5432:5432"
    volumes:
      - "./postgresql/init_db:/docker-entrypoint-initdb.d"
      - "./postgresql/data:/var/lib/postgresql/data"
    healthcheck:
      test: [ "CMD-SHELL", "pg_isready -U db_admin -d otpdb" ]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s

Запускаем контейнеры и убеждаемся, что нет сообщений об ошибках.

sudo docker-compose -f /opt/ovpn-freeradius-lldap/docker-compose.yaml up 

После первого запуска и успешной инициализации базы нужно удалить файл /opt/ovpn-freeradius-lldap/postgresql/init_db/init_db.sql и закомментировать строку ./postgresql/init_db:/docker-entrypoint-initdb.d в файле /opt/ovpn-freeradius-lldap/docker-compose.yaml

Управление секретами генератора OTP

Для создания, обновления и удаления секретов написан скрипт на python, который можно отдать техподдержке. Скрипт расположен в каталоге репозитория openvpn-2fa-otp-freeradius-ldap/src/manage_otp.

Для подключения к базе данных создайте файл config.yaml. Укажите параметры по аналогии с примером ниже.

host: 172.21.2.3
port: 5432
db_name: otpdb
user: helpdesk_user

В параметре user указывается логин пользователя технической поддержки, созданный в postgres. В нашем примере - это helpdesk_user.

Находясь в каталоге openvpn-2fa-otp-freeradius-ldap установите зависимости и приложение в виртуальное окружение.

sudo apt install pipx
pipx ensurepath
pipx install --include-deps ./src/manage_otp

После установки будет доступна команда manage-otp.

Создайте секрет генератора OTP для пользователя user-test2.

manage-otp -c config.yaml create user-test2

По окончании работы приложение выведет строку для настройки генератора OTP, а так же создаст файл с QR-кодом. Добавьте на смартфоне в приложение для генерации OTP учетную запись с полученным секретом.

Останавливаем контейнеры.

sudo docker-compose -f /opt/ovpn-freeradius-lldap/docker-compose.yaml down

Плагин для FreeRADIUS

Находясь в каталоге openvpn-2fa-otp-freeradius-ldap копируем файлы плагина из репозитория.

mkdir -p /opt/ovpn-freeradius-lldap/freeradius
cp -r src/freeradius/freeradius-config /opt/ovpn-freeradius-lldap/freeradius/
cp -r src/freeradius/freeradius-plugin /opt/ovpn-freeradius-lldap/freeradius/

Создаем файл /opt/ovpn-freeradius-lldap/freeradius/freeradius-plugin/strongpass-otp/config.yml. Пояснения к параметрам можно найти в файле openvpn-2fa-otp-freeradius-ldap/src/freeradius/freeradius-plugin/config.yml.example.

otp_option: 2
auth_test_user: "user-test1"
db:
  host: 172.21.2.3
  port: 5432
  db_name: otpdb
  user: freeradius_user
  # замените на свой пароль.
  password: "7xFWnt8BN6Ww"
ldap:
  host: 172.21.2.2
  port: 3890
  use_ssl: false
  # для LLDAP
  search_base: "ou=people,dc=acme,dc=corp"
  search_filter: "(&(uid=%s)(memberof=cn=otp-vpn-users,ou=groups,dc=acme,dc=corp))"
  bind_dn: "uid=freeradius-ldap-user,ou=people,dc=acme,dc=corp"
  # замените на свой пароль
  password: "587MVkETPhRf"

Важно! На этапе настройки и тестирования для облегчения ввода пароля и OTP устанавливаем для параметра otp_option значение 2. В этом варианте OTP вводится пользователем сразу после пароля (6 цифр). Пример: password349307. После окончания настройки, тестирования и при использовании с OpenVPN нужно будет установить значение opt_option: 1.

FreeRADIUS

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

Находясь в каталоге openvpn-2fa-otp-freeradius-ldap выполняем команду:

sudo docker build -f src/freeradius/docker/Dockerfile -t freeradius:otp .

Добавляем в файл /opt/ovpn-freeradius-lldap/docker-compose.yaml раздел касающийся FreeRADIUS. Содержимое файла на этом этапе выглядит так:

networks:
  ovpn-freeradius-lldap_net:
    name: ovpn-freeradius-lldap_net
    ipam:
      config:
        - subnet: 172.21.2.0/24
services:
  lldap:
    image: mirror.gcr.io/lldap/lldap:stable
    container_name: lldap
    networks:
      ovpn-freeradius-lldap_net:
        ipv4_address: 172.21.2.2
    ports:
      - "389:3890"
      # web frontend
      - "17170:17170"
    volumes:
      - "./lldap/data:/data"
    environment:
      - UID=1000
      - GID=1000
      - TZ=Europe/Moscow
      - LLDAP_JWT_SECRET=REPLACE_WITH_RANDOM
      - LLDAP_KEY_SEED=REPLACE_WITH_RANDOM
      - LLDAP_LDAP_BASE_DN=dc=acme,dc=corp
  postgres:
    image: mirror.gcr.io/postgres:16.3-bookworm
    container_name: postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: "otpdb"
      POSTGRES_USER: "db_admin"
      POSTGRES_PASSWORD: "IrcfRqB0cQ2G"
      TZ: "Europe/Moscow"
    networks:
      ovpn-freeradius-lldap_net:
        ipv4_address: 172.21.2.3
    ports:
      - "5432:5432"
    volumes:
      - "./postgresql/init_db:/docker-entrypoint-initdb.d"
      - "./postgresql/data:/var/lib/postgresql/data"
    healthcheck:
      test: [ "CMD-SHELL", "pg_isready -U db_admin -d otpdb" ]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s
  freeradius:
    image: "freeradius:otp"
    container_name: freeradius
    # Раскомментируйте строчку ниже для отладки freeradius
    # command: "freeradius -X"
    environment:
      - TZ="Europe/Moscow"
    networks:
      ovpn-freeradius-lldap_net:
        ipv4_address: 172.21.2.4
    ports:
      - "1812:1812"
      - "1813:1813"
    volumes:
      - "./freeradius/freeradius-config/sites-enabled:/etc/freeradius/sites-enabled"
      - "./freeradius/freeradius-config/mods-enabled/python3:/etc/freeradius/mods-enabled/python3"
      - "./freeradius/freeradius-config/clients.conf:/etc/freeradius/clients.conf"
      - "./freeradius/freeradius-plugin/strongpass-otp:/opt/strongpass-otp"
      - "./freeradius/logs/:/var/log/freeradius"

Указываем в файле /opt/ovpn-freeradius-lldap/freeradius/freeradius-config/clients.conf настройки для подключения клиентов.

client docker-net {
	ipv4addr=172.21.2.0/24
	secret=MOhqCOukFGp02lRC
	proto=udp
	nas_type=other
}

Установим разрешения для файлов.

sudo chmod -R  600  /opt/ovpn-freeradius-lldap/freeradius/freeradius-config/mods-enabled
sudo chmod -R  600  /opt/ovpn-freeradius-lldap/freeradius/freeradius-config/sites-enabled
sudo chmod 600  /opt/ovpn-freeradius-lldap/freeradius/freeradius-config/clients.conf

Запускаем контейнеры и убеждаемся, что нет сообщений об ошибках.

sudo docker-compose -f /opt/ovpn-freeradius-lldap/docker-compose.yaml up 

Журнал FreeRADIUS пишется в /opt/ovpn-freeradius-lldap/freeradius/logs/radius.log.

Проверяем возможность подключиться тестовым пользователем, который будет использоваться плагином openvpn-multi-authentication-plugin для мониторинга работоспособности аутентификации с выбранными серверами.

Напомним, что пользователей и группу мы создали на шаге "Создание пользователей". Перед тестированием убедитесь, что:

  • в файле /opt/ovpn-freeradius-lldap/freeradius/freeradius-plugin/strongpass-otp/config.yml значение параметра auth_test_user совпадает с логином выбранного нами пользователя (в нашем случае user-test1). Для указанного в этом параметре пользователя проверка OTP не производится.
  • пользователи user-test1, user-test2 входят в группу otp-vpn-users.
  • в файле /opt/ovpn-freeradius-lldap/freeradius/freeradius-plugin/strongpass-otp/config.yml в значении параметра ldap.search_filter указана группа otp-vpn-users.

Скачиваем последнюю версию плагина из раздела "Releases" репозитория openvpn-multi-authentication-plugin. В архиве находится radius-client, который позволит протестировать нам правильность взаимодействия FreeRADIUS, плагина FreeRADIUS и LDAP сервера.

Выполняем команду (вместо <пароль> укажите свое значение). Значение параметра -s должно совпадать со значением параметра secret в /opt/ovpn-freeradius-lldap/freeradius-config/clients.conf

./radius-client --addr 172.21.2.4:1812 --proto pap -u user-test1 -p <пароль> -s MOhqCOukFGp02lRC

Если мы получили код ответа Access-Reject, то необходимо проверить журнал FreeRADIUS (/opt/ovpn-freeradius-lldap/freeradius/logs/radius.log) на предмет ошибок.

В случае успеха ответ будет следующий:

Settings from command line:
Server: 172.21.2.4:1812
Secret: MOhqCOukFGp02lRC
Username: user-test
Password: 5bc5ce448a71
Protocol: pap
NAS ID:
NAS Port: 443

Response contents

Code: 2, Access-Accept
Service Type:  ServiceType(0)
Framed-Protocol:  FramedProtocol(0)
Framed-IPAddress:  <nil>
Framed-Netmask:  <nil>
Framed-Routing:  None
MS-Primary-DNS-Server:  <nil>
MS-Secondary-DNS-Server:  <nil>

Теперь протестируем аутентификацию пользователя с OTP. OTP указывается сразу после пароля (6 цифр). Пример: password349307.

./radius-client --addr 172.21.2.4:1812 --proto pap -u user-test2 -p <пароль + OTP код> -s MOhqCOukFGp02lRC

При успешной проверке OTP и логина/пароля мы получим ответ Access-Accept:

Settings from command line:
Server: 172.21.2.4:1812
Secret: MOhqCOukFGp02lRC
Username: user-test2
Password: 5bc5ce448abad489327
Protocol: pap
NAS ID:
NAS Port: 443

Response contents

Code: 2, Access-Accept
Service Type:  ServiceType(0)
Framed-Protocol:  FramedProtocol(0)
Framed-IPAddress:  <nil>
Framed-Netmask:  <nil>
Framed-Routing:  None
MS-Primary-DNS-Server:  <nil>
MS-Secondary-DNS-Server:  <nil>

При отсутствии ошибок можно переходить к настройке OpenVPN.

Если FreeRADIUS и плагин будут использоваться с любым другим VPN шлюзом, способным аутентифицировать пользователей по протоколу RADIUS, то на этом настройка закончена, и система готова к работе.

Плагин для OpenVPN

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

Скачиваем последнюю версию плагина из раздела "Releases" репозитория openvpn-multi-authentication-plugin. Т.к. используется Debian 12, нам нужен файл openvpn-multi-authentication-plugin-linux-libssl3-amd64.tar.gz. Копируем из скачанного архива каталоги auth-service и openvpn-plugin в /opt/ovpn-freeradius-lldap/openvpn. Указываем в файле /opt/ovpn-freeradius-lldap/openvpn/auth-service/config.yml нужные значения параметров. Комментарии к каждому параметру можно найти в самом файле config.yml. Содержание файла:

web_server:
  listen_address: 127.0.0.1
  port: 11245
  auth_api_key: 7a8lkmq73z15x06guhuh
  https:
    enable: false
    private_key: ""
    certificate: ""
  status:
    enable: false
    path: ""
    api_key: ""
log:
  file: /var/log/auth-service.log
  level: error
auth_provider:
  type: radius
  auth_check:
    enable: false
  radius:
    nas_id: "openVPN"
    nas_ipv4_address: ""
    nas_port: 443
    servers:
	    # параметры подключения к FreeRADIUS
      - name: server1
        address: 172.21.2.4
        port: 1812
        protocol: pap
        secret: MOhqCOukFGp02lRC
        response_timeout_sec: 15

Создаем файл /etc/systemd/system/auth-service.service следующего содержания:

[Unit]
Description=OpenVPN plugin. Auth Service
After=network.target 
Wants=network.target 

[Service]
Type=simple
ExecStart=/opt/ovpn-freeradius-lldap/openvpn/auth-service/auth-service -config /opt/ovpn-freeradius-lldap/openvpn/auth-service/config.yml


[Install]
WantedBy=multi-user.target

Запускаем сервис и проверяем журнал на наличие ошибок:

systemctl enable --now auth-service
journalctl -xeu auth-service.service
systemctl status auth-service

В случае отсутствия ошибок в журналах systemd и auth-service проверяем, что можем аутентифицироваться с помощью тестового пользователя user-test1. Значение заголовка X-Api-Key берем из параметра web_server.auth_api_key файла /opt/ovpn-freeradius-lldap/openvpn/auth-service/config.yml. Вместо <user_password> укажите свое значение.

 curl -H "X-Api-Key: 7a8lkmq73z15x06guhuh" -X POST --data '{"u": "user-test1", "p": "<user_password>", "client_ip": "127.0.0.1"}' -i http://127.0.0.1:11245/auth

При успешной аутентификации получим ответ с кодом 200.

Важно! После окончания проверки в файле /opt/ovpn-freeradius-lldap/freeradius/freeradius-plugin/strongpass-otp/config.yml установите otp_option: 1 и перезапустите контейнеры. OpenVPN передает пароль и OTP в закодированном формате. В случае несоответствия настроек плагина FreeRADIUS аутентификация клиентов OpenVPN не будет успешной.

Далее настраиваем непосредственно плагин OpenVPN. Указываем в файле /opt/ovpn-freeradius-lldap/openvpn/openvpn-plugin/config.yml нужные значения параметров. Комментарии к каждому параметру можно найти в самом файле config.yml.

Важно! Значение параметра:

  • auth_service.url должно соответствовать значениям web_server.listen_address, web_server.port, https.enable конфигурационного файла auth-service.
  • auth_service.api_key должно совпадать со значением web_server.auth_api_key конфигурационного файла auth-service.

Содержание /opt/ovpn-freeradius-lldap/openvpn/openvpn-plugin/config.yml:

https_verify_cert: false
auth_service:
  - name: Service 1
    url: http://127.0.0.1:11245
    api_key: 7a8lkmq73z15x06guhuh
    monitoring:
	  # т.к. мониторинг в нашем примере не используется, то оставляем значения по умолчанию. 
      path:  /monitor/12345
      api_key: 123456789
log:
  file: /var/log/openvpn-plugin.log
  level: error
auth:
  connect_timeout_sec: 5
  
monitoring:
  enable: false
  check_interval_sec: 5

Сервер OpenVPN

Настройка сервера OpenVPN с нуля не будет подробно описываться, т.к. в интернете есть много статей на эту тему. Остановимся только на основных моментах и особенностях настройки. Предполагается, что на сервере установлен OpenVPN версии не ниже: 2.6.3
OpenVPN и плагин к нему работает непосредственно в операционной системе, а не в Docker-контейнере. Пользователям не требуются индивидуальные сертификаты для подлючения к серверу. Для аутентификации пользователей используется логин, пароль и OTP.

Пример конфигурационного файла сервера.

dev tun
port 443
proto tcp4
persist-tun
persist-key
data-ciphers CHACHA20-POLY1305:AES-256-GCM:AES-256-CBC
data-ciphers-fallback AES-256-CBC
auth SHA512
tls-server
resolv-retry infinite
auth-nocache
reneg-sec 0
keepalive 10 120
tls-version-min 1.2
verify-client-cert none
topology subnet
server 192.168.200.0 255.255.255.0
#push "redirect-gateway def1"
push "route 192.168.200.0 255.255.255.0"
push "dhcp-option DNS 192.168.200.2"
push "dhcp-option DNS 8.8.8.8"
push "dhcp-option DOMAIN acme.corp"
push "dhcp-option DOMAIN-SEARCH acme.corp"

ca /etc/openvpn/server/ca.crt
cert /etc/openvpn/server/openvpn-server.crt
key /etc/openvpn/server/openvpn-server.key
dh /etc/openvpn/server/dh.pem
tls-crypt /etc/openvpn/server/ta.key

plugin /opt/ovpn-freeradius-lldap/openvpn/openvpn-plugin/libopenvpn_auth_plugin.so --config /opt/ovpn-freeradius-lldap/openvpn/openvpn-plugin/config.yml

status /var/log/openvpn-status.log
log    /var/log/openvpn.log
verb 4

Ключевые параметры, определяющие логику аутентификации:

verify-client-cert none
plugin /opt/ovpn-freeradius-lldap/openvpn/openvpn-plugin/libopenvpn_auth_plugin.so --config /opt/ovpn-freeradius-lldap/openvpn/openvpn-plugin/config.yml

Запускаем сервер OpenVPN и проверяем журналы сервера и плагина на наличие ошибок.

Клиент OpenVPN

В конфигурационном файле клиента нужно добавить параметр static-challenge "Введите одноразовый пароль" 1 . После этого в графическом интерфейсе клиента OpenVPN для Windows и в TunnelBlick для MacOS будет показываться отдельное поле для ввода OTP. В Linux при запуске openvpn из командой строки будет дополнительно запрошен OTP.

Пример конфигурационного файла клиента:

dev tun
persist-tun
persist-key
data-ciphers CHACHA20-POLY1305:AES-256-GCM:AES-256-CBC
data-ciphers-fallback AES-256-CBC
auth SHA512
tls-client
client
resolv-retry infinite
remote vpn1.acme.corp 443 tcp4
remote vpn2.acme.corp 443 tcp4
remote vpn3.acme.corp 443 tcp4
server-poll-timeout 10
lport 0
auth-user-pass
auth-nocache
verify-x509-name "openvpn.acme.corp" name
remote-cert-tls server
reneg-sec 0
allow-pull-fqdn
verb 4
setenv CLIENT_CERT 0
static-challenge "Введите одноразовый код" 1
<ca>
-----BEGIN CERTIFICATE-----
MIIB9TCCAXygAwIBAgIUNArxOIYVEI6U9C6GC8hK5yIfk98wCgYIKoZIzj0EAwQw
................................................................
I0QfT71JPXFUAnIbgVDh4DZfZ6k4uUECMA9C2ypGO2ezDjO5rF+odCW1F1DeiIWD
PvoUN4aHG4Y7/cxGIP9fK6MWq8TYxRCqbg==
-----END CERTIFICATE-----
</ca>

<tls-crypt>
#
# 2048 bit OpenVPN static key
#
-----BEGIN OpenVPN Static key V1-----
035d7bb282df6c5b981c2b733d6051a8
................................
08ab402ee7605146e38ef097979f1ebc
-----END OpenVPN Static key V1-----
</tls-crypt>

Ключевые параметры конфигурации клиента касающиеся аутентификации:

auth-user-pass
setenv CLIENT_CERT 0
static-challenge "Введите одноразовый код" 1

Пробуем подключиться к серверу OpenVPN под учетной записью user-test2 с указанием одноразового пароля (настройка генератора OTP производилась на шаге "Управление секретами генератора OTP"). На этом настройка закончена, система готова к эксплуатации.