Ein Rechner, mehrere Domains und dann auch noch Zertifikate!

Es ist teilweise unglaublich, wieviele Probleme man bekommen kann, wenn man einen eigenen Server betreiben will. Das fängt bei der Erreichbarkeit an (Stichwort dynamische IP), geht über die Probleme beim E-Mail-Versenden (Stichwort Reverse-DNS) und endet bei den SSL-Zertifikaten.

Die Zertifikate haben es nämlich ganz schön in sich. Aber fangen wir vorne an: Wenn ein Rechner sich mit einem Server über den Domainnamen verbindet, dann funktioniert das, indem der Rechner bei einem DNS-Server anfragt, wie denn die IP des Rechners ist, der sich hinter dem Namen versteckt. Der Rechner verbindet sich dann mit der IP und beginnt mit der eigentlichen Kommunikation.
Wenn der Rechner also bei dem Server aufschlägt, hat der Server garkeine Ahnung, dass der Besucher mit einer bestimmten Domain sprechen möchte. Diese Information wird z.B. einem HTTP-Server erst im weiteren Gesprächsverlauf mitgeteilt.

Jetzt kommt aber das große Problem: Wenn ein Rechner sich mit dem Server verbindet und der Rechner eine verschlüsselte SSL-Verbindung haben möchte, dann muss der Server ein passendes Zertifikat zurückliefern. In diesem Zertifikat steht der Domainname, für den das Zertifikat gilt.
Der Stolperstein ist nun, dass der Server das passende Zertifikat zurückgeben muss, obwohl ihm der Rechner noch garnicht verraten hat, welche Domain er überhaupt abrufen will!

Um dieses Problem zu lösen gibt es nun mehrere Möglichkeiten, von denen ich mir einige angeguckt habe und die ich mal ein bisschen näher erklären will.

Fangen wir mal mit der Lösung an, die wohl die wenigstens Haushalte "einfach so" aus dem Stehgreif lösen können: Dem Rechner mehrere IPs zuweisen. Hintergrund ist, dass einer Netzwerkkarte mehrere IPs zugewiesen werden können. Anhand der IP, an die die Abfrage gerichtet ist, kann der Server nun herausfinden, welche Domain erfragt werden soll. Je nachdem wird dann einfach das passende Zertifikat verteilt.
Die Lösung ist für den Privathaushalt deshalb unpraktikabel, da man als Privatperson von seinem Internetanbieter meist nur eine einzige IP zugewiesen bekommt. Es gibt zwar die Möglichkeit, sich z.B. bei Manitu mehrere IPs zu besorgen, jedoch bekommt man dann das Problem, dass man einen professionellen Router benötigt, der in der Lage ist, mehrere öffentliche IPs zu verwalten.

Deshalb habe ich lange daran gedacht, folgende Lösung umzusetzen: Ich besorge mir eine Domain, über die sämtliche verschlüsselte Kommunikation abläuft. Wenn jemand SSL benutzen möchte und keine Programmwarnungen sehen will, soll er halt über diese designierte Domain gehen, da diese dann in den Zertifikaten steht. Das einzige Problem wäre, dass ein Benutzer sich evtl. wundert, dass er von einer Webseite auf eine komplett andere Domain weitergeleitet wird. Alles andere könnte man durch korrekte Konfiguration und durch Programmierung lösen.

Für viele würde diese Variante sicherlich ausreichen - ich persönlich finde sie allerdings ziemlich unschön. Also habe ich weitergesucht und habe die TLS-Erweiterung namens SNI gefunden. Diese löst genau das oben Problem: Bei der Verwendung von SNI wird bereits beim Aufbau der verschlüsselten Verbindung mitgeteilt, welche Domain angesprochen werden soll. Dadurch kann der Server das passende Zertifikat heraussuchen und die Verbindung korrekt herstellen. An sich eine gute Lösung...

...wenn denn der Server es nicht explizit unterstützen müsste. Leider gibt es immernoch einige Serversoftware, die ausschließlich SSL (bzw. TLS 1.0) unterstützen. Deshalb habe ich noch weiter gesucht und bin schlussendlich beim X.509-Standard gelandet. Dieser beschreibt, welche Werte in einem Zertifikat stehen. Auch dieser hat in letzter Zeit eine Veränderung erfahren, die für unsere Belange genutzt werden kann: Gemeint ist die Erweiterung SubjectAltName.
Diese sieht vor, dass man ein Zertifikat erstellen kann, das für mehrere Domains gültig ist - nämlich genau für die, die im Feld SubjectAltName hinterlegt sind.
Der Vorteil ist, dass dieses Vorgehen mit allen Servern funktioniert - auch, wenn sie die Erweiterung nicht unterstützen. Lediglich der Client muss sie auswerten können. Sollte es nun einen Client geben, der lediglich den Common Name (CN) auswertet, so kann man für diesen zumindest noch zusätzlich die oben erwähnte Variante mit der separaten SSL-Domain anbieten.

Abschließend wollte ich euch noch zeigen, wie man relativ einfach ein selbst-signiertes Zertifikat mit integrierten SubjectAltNames erstellen kann. Basis hierfür waren die Artikel auf therowes.net, iiegn.blogspot.com und sial.org.

Wenn man es noch nicht hat, muss man sich zuerst einmal einen privaten Schlüssel erzeugen. Das geht bei OpenSSL mit diesem Befehl:

1
openssl genrsa 2048 > key.rsa

Denkt bitte dran, dass dies der private Schlüssel ist, der im Moment nicht mit einem Passwort geschützt ist. Einige Serverprogramme brauchen ihn in diesem Zustand. Der Schlüssel muss in diesem Zustand unbedingt vor unbefugtem Zugriff geschützt werden!

Als nächstes muss die Datei "openssl.cfg" der OpenSSL-Installation bearbeitet werden. Über den interaktiven Modus von OpenSSL ist es derzeit nicht möglich, die SubjectAltNames zu definieren, deshalb muss dies in der Konfigurationsdatei geschehen.

Guckt zuerst, ob im Bereich "req" der Wert "req_extensions" auf "v3_req" gesetzt ist:

1
2
[ req ]
req_extensions = v3_req # The extensions to add to a certificate request

Als nächtes müsst ihr in den Bereichen "v3_req" und "v3_ca" den Wert "subjectAltName" auf "@subject_alt_names" setzen:

1
2
[ v3_req ]
subjectAltName=@subject_alt_names
1
2
[ v3_ca ]
subjectAltName=@subject_alt_names

Abschließend müsst ihr einen neuen Bereich "subject_alt_names" anlegen und dort die Domains reinschreiben, für die das Zertifikat gelten soll. Vergesst hier auf keinen Fall die Domain, die im Common Name stehen wird!

1
2
3
4
5
[ subject_alt_names ]
DNS.1 = example.com
DNS.2 = *.example.com
DNS.3 = example.net
DNS.4 = *.example.net

Und nun liegt es an euch, auf welchem Weg ihr das Zertifikat erstellen wollt. Wenn ihr über einen CSR gehen wollt, wird die Einstellung im Bereich "v3_req" verwendet. Wenn ihr jedoch gleich selbst signiert, sind die Einstellung in "v3_ca" aktiv.

Ich persönlich habe den zweiten Weg genommen, da ich mir damit das Erstellen einer eigenen CA gespart habe. Der Befehl für den direkten Weg lautet wie folgt:

1
openssl req -new -x509 -nodes -sha1 -days 365 -key key.rsa > key.rsa.cert

Beim Ausführen des Befehls werdet ihr nach den Werten für das Zertifikat gefragt. Vergesst nicht, den Common Name richtig zu setzen. Das dabei herauskommende Zertifikat wird 365 Tage lang gültig sein. Dieses Zertifikat muss übrigens nicht (im Gegensatz zum privaten Schlüssel) gesondert gesichert werden - es wird sowieso vom Server an jeden geschickt, der danach fragt! :D

Sooo... meine Güte! Der Artikel ist länger geworden, als ich gedacht hatte :D ! Ich muss aber auch zugeben, dass ich ihn eine ganz schöne Zeit vor mir hergeschoben habe und er zeitweise zu einer reinen internen Referenzsammlung verkommen war ;-) . Der Artikel ist auch für mich wichtig, damit ich nicht vergesse, welchen Weg ich nun schlussendlich gegangen bin :D !

Wie hat auch der Artikel gefallen? Standet/steht ihr auch von dem Problem? Wie habt ihr es gelöst? Wäre der gezeigte Weg eine Lösung, die ihr in Betracht ziehen würdet?
Verschlüsselnde Grüße, Kenny