NGINX nos da la posibilidad de utilizar certificados de usuario para autenticar sitios web, incluso sitios que se están interceptando mediante proxy. Ésta es una alternativa muy interesante para proteger sitios web que no dispongan de mecanismos de autenticación adecuados u obsoletos, como por ejemplo la autenticación básica HTTP.

Para esta configuración necesitamos tener accesible a NGINX un certificado CA que será contra el que se validen los certificados de usuario. Además los usuarios habrán de tener instalados en sus navegadores y equipos sus certificados de usuario firmados por la CA anterior.

Creación de la CA

Clave privada

openssl genrsa -des3 -out ca.key 4096

Certificado

openssl req -new -x509 -days 365 -key ca.key -out ca.crt

Creación del certificado cliente

Clave privada

openssl genrsa -des3 -out user.key 4096

Petición de certificado

openssl req -new -key user.key -out user.csr

Firma del certificado por la CA

openssl x509 -req -days 365 -in user.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out user.crt

Empaquetando certificado y clave

openssl pkcs12 -export -out user.pfx -inkey user.key -in user.crt -certfile ca.crt

Este fichero, user.pfx será el que cada usuario instale en su navegador para autenticarse contra el servidor o el proxy

Configuración de NGINX

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;
  }
}

Esta configuración redirige HTTP a HTTPS por una parte, y por otra ya en HTTPS realiza la validación del certificado cliente, retornando un código de estado 403 NOT FOUND, si el certificado no se presenta, o no es válido.

Nota: En este caso el servidor es un **proxy** ya que el servidor destino no dispone de HTTPS, así que es ésta instancia de **NGINX** la que realiza el **SSL offload** y además se encarga de la validación de usuario que comentamos.