Teil 1: PKI und Zertifikate – ein paar Grundlagen
Teil 2: IP-Whitelist durch Client-Zertifikate ersetzen
Teil 3: Client Certificates Webapp
In den letzten beiden Beiträgen wurde ja schon ausführlich dargelegt, was es mit Client-Zertifikaten auf sich hat, und wofür sie gebraucht werden. Wie ebenfalls festgestellt, ist der Prozess zum Ausstellen aber nicht eben einfach – und das betrifft nicht mal nur die User.
Da wäre es doch schön, wenn der Benutzer seinen CSR einfach im Web hochladen könnte, und später das signierte Zertifikat an der selben Stell zum Download bereit steht. Und als Admin wäre es mindestens genau so schön, wenn ich eine Liste sämtlicher Requests habe, wo ich einfach nur das Passwort eingeben und auf „signieren“ klicken muss.
Naja, und weil das so schön wäre (und wir kein existierendes Projekt gefunden haben, dass das ohne einen gigantischen Aufriss möglich macht), haben wir auch eine entsprechende Webapp gebaut:
Was waren die Anforderungen?
Das Benutzer sollte eine einfache Möglichkeit an die Hand bekommen, um einen CSR zu erstellen und einzureichen. Außerdem sollte der Status des Request direkt ersichtlich sein. Und letztlich sollte das fertike Zertifikat direkt herunter geladen werden können.
Von der Infrastrukturseite kamen jetzt noch ein paar Anforderungen dazu!
So soll der Zugriff nur einem definierten Personenkreis gestattet sein. Und außerdem soll jeder Benutzer identifiziert werden, damit ersichtlich ist, von wem tatsächlich der jeweilige Request kommt. Wäre ja noch schöner, wenn man sich einfach für jemand anderen ausgeben könnte. Und damit dafür jetzt nicht noch eine zusätzliche Lösung gebaut werden musste, sollte das ganze an das existierende Azure Active Directory angebunden werden.
Von der Admin-Seite her sollte man auf einen Blick alle Anfragen sehen und per Knopfdruck signieren oder ablehnen können. Und eine Liste aller bereits signierten Zertifikate ist ja wohl selbstverständlich.
Von der Benutzerseite…
Beim Aufruf der Webapp muss der User sich erst mal via MS SSO einloggen (Azure AD Anbindung – wir erinnern uns) und bekommt dann eine Seite angezeigt, die ihn begrüßt und nur seine Daten anzeigt. Zunächst muss ich als Benutzer dann erst mal einen CSR erzeugen. Da die meisten aber nicht wissen, was das ist, war eine verständliche Erläuterung unabdingbar. Das war bei uns allerdings eher ein iterativer Prozess. Wir stellen dem Benutzer ja ein Script bereit, dass die Erstellung eines privaten Key und des CSR vereinfacht und ein paar sinnvolle Defaults setzt – weil der User die meistens eh nicht von sich aus angibt.
Es stellte sich allerdings heraus, dass die meisten User davon ausgingen, dass das Script ihnen bereits ihr Zertifikat erzeugt. Ein einfacher Hinweis darauf, dass es sich um einen zweistufigen Prozess handelt, reichte schlicht und ergreifend nicht aus.
Es mussten deutlichere Worte gefunden werden:
Das Script erstellt KEIN Zertifikat!!! Nur einen ANTRAG auf ein Zertifikat!!!!! Dieser ist zu nichts anderem nütze, als ihn hier hoch zu laden, um dann ggfs. ein Zertifikat bewilligt zu bekommen – sofern wir uns in unserer unendlichen Großzügigkeit dazu bereit erklären!
Mit dieser, und ein paar anderen visuellen Veränderungen, klappte es dann deutlich besser – wenn man davon absieht, dass ein paar wenige Benutzer hin und wieder das Passwort für ihren privaten Key „verlegen“.
Von der Adminseite…
Wer im Azure AD in der richtigen Gruppe ist, bekommt oben rechts einen Link angezeigt, der ihn zur Adminoberfläche bringt. Dort hat es dann die zwei Listen mit ausstehenden Anfragen und bereits signierten Zertifikaten. Beim Hovern über den Filename des CSR, werden in einem Tooltip noch Details (CN, OU, ST …usw) angezeigt. Daneben befinden sich dann jeweils zwei Passwortfelder und ein (oder zwei) Buttons (bei den signierten Zertifikaten zum revoken, und bei den Anfragen zum signieren oder ablehnen).
Das ist nicht optimal, aber war die einfachste Lösung, um nicht noch aufwändig eine Art Keyvault in die App einbauen zu müssen. Aber hey, die App ist freie Software – wer sich die Mühe machen möchte: entsprechende Pull-Requests sind gerne gesehen! 🙂
Warum zwei Passwörter, fragt ihr? Naja, …das erläutere ich direkt mal im nächsten Abschnitt:
Infrastrukturseite…
Das ganze OpenSSL-Zeugs (Root-Zertifikate, Keys, Configs, CSRs, signierte Zertifikate, CRLs, Index usw.) soll – symetrisch verschlüsselt – in einem git-Repository gespeichert werden. Damit braucht man, neben dem Passwort für den CA-Key, auch noch eins für die Verschlüsselung!
Aber immerhin erübrigt sich damit zum einen die Frage, wo, außerhalb des Cluster, das ganze gespeichert werden soll. Und zum anderen braucht man dann nicht noch ein eigenes Audit-Log pflegen!
Durch die Verschlüsselung kann man das git-Repo auch ohne große Bedenken beim eigenen Clouddienstleister (bspw. Azure DevOps) hosten lassen. Dann ist das Thema „Backup“ nämlich direkt mit erledigt.
An dieser Stelle sollte auch noch mal erwähnt werden, dass der Salt für die Verschlüsselung immer der selbe sein muss. Damit unveränderte Dateien in verschlüsselter Form auch immer identisch sind. Das würde normalerweise als Sicherheitsrisiko eingestuft werden – aber andernfalls wäre die Nutzung von git nicht möglich, da dann immer alle Dateien als grundlegend geändert erkannt würden. Und das würde die ganzen Vorteile von git hinfällig machen und das ganze ad absurdum führen.
Implementierung
Das ganze ist als SpringBoot Anwendung umgesetzt. Was den Vorteil hat, dass wir für den Login direkt spring-security in Kombination mit der Azure-AD-Integration benutzen können!
Und im Gegensatz zu Python gibt es bei Java auch gescheite Libraries für git.
Bei SSL-Operationen wird es schon wieder schwieriger! Details eines CSR auszulesen, ist noch mit akteptablem Aufwand möglich. Aber bei allem anderen ist es deutlich einfacher, direkt openssl aufzurufen!
Details zu alledem sind auf der Projektseite bzw. im Code zu finden: https://github.com/hmg-dev/client-certificate-webapp
Beim Studieren des Code wird ggfs. auffallen, dass da einige Scripte benötigt werden, die aber im Repo mit den Certs erwartet werden. Dazu im nächsten Abschnitt mehr.
Fehlt da nicht noch was?
Ganz recht. Oben war ja schon die Rede davon, dass es möglich ist, Zertifikate zu widerrufen. Die müssen dann natürlich auf einer Certificate-Revocation-List landen. Und die muss öffentlich abrufbar sein!
Das Erstellen der Revocationlist wird – analog dem Signieren der Zertifikate – durch den Aufruf eines Script erledigt, dass die Webapp als Teil des git-Repo erwartet, welches die OpenSSL-Struktur beinhaltet.
Dazu gibt es unter src/main/bash
die beiden Scripte revoke-cert.sh
und sign-csr.sh
welche ihr einfach in besagtes Repository kopieren könnt. Kontrolliert aber ggfs. trotzdem noch mal, dass die Verzeichnisstruktur mit den im Script verwendetet Pfaden übereinstimmt.
Da die CRL eine recht kurze Lebensdauert hat, sollte sie so wenigstens alle zwei Wochen neu erstellt werden. Um dafür nicht immer die App selbst bemühen zu müssen, haben wir ein weiteres kleines Projekt erstellt, dass einfach ein Shell-Script und ein Dockerfiles beinhaltet.
Das kann man dann bspw. in einen Kubernetes-Cronjob einbinden und fertig ist die Laube: https://github.com/hmg-dev/client-certificate-docker-crl
Abrufen kann man die CRL dann über den Pfad „list.crl“. Also bspw. „www.my-pki-webapp.invalid/list.crl“ – sofern ihr die Webapp unter „www.my-pki-webapp.invalid“ laufen lassen würdet.
Fazit
OK, zugegeben – wir haben jetzt drei volle Artikel gebraucht, um das Thema näher zu bringen, ohne wichtige Details auszulassen.
Aber wenn es einfach wäre, könnte es ja jeder 😉
Was aber auf jeden Fall klar sein sollte: wenn man initial einmal etwas Aufwand da rein steckt, dann kann man das Thema nicht nur in den Griff bekommen sondern – dank der von uns bereit gestellten Webapp – auch auf ein Level bringen, auf dem es selbst von Leuten benutzbar ist, die davon bestenfalls mal gehört haben.
Es gibt also eigentlich keine Ausrede mehr, noch auf so etwas wie IP-Whitelists zu setzen.
p.s.
Als kleine Zugabe, gibt es jetzt noch drei Möglichkeiten, um Zertifikate für Drittanbieter zur Verfügung zu stellen:
Möglichkeit 1: Erzeugen aller nötigen Files für den Dienstleister
Das Script, das der User downloaden kann, um lokal einen Key und CSR zu erzeugen, versucht normalerweise automatisch den Usernamen zu bestimmen. Man kann selbigen aber auch als Parameter angeben.
Auch die restlichen Daten des CSR lassen sich anpassen.
So kann ein Entwickler den selben Prozess wie für sich selbst noch einmal mit den Daten für den Dienstleister durchlaufen, und diesem hinterher alle fertigen Files zuschicken.
Hat natürlich den Nachteil dass der Entwickler dann ebenfalls den Key und das Passwort dazu hat.
Möglichkeit 2: Der Dienstleister schickt einen CSR
Wenn der Dienstleister technisch versiert genug ist, kann ihm das Zip mit dem Script zum Erzeugen von Key und CSR an die Hand gegeben werden. Und der Dienstleister schickt dann nur noch den CSR an jemand mit Zugang zur Webapp – und dieser dann das fertige Zertifikat zurück.
Schon besser, aber braucht halt noch einen Vermittler.
Möglichkeit 3: Azure Gastuser
Wenn man es richtig elegant haben möchte, dann erstellt man für den Dienstleister einen Azure Gastuser und fügt diesen der verwendeten App-Registration hinzu.
Dann hat der Dienstleister Zugriff auf die Webapp und kann selber ein Zertifikat beantragen.
Kommentare von Martin Drößler