Nginx Client Authentifizierung mit Fallback

Jan 02

Authentifizierung mittels SSL-Client-Zertifikaten ermöglicht die Sicherung von Resourcen, ohne dass jedesmal ein Passwort eingegeben werden muss. Allerdings hat es den nachteil, dass auf jedem Client-Computer zuerst das entsprechende Zertifikat installiert werden muss.

Mit diesem Setup kann eine SSL-Client Authentifizierung eingerichtet werden, während eine "normale" - weiterhin durch HTTPS geschützte - HTTP-Basic Authentifizierung als Fallback bereit steht falls kein Clientzertifikat gesendet wird.

Schritt 1: Erstellen der Zertifikate

Mittels den unten angedruckten openssl Befehlen kann einfach ein Selbst-Signiertes CA-Zertifikat sowie passende Server- und Clientzertifikate erstellt werden, welche hier verwendet werden.

# Certificate Authority
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out ca.key
openssl req -new -x509 -days 3650 -sha256 -key ca.key -out ca.pem

# Client Certificate
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out client.key
openssl req -new -key client.key -sha256 -out client.csr
openssl x509 -req -days 3650 -in client.csr -CA ca.pem -CAkey ca.key -set_serial 0x`openssl rand 16 -hex` -sha256 -out client.pem
# .p12 for Browser Import
openssl pkcs12 -export -in remoraeber.pem -inkey remoraeber.key -name "Remo Raeber" -out remoraeber.p12

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out server.key
openssl req -new -key server.key -sha256 -out server.csr
openssl x509 -req -days 3650 -in server.csr -CA ca.pem -CAkey ca.key -set_serial 0x`openssl rand 16 -hex` -sha256 -out server.pem

Der Einfachheit halber sind die Zertifikate hier nicht mit Passwort geschützt, das sollte jedoch bei Produktiven installationen auf jeden Fall vermieden werden!

Schritt 2: Konfiguration von nginx

Die entsprechende Konfiguration von nginx sieht bei mir wie folgt aus:

server {
    listen [::]:443 http2 ssl;
    listen 443  http2 ssl;
    server_name mein-server-name;

    ssl_certificate /etc/nginx/server.pem;
    ssl_certificate_key /etc/nginx/server.key;
    ssl_client_certificate /etc/nginx/ca.pem;
    ssl_verify_client optional;
    auth_basic_user_file /etc/nginx/htpasswd;

    location / {
        if ($ssl_client_verify = SUCCESS) {
            set $auth_basic off;
        }
        if ($ssl_client_verify != SUCCESS) {
               set $auth_basic Required;
        }

        auth_basic $auth_basic;
        proxy_pass http://mein-upstream;
    }
}

Grundsätzlich ist die Konfiguration, SSL-Client Authentifizierung wird auf optional gesetzt. Später - im Server Block - wird getestet ob die Authentifizierung erfolgreich war, wenn ja wird die variable auth_basic auf off gesetzt, ansonsten auf 'Required'. Der gesetzte Wert aktiviert oder deaktiviert dann die HTTP-Basic Authentifizierung.

Der Umweg über die Variable muss gemacht werden, da die auth_basic Anweisung nicht innerhalb eines if-Blocks gesetzt werden kann.