Wie zuletzt beschrieben, funktioniert das lokale SSL nun grundsätzlich. Aber im Browser bekommt man beim Aufruf von https://gruniversal.mars trotzdem ne Warnung angezeigt, dass die Konfiguration nicht korrekt ist (oder ein böser Mensch sich zu schaffen macht):

Die kann man zwar wegklicken, aber am Ende wünscht man sich doch einen “sauberen” Weg, der ohne Fehlermeldungen auskommt. Was kann man da machen? Und was genau bedeutet der Fehler eigentlich?

Die Fehlermeldung verstehen

Wenn man die Ursache besser verstehen will, lohnt es sich mal genauer hinzuschauen, was der Browser da eigentlich bemängelt. Im Chrome heißt der Fehler beispielsweise NET::ERR_CERT_AUTHORITY_INVALID und beschreibt, dass der Browser die “Zertifikatsautorität” für ungültig hält. Beim Klick auf den Fehlercode erscheint noch:

Subject: gruniversal.mars
Issuer: gruniversal.mars
Expires on: 14.02.2030
Current date: 21.02.2020

Firefox meldet einen MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT und ist auch generell etwas auskunftsfreudiger:

gruniversal.mars verwendet ein ungültiges Sicherheitszertifikat.

Dem Zertifikat wird nicht vertraut, weil es vom Aussteller selbst signiert wurde

Das Problem ist also, dass mein Zertifikat von mir selbst erstellt wurde und mein Browser das offenbar nicht als vertrauenswürdig empfindet, wenn man für sich selbst bestätigt, dass man selbst man selbst ist. Oder so :)

Die eigene Certificate Authority

Die Browserwarnung wird angezeigt, weil der Browser die sogenannte Certificate Authority (kurz CA) nicht kennt und er in der Folge dem Zertifikat nicht vertraut.

“Was der Browser nicht kennt, frisst er nicht.”

Redensart

Das ganze funktioniert so, dass Betriebssysteme eine Reihe von Zertifikaten großer Organisationen von Haus aus mitbringen und die Browser diese “Root-Zertifikate” automatisch als glaubwürdig betrachten. Wesentliche Marktanteile haben dabei CAs wie IdenTrust, Comodo oder DigiCert. Alle auf Basis dieser Root-Zertifikate ausgestellten Website-Zertifikate sind damit vertrauenswürdig.

Meine “Organisation” ist wenig überraschenderweise nicht unter den Root-CAs, daher stellt sich die Frage, wie ich vertrauenswürdig werden kann. Antwort: Indem ich zuerst einmal eine richtige eigene CA werde. Dabei hilft uns wieder openssl.

Zuerst wird ein privater Schlüssel erzeugt:

david@mars ~ $ sudo openssl genrsa -out /etc/apache2/ssl/ca-mars.key 2048
Generating RSA private key, 2048 bit long modulus
.........................................................+++
..........+++
e is 65537 (0x10001)

Mit dem privaten Schlüssel wird dann das eigentliche Zertifikat erzeugt. Dabei sorgt die Angabe -extensions v3_ca dafür, dass ein CA-Zertifikat erstellt wird.

david@mars ~ $ sudo openssl req -x509 -new -extensions v3_ca -key /etc/apache2/ssl/ca-mars.key -days 3650 -out /etc/apache2/ssl/ca-mars.pem
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:DE
State or Province Name (full name) [Some-State]:.
Locality Name (eg, city) []:Leipzig
Organization Name (eg, company) [Internet Widgits Pty Ltd]:gruniversal
Organizational Unit Name (eg, section) []:mars
Common Name (e.g. server FQDN or YOUR name) []:gruniversal.mars
Email Address []:

Nach dieser Prozedur ist die eigene Certificate Authority einsatzbereit. Aber noch ist sie nicht vertrauenswürdig für Linux oder die Browser. Das ist der nächste Schritt.

Der eigenen CA vertrauen

Damit der Browser das Zertifikat kennt, muss man es importieren. Dazu findet sich in den Einstellungen der Punkt “Zertifikate” bzw. “Zertifikate verwalten”. Dort kann man unter “Zertifizierungsstellen” die pem-Datei importieren. Wenn alles geklappt hat, fragt der Browser noch welches Vertrauen wir uns selbst geben wollen:

Damit das Linux das Zertifikat auch kennt, hab ich es zudem auf diese Weise importiert und auch hier meinem Zertifikats-Ich vollstes Vertrauen zugesichert:

david@mars ~ $ sudo cp /etc/apache2/ssl/ca-mars.pem /usr/share/ca-certificates/ca-mars.crt

david@mars ~ $ sudo dpkg-reconfigure ca-certificates
Trigger für ca-certificates (20170717~16.04.2) werden verarbeitet ...
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...

Adding debian:ca-mars.pem
done.
Updating Mono key store
Linux Cert Store Sync - version 4.2.1.0
Synchronize local certs with certs from local Linux trust store.
Copyright 2002, 2003 Motus Technologies. Copyright 2004-2008 Novell. BSD licensed.

I already trust 148, your new list has 149
Certificate added: C=DE, L=Leipzig, O=gruniversal, OU=mars, CN=gruniversal.mars
1 new root certificates were added to your trust store.
Import process completed.

Ok cool. Das müsste es dann ja sein, oder? Ich mein, was kann jetzt noch schief gehen bei soviel selbstausgesprochenem Vertrauen? Hmm.. klappt nicht! Und nun?

Die eigene Autorität nutzen

Der Haken ist, dass ich zwar nun ein eigenes Root-Zertifikat habe, aber meine Website-Zertifikate, die der Apache ausliefert, damit in keiner Verbindung stehen.

Um dies zu ändern, müssen die Zertifikate anders erstellt werden. Dabei wird zunächst eine Zertifikatsanfrage an die CA gestellt und daraus wird dann das eigentliche Website-Zertifikat erstellt, welches praktisch vom Root-Zertifikat abgeleitet wird.

Auf diese Weise gehören sie quasi zu meiner CA und das ausgesprochene Vertrauen sollte ausreichen dann auch die Website-Zertifikate vertrauenswürdig zu machen.

Eine Zertifikatsanfrage erstellt man wiederum mit openssl in zwei Schritten über einen privaten Schlüssel und die entsprechenden Angaben für das Zertifikat. Hierbei ist es wichtig im als Common Name den korrekten Domainnamen anzugeben:

david@mars ~ $ sudo openssl genrsa -out /etc/apache2/ssl/gruniversal.mars.key 2048
Generating RSA private key, 2048 bit long modulus
..+++
...+++
e is 65537 (0x10001)

david@mars ~ $ sudo openssl req -new -key /etc/apache2/ssl/gruniversal.mars.key -out /etc/apache2/ssl/gruniversal.mars.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:DE
State or Province Name (full name) [Some-State]:.
Locality Name (eg, city) []:Leipzig
Organization Name (eg, company) [Internet Widgits Pty Ltd]:gruniversal
Organizational Unit Name (eg, section) []:mars
Common Name (e.g. server FQDN or YOUR name) []:gruniversal.mars
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Mit der gerade erzeugten csr-Datei kann man dann das entsprechende Zertifikat erstellen:

david@mars ~ $ sudo openssl x509 -req -in /etc/apache2/ssl/gruniversal.mars.csr -CA /etc/apache2/ssl/ca-mars.pem -CAkey /etc/apache2/ssl/ca-mars.key -CAcreateserial -out /etc/apache2/ssl/gruniversal.mars.pem -days 3650
Signature ok
subject=/C=DE/L=Leipzig/O=gruniversal/OU=mars/CN=gruniversal.mars
Getting CA Private Key

Das muss nun noch im Apache hinterlegt werden. Ich habe dazu bewusst das Naming der Datei angepasst, weil das Zertifikat ja nur für die Domain gruniversal.mars gilt:

david@mars ~ $ sudo vim /etc/apache2/sites-available/001-gruniversal.mars.conf
<VirtualHost *:443>
    ServerName gruniversal.mars:443
    ServerAlias www.gruniversal.mars:443

    DocumentRoot /home/david/workspace/websites/gruniversal.de/

    SSLEngine on
    SSLCertificateFile /etc/apache2/ssl/gruniversal.mars.pem
    SSLCertificateKeyFile /etc/apache2/ssl/gruniversal.mars.key
</VirtualHost>

Nach einem entsprechenden Reload der Konfiguration ist der große Moment gekommen. Klappt der Aufruf im Browser nun?

Firefox mag mein selbstvertrautes Zertifikat

Yeah, das sieht gut aus im Firefox. Und was sagt der Chrome?

NET::ERR_CERT_COMMON_NAME_INVALID

This server could not prove that it is gruniversal.mars; its security certificate does not specify Subject Alternative Names. This may be caused by a misconfiguration or an attacker intercepting your connection.

Hmm.. zumindest hat sich der Fehlertext geändert. Aber was sind Subject Alternative Names und warum braucht Chrome das überhaupt? Die Erklärung findet sich in diesem Hilfe-Beitrag. Ab Chrome 58 wird nicht mehr der Common Name abgeglichen, sondern Einträge aus der Erweiterung “Subject Alternative Name“.

Hmm.. Einträge. Plural! Kann ich damit etwa mehrere Domains abdecken? Oder zumindest die jüngst erstellte www-Subdomain?

Noch mehr Autorität

In der Tat ermöglicht es subjectAltName mehrere Domainnamen anzugeben, womit wir es uns sparen können für jede Domain ein eigenes Zertifikat zu erstellen.

Zudem wird es Zeit für etwas Komfort. Das händische Eingeben der Informationen zu meiner Organisation wird auf Dauer auch etwas ermüdend. Zum Glück kann man dafür eine Konfigurationsdatei bemühen:

david@mars ~ $ sudo vim /etc/apache2/ssl/openssl.cnf
[ req ]
default_bits         = 2048
distinguished_name   = req_distinguished_name
req_extensions       = req_ext
prompt               = no

[ req_distinguished_name ]
countryName          = DE
stateOrProvinceName  = Saxony
localityName         = Leipzig
organizationName     = gruniversal
commonName           = gruniversal.mars

[ req_ext ]
subjectAltName = @alt_names

[ alt_names ]
DNS.1   = gruniversal.mars
DNS.2   = www.gruniversal.mars

Die verschiedenen Domainnamen kann man wunderbar unter alt_names verwalten und das prompt = no sorgt dafür, dass die angegeben Informationen zur Organisation automatisch eingetragen werden.

Die Konfiguration kann nun beim Erstellen der Zertifikatsanfrage über den Parameter -config mit angefügt werden:

david@mars ~ $ sudo openssl req -new -key /etc/apache2/ssl/gruniversal.mars.key -out /etc/apache2/ssl/gruniversal.mars.csr -config /etc/apache2/ssl/openssl.cnf

Und beim Erstellen des Zertifikates ergänzt sich der Aufruf um -extensions req_ext -extfile /path/to/config.

david@mars ~ $ sudo openssl x509 -req -in /etc/apache2/ssl/gruniversal.mars.csr -CA /etc/apache2/ssl/ca-mars.pem -CAkey /etc/apache2/ssl/ca-mars.key -CAcreateserial -out /etc/apache2/ssl/gruniversal.mars.pem -days 3650 -extensions req_ext -extfile /etc/apache2/ssl/openssl.cnf
Signature ok
subject=/C=DE/ST=Saxony/L=Leipzig/O=gruniversal/CN=gruniversal.mars
Getting CA Private Key

Dann den Apache reloaden und schon hat auch der Chrome seinen Tada-Moment:

Chrome ist nun auch zufrieden

Nachbetrachtungen

Bei all dem Aufwand fragt es sich, ob es nicht einfacher ist, die Browserwarnung einfach wegzuklicken und damit zu leben, dass es kein gültiges lokales Zertifikat gibt.

Rein von der funktionellen Notwendigkeit her ist dies sicherlich der Fall, aber mir ging es ganz bewusst auch darum hier etwas zu lernen, zumal der Prozess außerhalb der lokalen Umgebung ja auch nicht wesentlich anders funktioniert.

Ob ich tatsächlich für alle Projekte, die ich lokal entwickle so vorgehen werde, wird die Praxis zeigen. Hängt sicher auch davon ab, was die Browserhersteller sich noch alles so einfallen lassen.

Auf jeden Fall an dieser Stelle ein Dank an meine zahlreichen Tippgeber, die mir meinen Lernprozess erst ermöglichten, da seien besonders genannt:

Damit endet meine kleine Miniserie zur Einrichtung meiner Entwicklungsumgebung für die Website. Weiter geht es dann mit WordPress oder sonstigen Themen.