nginx: Aktivierung von OCSP Must-Staple ohne Timeout
In diesem kurzen Microblog-Beitrag möchte ich auf die Aktivierung von OCSP Must-Staple in Kombination mit nginx eingehen. Nach der Aktivierung dieses Features hatte ich beim Aufruf sporadisch folgende Fehlermeldung im Client (Browser, Firefox):
MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING
Wie sich das Vermeiden lässt, zeigt der vorliegende Beitrag.
Was ist OCSP Stapling überhaupt? Das ist hier kompakt erklärt: Using OCSP Stapling to Improve Response Time and Privacy. Kurzer Ausschnitt aus der Wikipedia dazu:
Online Certificate Status Protocol stapling, formell bekannt als die TLS-Zertifikatsstatusabfrage-Erweiterung, ist ein alternativer Ansatz zum Online Certificate Status Protocol (OCSP) um den Gültigkeitsstatus von digitalen Zertifikaten nach X.509 zu prüfen.[1] Es ermöglicht dem Zertifizierten die Aufgabe der Zertifikatsvalidierung zu übernehmen, indem er eine von der Zertifizierungsstelle signierte OCSP-Antwort mit Zeitstempel an den ursprünglichen TLS-Handshake anhängt („stapling“). Dieses Verfahren verringert den Kommunikationsaufwand zwischen Clients und Zertifizierungsstellen deutlich.
OCSP Must-Staple ist nun eine Erweiterung für Zertifikate, damit Clients bereits während des TLS-Handshakes über die Verwendung von OCSP-Stapling informiert werden können.
Nun aber zur Umsetzung anhand eines Beispiels. Konkret wird eingesetzt:
- Debian Stretch
- nginx 1.10.3
- OpenSSL 1.1.0f
- Let’s Encrypt mit dem acme.sh Client
Zunächst generieren wir uns den Certificate Signing Request (CSR):
openssl req -new -sha512 -key /etc/ssl/private/kuketz-blog.key -subj "/" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:kuketz-blog.de,DNS:www.kuketz-blog.de\ntlsfeature=status_request")) > /etc/ssl/certs/kuketz-blog.csr
Achtet auf die Ergänzung:
tlsfeature=status_request (siehe RFC7633)
Wir setzen das Must-Staple Flag also direkt schon beim Signing Request.
Anschließend lassen wir den Request von Let’s Encrypt bestätigen bzw. unterschreiben:
acme.sh --signcsr --ocsp-must-staple --keylength ec-384 --csr /etc/ssl/certs/kuketz-blog.csr -w /var/www/sites/www.kuketz-blog.de/
Achtet hier auf die Ergänzung:
--ocsp-must-staple
Ihr müsst Let’s Encrypt über das Vorhandensein des Must-Staple Flag informieren.
Anschließend könnt ihr in nginx OCSP-Stapling aktivieren und das Zertifikat wie gewohnt ausrollen bzw. benutzen:
# OCSP-Stapling ssl_stapling on; ssl_stapling_verify on;
In der Praxis ist dann allerdings beim Aufruf des Blogs (erster Verbindungsversuch) via Firefox folgender Fehler aufgetreten:
MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING
Warum ist das so? Bei der Abfrage der OCSP-Antwort beim Aussteller des Zertifikats (hier Let’s Encrypt) ist nginx (teilweise) nicht schnell genug. Es kann also vorkommen, dass die erste Antwort an den Client ohne angeheftete OCSP-Informationen erfolgt. Folglich wird der Client (Browser) den Verbindungsaufbau ablehnen, da er diese Information als Teil der Zertifikats erwartet. Als Workaround kann der Nutzer einfach nochmal die Seite aktualisieren. Im Normalfall sendet nginx dann spätestens im zweiten Anlauf die OCSP-Information mit an den Client raus. Für die Praxis ist dies natürlich nicht optimal.
nginx bietet daher die Möglichkeit, über eine Direktive (ssl_stapling_file), die OCSP-Antwort der CA lokal aus einer Datei auszulesen, um diese bei einem Verbindungsaufbau direkt an den Client übersenden zu können – ohne dabei in Verzug zu kommen. Das Problem ist nur: Wie kommen wir an die OCSP-Antwort von Let’s Encrypt? Das geht via OpenSSL-Kommando.
Zunächst benötigen wir die OCSP-URI, die in unserem signierten Zertifikat steckt:
openssl x509 -noout -ocsp_uri -in /etc/ssl/certs/kuketz-blog.pem
Ausgabe
http://ocsp.int-x3.letsencrypt.org
Anschließend müssen wir diese Information gemeinsam mit der OCSP-Antwort für unser Zertifikat in DER-kodiertem Format speichern:
openssl ocsp -no_nonce -issuer /root/.acme.sh/kuketz-blog.de/ca.cer -verify_other /root/.acme.sh/kuketz-blog./ca.cer -cert /etc/ssl/certs/kuketz-blog.pem -respout /etc/ssl/certs/kuketz-blog_ocspresponse.der -url http://ocsp.int-x3.letsencrypt.org
Eben auf diese »zwischengespeicherte« OCSP-Antwort verweisen wir nun mit nginx:
# OCSP-Stapling ssl_stapling on; ssl_stapling_verify on; ssl_stapling_file /etc/ssl/certs/kuketz-blog_ocspresponse.der;
Problem gelöst. Keine verzögerten OCSP-Antworten mehr, die dann nicht mehr rechtzeitig bei einem Verbindungsversuch, von einem Client, mitgesendet werden können.
Testen ob OCSP Must-Staple funktioniert könnt ihr via SSL Labs. Unter dem Bereich »Server Key and Certificate #1« wird in der Zeile bei OCSP Must-Staple erscheinen:
Supported
Ich werde das Thema OCSP Stapling bzw. HTTP-Security-Header mal demnächst in einer Artikelserie aufgreifen.