Nginx: SSL certificate user authentication
NGINX gives us the possibility to use user certificates to authenticate web sites, even sites that are being intercepted by proxy. This is a very interesting alternative to protect web sites that do not have adequate or obsolete authentication mechanisms, such as HTTP basic authentication.
For this configuration we need to have a CA certificate accessible to NGINX, against which the user certificates will be validated. In addition, users must have their user certificates signed by the above CA installed in their browsers and computers.
CA creation
Private key
openssl genrsa -des3 -out ca.key 4096
Certificate
openssl req -new -x509 -days 365 -key ca.key -out ca.crt
Creation of the client certificate
Private key
openssl genrsa -des3 -out user.key 4096
Certificate request
openssl req -new -key user.key -out user.csr
Certificate signing by the CA
openssl x509 -req -days 365 -in user.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out user.crt
Certificate and key packaging
openssl pkcs12 -export -out user.pfx -inkey user.key -in user.crt -certfile ca.crt
This file, user.pfx will be the one that each user installs in his/her browser to authenticate against the server or the proxy.
NGINX configuration
server {
listen 80;
server_name server.example.com;
return 301 https://server.example.com$request_uri;
}
server {
listen 443 ssl;
ssl_certificate /usr/local/etc/ssl/fullchain.pem;
ssl_certificate_key /usr/local/etc/ssl/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_client_certificate /usr/local/etc/nginx/ca.crt;
ssl_verify_client optional;
server_name server.example.com;
location / {
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 "reverse proxy 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://server.example.com;
}
}
This configuration redirects HTTP to HTTPS on the one hand, and on the other hand already in HTTPS performs the validation of the client certificate, returning a status code 403 NOT FOUND, if the certificate is not present, or is invalid.
Note: In this case the server is a **proxy** since the target server does not have HTTPS, so it is this instance of **NGINX** that performs the **SSL offload** and also takes care of the user validation that we commented.