Иногда возникает потребность выдать доступ пользователям только к одному веб ресурсу в компании. Самый очевидный вариант сделать это через урезанный VPN, но тут возникают препятствия в виде девайсов, с которых подключаются пользователи, и качество интернета, которым они пользуются. OpenVPN, который мы пробовали использовать для этих целей, не дал желаемого результата, подключение было медленным и нестабильным.
В поисках более стабильного решения для нашей проблемы мы наткнулись на статью Авторизация с помощью сертификата ssl на nginx + Let's Encrypt.
Попробовали реализовать ее на нашей инфре и столкнулись с проблемой, что сертификаты для пользователей создаются, но при попытке подключиться к целевому веб ресурсу, созданный сертификат не проходит аутентификацию и падает с 404 ошибкой.
После анализа обнаружили проблему, что алгоритм шифрования который используется в статье, не подходит для нашей конфигурации nginx 1.18.0 + Ubuntu 20.04.4 LTS + Let`s Encrypt, он считается небезопасным. Решением этой ситуации было использование шифрования на эллиптических кривых.
cd /etc/nginx/example.com #переходим в нее
mkdir users_certs certs newcerts #создаем необходимые поддиректории
touch index.txt root.config serial #создаем необходимые кофиг-файлы
echo "01" > serial #записываем первое значение в serial от него система начнет отсчет номеров сертификатов
chmod 700 ./
Итоговая файловая структура
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
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;
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
#!/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
В итоге схема создания сертификата выглядит так:
В поисках более стабильного решения для нашей проблемы мы наткнулись на статью Авторизация с помощью сертификата 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 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 добавляем строки с корневым сертификатом для агентов:
ssl_client_certificate /etc/nginx/example.com/certs/root.crt; #путь до корневого сертификатаСтроки добавляются в конфигурацию nginx целевого ресурса, которая обычно лежит в папке /etc/nginx/sites‑enabled/имя_веб_ресурса.
Данные добавляются в раздел server, где слушается 443 https порт.
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, созданный ранее с необходимыми параметрами для автоматического выпуска сертификатов:
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
- На этом настройка аутентификации закончена.
Создание пользовательского сертификата
- Создаем закрытый ключ пользователя (нужно заполнить параметры в скобках):
- Создаем CSR сертификат пользователя (нужно заполнить параметры в скобках):
- Создаем CRT сертификат пользователя (нужно заполнить параметры в скобках):
- Создаем 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. Для этого можете подкорректировать следующий параметр:
-out /etc/nginx/example.com/agent/{email пользователя}.p12
- Готово. Теперь подключаемся к серверу любым SFTP клиентом и скачиваем сертификат с расширением .p12 и передаем его и пароль пользователю.
#!/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.
Эти действия тоже можно зашить в скрипт, но данный функционал я не реализовывал.
Итог
Аутентификация готова, главное не забывать отзывать сертификаты, если пользователю они больше не нужны.Настройка авторизации через ssl сертификат на уровне nginx
Привет уважаемые, хабровчане! Иногда возникает потребность выдать доступ пользователям только к одному веб ресурсу в компании. Самый очевидный вариант сделать это через урезанный VPN, но тут возникают...
habr.com