Привет уважаемые, хабровчане!
Иногда возникает потребность выдать доступ пользователям только к одному веб ресурсу в компании. Самый очевидный вариант сделать это через урезанный 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.
Эти действия тоже можно зашить в скрипт, но данный функционал я не реализовывал.
Итог
Аутентификация готова, главное не забывать отзывать сертификаты, если пользователю они больше не нужны.