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

В поисках более стабильного решения для нашей проблемы мы наткнулись на статью Авторизация с помощью сертификата ssl на nginx + Let's Encrypt.
Попробовали реализовать ее на нашей инфре и столкнулись с проблемой, что сертификаты для пользователей создаются, но при попытке подключиться к целевому веб ресурсу, созданный сертификат не проходит аутентификацию и падает с 404 ошибкой.
После анализа обнаружили проблему, что алгоритм шифрования который используется в статье, не подходит для нашей конфигурации nginx 1.18.0 + Ubuntu 20.04.4 LTS + Let`s Encrypt, он считается небезопасным. Решением этой ситуации было использование шифрования на эллиптических кривых.
Собственно сама настройка
Перед началом настройки стоит создать следующую структуру конфиг-файлов:
Лучше ограничить доступ к папке с конфигами, чтобы кто угодно не мог сгенерировать сертификат, поломать настроенный функционал
mkdir /etc/nginx/example.com #создаем папку, в которой будут лежать наши кофиги и сертификаты cd /etc/nginx/example.com #переходим в нее mkdir users_certs certs newcerts #создаем необходимые поддиректории touch index.txt root.config serial #создаем необходимые кофиг-файлы echo "01" > serial #записываем первое значение в serial от него система начнет отсчет номеров сертификатов chmod 700 ./

Создаем собственный самоподписанный доверенный сертификат (в командах отмечены поля которые нужно заполнить), с алгоритмом шифрования на эллиптических кривых:
openssl ecparam -out /etc/nginx/example.com/certs/root.key -name secp384r1 -genkey openssl req -new -x509 -days 500 -sha512 -key /etc/nginx/example.com/certs/root.key -subj /O={Название вашей компании}/emailAddress={email создателя сертификата} -out /etc/nginx/example.com/certs/root.crt
В nginx добавляем строки с корневым сертификатом для агентов:
Строки добавляются в конфигурацию nginx целевого ресурса, которая обычно лежит в папке /etc/nginx/sites‑enabled/имя_веб_ресурса.
Данные добавляются в раздел server, где слушается 443 https порт.
ssl_client_certificate /etc/nginx/example.com/certs/root.crt; #путь до корневого сертификата ssl_verify_client on; keepalive_timeout 70; #ниже необязательные параметры, можно их не указывать fastcgi_param SSL_VERIFIED $ssl_client_verify; fastcgi_param SSL_CLIENT_SERIAL $ssl_client_serial; fastcgi_param SSL_CLIENT_CERT $ssl_client_cert; fastcgi_param SSL_DN $ssl_client_s_dn;
В папке /etc/nginx/ssl/ открываем root.config, созданный ранее с необходимыми параметрами для автоматического выпуска сертификатов:
[ ca ] default_ca = CA_CLIENT [ CA_CLIENT ] dir = /etc/nginx/example.com/ certs = /etc/nginx/example.com/certs new_certs_dir = /etc/nginx/example.com/newcerts database = $dir/index.txt serial = $dir/serial certificate = $dir/certs/root.crt private_key = $dir/certs/root.key default_days = 365 #срок действия пользовательских сертификатов default_crl_days = 7 default_md = md5 policy = policy_anything [ policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = supplied commonName = supplied emailAddress = supplied
На этом настройка аутентификации закончена.
Создание пользовательского сертификата
Создаем закрытый ключ пользователя (нужно заполнить параметры в скобках):
openssl ecparam -out /etc/nginx/example.com/users_certs/{email пользователя}.key -name secp384r1 -genkey
Создаем CSR сертификат пользователя (нужно заполнить параметры в скобках):
openssl req -new -key /etc/nginx/example.com/users_certs/{email пользователя}.key -subj /C={аббревиатура вашей страны}/ST={название вашего региона}/L={название вашего города}/OU={имя фамилия пользователя}/CN={название компании}/emailAddress={email пользователя} -out /etc/nginx/example.com/users_certs/{email пользователя}.csr
Создаем CRT сертификат пользователя (нужно заполнить параметры в скобках):
openssl ca -config /etc/nginx/example.com/root.config -in /etc/nginx/example.com/users_certs/{email пользователя}.csr -out /etc/nginx/example.com/users_certs/{email пользователя}.crt -batch
Создаем P12 сертификат, который непосредственно будем передавать пользователю ( (нужно заполнить параметры в скобках и задать пароль, который будет использоваться для установки сертификата пользователем):
Если не переживаете, что кто-то случайно удалит сертификаты пользователей, то вывод можно настроить сразу в общую папку, из которой будете их забирать по SFTP. Для этого можете подкорректировать следующий параметр:
-out /etc/nginx/example.com/agent/{email пользователя}.p12
openssl pkcs12 -export -clcerts -in /etc/nginx/example.com/users_certs/{email пользователя}.crt -inkey /etc/nginx/example.com/users_certs/{email пользователя}.key -out /etc/nginx/example.com/users_certs/{email пользователя}.p12 -passout pass:{пароль который хотите задать для сертификата}
Готово. Теперь подключаемся к серверу любым SFTP клиентом и скачиваем сертификат с расширением .p12 и передаем его и пароль пользователю.
Чтобы было проще вот весь этот функционал в одном bash-скрипте, который нужно положить в директорию /etc/nginx/example.com:
#!/bin/bash echo "Please, enter user name:" read A Q=$(grep -rn $A index.txt | awk '{print $5}' | awk -F/ '{print $6}' | sed 's/OU=//g') echo "Please, enter user email:" read B echo "Please, enter key pass:" read P if [[ "$A" = "$Q" ]] then echo "this user is already exist" else openssl ecparam -out /etc/nginx/example.com/users_certs/$B.key -name secp384r1 -genkey && openssl req -new -key /etc/nginx/example.com/users_certs/{email пользователя}.key -subj /C={аббревиатура вашей страны}/ST={название вашего региона}/L={название вашего города}/OU={имя фамилия пользователя}/CN={название компании}/emailAddress={email пользователя} -out /etc/nginx/example.com/users_certs/{email пользователя}.csr && openssl ca -config /etc/nginx/example.com/root.config -in /etc/nginx/example.com/users_certs/$B.csr -out /etc/nginx/example.com/users_certs/$B.crt -batch && openssl pkcs12 -export -clcerts -in /etc/nginx/example.com/users_certs/$B.crt -inkey /etc/nginx/example.com/users_certs/$B.key -out /etc/nginx/example.com/users_certs/$B.p12 -passout pass:$P fi
В итоге схема создания сертификата выглядит так:

Установка сертификата
Покажу на примере Google Chrome
Переходим по следующим скриншотам и добавляем сертификат:




Вводим пароль и нажимаем ОК:

Переходим на целевой сайт, выбираем нужный сертификат в выпадающем окне и нажимаем ОК:

Проверяем что все открылось:

Удаление сертификата
Удалите созданные 4 сертификата пользователя из папки /etc/nginx/example.com/users_certs.
Удалите строчку пользователя из /etc/nginx/example.com/index.txt.
Удалите последний номер из файла /etc/nginx/example.com/serial.
Эти действия тоже можно зашить в скрипт, но данный функционал я не реализовывал.
Итог
Аутентификация готова, главное не забывать отзывать сертификаты, если пользователю они больше не нужны.
