Продолжаем тему прозрачной аутентификации для юзеров. Сегодня на повестке дня как прозрачно разграничить доступ к сайту для разных пользователей.
Как понятно из названия, будем делать доступ к сайту по сертификату. На самом деле, вещь очень приятная - при наличии сертификата не нужно вводить ни паролей, ни логинов. Просто заходишь на нужную страницу и получаешь то, что нужно. Удобно, когда, к примеру, нужно убрать админку сайта или похожие вещи.
Я исхожу из предположения, что читатели представляют себе, что такое TLS и сертификаты, как и с чем это едят и зачем это нужно. Если нет - вам в википедию.
Далее я буду расписывать все это в виде пошагового мануала, который можно достаточно легко и быстро адаптировать для своих нужд.
Первым делом генерируем ключ для CA и сразу же сертификат:
$ openssl genrsa -des3 -out ca.key 4096
$ openssl req -new -x509 -days 90 -key ca.key -out ca.crt
В примере выше моя паранойя во все красе - сертификат дейсвителен 90 дней. Если вам нужно больше - ставьте больше, но не советую. Утечки сущетвуют всегда, ограничение по времени это в том числе и механизм безопасности.
При обновлении сертификата имеет смысл не менять данные, которые вы вводите при его генерации.
Клиентский сертификат генерируется аналогично CA, с той лишь разницей, что есть промежуточная ступень - создание CSR (Certificate Signing Request).
$ openssl genrsa -des3 -out user.key 4096
$ openssl req -new -key user.key -out user.csr
$ openssl x509 -req -days 90 -in user.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out user.crt
Опять срок действия 90 дней, будьте внимательны!
Следующим шагом необходимо привести сертификат в состояние, в котором его можно импортировать в браузер:
$ openssl pkcs12 -export -out user.pfx -inkey user.key -in user.crt -certfile ca.crt
На выходе получаем сертификат, который можно отдавать пользователям.
Теперь нужно настроить Nginx, чтобы он проверял сертификат и, в зависимости от результатов проверки, отдавал разные данные.
Простейший конфиг выглядит примерно следующим образом:
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 768;
}
http {
# some HTTP boilerplate
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
gzip_disable "msie6";
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# server on port 80 for HTTP -> HTTPS redirect
server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri;
}
# The letsencrypt-secured HTTPS server, which proxies our requests
server {
listen 443 ssl;
server_name example.com;
ssl_protocols TLSv1.1 TLSv1.2;
# letsencrypt certificate
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# client certificate
ssl_client_certificate /etc/nginx/client_certs/ca.crt;
# make verification optional, so we can display a 403 message to those
# who fail authentication
ssl_verify_client optional;
access_log /var/log/nginx/example.com;
location / {
# if the client-side certificate failed to authenticate, show a 403
# message to the client
if ($ssl_client_verify != SUCCESS) {
return 403;
}
proxy_set_header Host $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;
# Fix the "It appears that your reverse proxy set up is broken" error.
proxy_pass http://localhost:8080;
proxy_read_timeout 90;
# web sockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_redirect http://localhost:8080 https://example.com;
}
}
}
В данном примере все HTTP-соединения заворачиваются на HTTPS, для которого выпущен сертификат Let's Encrypt. После чего происходит проверка сертификата, с которым пришел пользователь. Если сертификат проверку не прошел, отдается 403 ошибка. В случае, если проверка все-таки пройдена, то происходит перенаправление на приложение, которое запущено на порту 8080. Важный момент - приложение повешенно на локалхост, то есть его порт не торчит наружу. Все что видно снаружи - nginx на 443 порту.