Привет, Хабр!

У одного из наших заказчиков вся инфраструктура расположена в Yandex Cloud и для доступа во внутреннюю сеть ко внутренним ресурсам компании а-ля Grafana, Prometheus, Elasticsearch и тд использовался VPN-сервис на базе Self-Hosted OpenVPN. При этом аутентификация пользователей VPN осуществлялась просто по локальным учетным записям на сервере через конфигурацию сервера вида

plugin /usr/lib/openvpn/openvpn-plugin-auth-pam.so login

Указанный выше плагин auth-pam входит в стандартный список файлов/возможностей OpenVPN сервера и в целом позволяет без проблем решать вопрос аутентификации для небольших инсталяций.

Но время шло,количество пользователей и разработчиков становилось все больше, управлять этим сложнее. Для централизованной аутентификации для доступа к ресурсам компании решено было использовать Keycloack (почему именно Keycloack - за рамками данного повествования). Так как VPN является одним из важнейших "кирпичиков" безопасности компании ,решено было перевести и его на эту централизованную аутентификацию.

В ходе исследования вопроса интеграции OpenVPN и Keycloak была найдет довольно неплохой на мой взгляд плагин https://github.com/jkroepke/openvpn-auth-oauth2 ,который ,судя по истории коммитов, развивается и дополняется. Плагин может быть установлен на разные ОС ,как debian-подобные, так и redhat.

Приступим к установке и настройке(в нашем случае ОС сервера Ubuntu 24.04 LTS):

1) Подключаем репозиторий и ставим пакет openvpn-auth-oauth2

curl -L https://raw.githubusercontent.com/jkroepke/openvpn-auth-oauth2/refs/heads/main/packaging/apt/openvpn-auth-oauth2.sources | sudo tee /etc/apt/sources.list.d/openvpn-auth-oauth2.sources
sudo apt update
sudo apt install openvpn-auth-oauth2

2) Создаем клиента на keycloak для openvpn-auth-oauth2

3) После сохранения клиента нового смотрим и копируем себе его секрет ,он нам понадобится позже.

4) Настраиваем плагин openvpn-auth-oauth2

Основной файл конфигурации хранится здесь /etc/openvpn-auth-oauth2/config.yaml

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

http:
  baseurl: "https://vpn.yourdomain.com"
  listen: ":9000"
  secret: "your_secret_password"
oauth2:
  issuer: "https://keycloack.yourdomain/realms/securerealm"
  client:
    id: "openvpn-auth-oauth2"
    secret: "your_secret_from_keycloak_client"
    #oauth2:
openvpn:
  addr: "unix:///run/openvpn/server.sock"  # This is overridden by /etc/sysconfig/openvpn-auth-oauth2
  password: "your_secret_password"

где

baseurl - домен, который будет использоваться для oauth2 и его можно развернуть прям на сервере с vpn и спроксировать через nginx на порт 9000 (порт сервиса openvpn-auth-oauth2). Пример файла конфигурации nginx с полученным сертификатом letsencrypt ниже

listen - порт,на котором будет слушать сервис

secret - пароль для шифрования куки,может быть длиной 16,24 или 32 символа

issuer - домен нашего Keycloack с путем до realm,через который будет осуществляться аутентификация

client.id - id созданного клиента в Keycloack

secret - пароль для аутентификации openvpn-auth-oauth2 при подключении к keycloack(получаем в пункте 3 выше)

openvpn.addr - адрес сокета openvpn-демона

openvpn.password - пароль для аутентификации при подключении к сокету openvpn

5. Настраиваем OpenVPN для возможности работы с openvpn-auth-oauth2 плагином.

а) создаем в /etc/openvpn/server/ конфиг server-keycloak.conf (в конфиге пояснения по интеграции с плагином openvpn-auth-oauth2,остальные опции стандартны)

port 56890
# /etc/openvpn/password.txt - путь к файлу с паролем для управления сокетом openvpn
management /run/openvpn/server.sock unix /etc/openvpn/password.txt
management-client-auth # разрешает управлять openvpn процессом внешними программами в нашем случае плагином
auth-user-pass-optionals # если не включить,сервер будет требовать логин/пароль от клиентов и фейлить подключение,если клиент их не предоставил
auth-gen-token 28800 external-auth # время жизни токена аутентификации
proto udp
dev tun
ca /etc/openvpn/certs/ca.crt
cert /etc/openvpn/certs/server.crt
key /etc/openvpn/certs/server.key
dh /etc/openvpn/certs/dh.pem
server 10.212.245.0 255.255.255.0
client-config-dir /etc/openvpn/ccd
ifconfig-pool-persist /etc/openvpn/ipp.txt
push "route 10.167.0.0 255.255.0.0"
push "route 10.168.0.0 255.255.0.0"
push "route 10.169.0.0 255.255.0.0"
keepalive 10 120
cipher AES-256-GCM
max-clients 100
persist-tun
status /var/log/openvpn/openvpn-keycloak-status.log
verb 3

b) стартуем демон openvpn и добавляем его в автозагрузку

systemctl enable openvpn-server@server-keycloak --now

6) устанавливаем и настраиваем nginx для поддержки работы плагина через домен вида vpn.yourdomain.com

Инструкцию по установке пакетов можно взять здесь

Конфигурация сервиса /etc/nginx/conf.d/vpn.yourdomain.com.conf

server {
    server_name vpn.yourdomain.com;
    listen 80;
    listen [::]:80;
    access_log /var/log/nginx/vpn.yourdomain.com-access.log;
    error_log /var/log/nginx/vpn.yourdomain.com-error.log;

    location ^~ /.well-known/acme-challenge/ {
        default_type "text/plain";
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    server_name vpn.yourdomain.com;
    listen 443 ssl;
    error_log /var/log/nginx/minio.fssoft.ru-error.log;

    ssl_certificate /etc/letsencrypt/live/vpn.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/vpn.yourdomain.com/privkey.pem;

    location ^~ /.well-known/acme-challenge/ {
        default_type "text/plain";
        root /var/www/certbot;
    }

    location / {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://localhost:9000;
    }
}

7) В целом все готово для подключения,осталось только протестировать подключение. Для этого подготавливаем openvpn-конфиг для клиентов и отдаем им. Пример конфигурации:

client
dev tun
proto udp
remote vpn.yourdomain.com 56890
nobind
persist-key
persist-tun
verb 3
<ca>
-----BEGIN CERTIFICATE-----
Здесь ваш CA-сертификат
-----END CERTIFICATE-----
</ca>
cipher AES-256-GCM

Конфиг импортируем в openvpn-клиент - например, openvpn-access и пробуем подключиться.

При аутентификации вы будете перенаправлены на страницу аутентификации в Keycloack:

и в случе успешной аутентификации:

a) увидите такой ответ в браузере

b) openvpn-клиент успешно подключится

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

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

Надеюсь,было полезно и интересно.