Verschlüsselung mit Let’s Encyrpt und Apache’s mod_md

Dank des freien Dienstes Let’s encrypt ist es jedem mittlerweile Möglich seine Seiten per SSL zu sicheren. Dank des Apache Module mod_md kann der Apache Webserver nun inline die Zertifikate erstellen und warten. Ich habe mir das die Tage mal angesehen, und will Euch hier die Ergebnisse präsentieren.

Hintergrund

SSL/TLS Verschlüsselung für Webseiten (HTTPS) kostet normalerweise richtig Geld. Dank des Let’s encrypt Projektes ist es seit geraumer Zeit jedem Möglich die Seiten seines Werbeauftritts (und auch seines Mailservers) verschlüsselt über den Äther zu schicken. Im Groben und Ganzen handeln Server und Client anhand eines Schlüsselpaares Zufallszahlen aus, die dann zur Verschlüsselung der zu sendenden Daten verwendet werden. Damit der Client auch sicher sein kann, dass er mit dem richtigen Server spricht, gibt es noch eine Stelle die das überprüft (die Authority, hier eben Let’s Encrypt).

Der alte Weg

Bisher habe ich für die Erzeugung und die Pflege der Zertifikate certbot verwendet. Ein Phyton Script, welches alles Nötige zur Verfügung stellt. Gehen wir also davon aus wir hätten eine Domain example.org und wollen die „verSSLen“ so brauchen wir ein Zertifikat, das die folgenden Subdomains enthält:

  • example.org
  • www.example.org
  • mail.example.org

Beim generieren überprüft Let’s encrypt per generierter Datei und Web-Abfrage (http://$domain/.well-known/……) ob wir auch wirklich Inhaber selbigen Webservers sind und schwub können wir das Schlüsselpaar nutzen.
Wenn das Zertifikat abzulaufen droht (90 Tage), muss man es erneuern. Das läuft eigentlich genauso ab. Das haupt Problem ist eigentlich, ich muss alles einzeln machen, und dafür sorgen, dass das .well-known Verzeichnis für alles Subdomains verfügbar ist… Teilweise echt ein gefrickel….

Der neue Weg

Beim letzten Apache Update stolperte ich über Module mod_md. Weil ich mit dem Namen nichts anfangen konnte, hab ich mir das angesehen, und war angenehm überrascht. Mit Hilfe dieses Moduls kann der Webserver selber on-the-fly Zertifikate erstellen (inklusive der Subdomains) und kümmert sich auch noch um die Erneuerung. Ich war begeistert! Immerhin betreue ich hier ein paar Seiten und alles was Arbeit spart….

Wie funktionierts?

Zunächst einmal braucht man eine Apache Version >= 2.4.30 und die Module mod_md und mod_watchdog. Zusätzlich natürlich noch einige für SSL etc.
Im folgenden gehe ich davon aus, dass wir für die oben genannten Domains einen Virtual Host anlegen. Ich zerlege gerne die Config gerne etwas. Was wir also in /usr/local/etc/apache24/Includes liegen haben, sind drei Files:

ssl.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
SSLRandomSeed startup file:/dev/urandom 512
SSLRandomSeed connect file:/dev/urandom 512

Listen 443

AddType application/x-x509-ca-cert .crt
AddType application/x-pkcs7-crl .crl
SSLPassPhraseDialog builtin

SSLSessionCache "shmcb:/var/run/ssl_scache(512000)"
SSLStaplingCache "shmcb:/var/run/ssl_stapling(32768)"
SSLSessionCacheTimeout 300

SSLCipherSuite EECDH+AESGCM+AES256:EECDH+AES256:EDH+AES256
SSLProtocol -ALL -SSLv3 +TLSv1 +TLSv1.1 +TLSv1.2

SSLHonorCipherOrder on
SSLUseStapling On

SSLStrictSNIVHostCheck Off
SSLCompression off

### MOD_MD

MDStoreDir etc/apache24/md
MDCertificateAgreement https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf
MDomain www.example.org mail.example.org

Die ersten Zeilen sind die „normale“ SSL Konfiguration. Zeile 29-31 sind die interessanten:
MDStoreDir gibt an, wo die Zertifikate gespeichert werden sollen. Hilfreich, wenn man zum Beispiel auch mit nem Mailserver darauf zugreifen will.

MDCertificateAgreement: Man muss irgendwie die Lizenz von Let’s encrypt akzeptieren. Das macht diese Zeile… 😉

MDomain: Der ServerName des Virtual Hosts. Pro „Hauptdomain“ eine eigne Zeile. So landen nicht alle im gleichen Zertifikat. Achtung: Alle ServerAliases landen auch im Zertifikat. Deshalb steht example.org hier nicht mit drin!

example.org.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<VirtualHost *:80>
ServerName www.example.org
ServerAlias example.org
Redirect permanent / https://www.example.org/
</VirtualHost>

<VirtualHost *:443>
ServerName www.example.org
ServerAlias example.org
ServerAdmin webmaster@example.org
DocumentRoot "/usr/local/www/wordpress"
ErrorLog "/var/log/apache24/example.org-error.log"
CustomLog "/var/log/apache24/example.org-access.log" combined
Include etc/apache24/Includes/ssl.inc
</VirtualHost>

Im ersten VirtualHost biegen wir Port 80 auf 443 um. Der zweite VirtualHost hat am Schluss ein Include. In diesem File steckt alles was für SSL nötig ist aber nicht global definiert wird…

ssl.inc:

1
2
3
4
5
6
7
8
9
10
11
SSLEngine on

Header always set Strict-Transport-Security "max-age=31536000; preload"

BrowserMatch ".*MSIE.*" \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0

<FilesMatch "\.(cgi|shtml|phtml|php)$">
SSLOptions +StdEnvVars
</FilesMatch>

Nach einem reload mit apachectl reload holt sich der Server nun die Zertifikate…

 

8 Comments

  1. ungünstige schreibfehler zwingen einen zum mitdenken 😉

    zeile 15 in example.org.conf:
    Include etc/apache24/Includes/ssl.inc
    am anfang fehlt ein /

    zeile 12 in example.org.conf:
    ErrorLog „/var/log/apache24/example.org-error.log
    hinten fehlt ein “

    bin dabei die tips für ubuntu LTS 18.04 anzuwenden 😉
    gruß klaus lehmann

    ps: wer mitlesen will: c’t 2018/4 steht was zu mod_md
    im linux-magazin dieses jahres müsste auch was erschienen sein!
    „der“ kofler hat ein ebook zu ubuntu LTS 18.04 herausgebracht, aber zum thema subdomains schweigen sich mehr oder wenige ALLE aus!
    ausser Sie: Ekkehard Gehm. somit sehr loebenswert 😉

    1. Also nach dem mein ServerRoot (Option in der httpd.conf) „/usr/local“ ist, ist das in Zeile 15 Absicht. Denn fehlt der / Am Anfang, sind die Pfade relativ zu eben diesem ServerRoot. Zu Zeile 12: Uuups 🙂

      Generell sollte man ja eh immer ein wenig nachdenken, bevor man was Blind übernimmt.

  2. die zeile in ssl.inc
    <FilesMatch „\.(cgi|shtml|phtml|php)$“>
    kann meines wissen nicht so stehen (bleiben). ich habe keine ahnung, über welchen sinn sie verfügt, aber es gibt diesbezüglich apache-fehler.
    ausserdem muss man das modul „expores“ aktivieren!
    das kann von distri zu distri und besonders im vergleich zu freeBSD ziemlich verschieden sein 😉

    besser gehts (bei ubuntu) damit:

    Header set Expires „Mon, 1 Jan 2020 12:00:00 GMT“
    Header set Cache-Control „max-age=29030400, public“

    1. Huch! Da ist was verloren gegangen… Das FilesMatch war auch nicht geschlossen! Ich habe das eben verbessert. Ich aktiviere so StdEnvVars für Scripte. ( Zitat:

      „StdEnvVars
      When this option is enabled, the standard set of SSL related CGI/SSI environment variables are created. This per default is disabled for performance reasons, because the information extraction step is a rather expensive operation. So one usually enables this option for CGI and SSI requests only.“

      )

      Naja. Ich denke das sollte „mod_expires“ sein. Das muss man dann natürlich scharf schalten. Hat aber nichts mit mod_md zu tun, sondern mit den letzten Zeilen in Ihrem Kommentar…
      Ich denke auch nicht, dass es extreme Unterschiede gibt ob ich den Apache unter Ubuntu oder unter nem richtigen OS laufen lasse. Höchsten evtl. ein paar Pfade…

  3. mojn. die freuden vom nichtwissen zum thema „staging“ habe ich gestern nach noch kennengelernt ;-( . tja, zu viel getestet. und plötzlich gelingt nix mehr.

    ->ServerOption in http.conf (ist in ubuntu ausgeschaltet), vielleicht muss deshalb ein kein relativer pfad sein: bei DStoreDir /etc/apache2/md (für ubuntu)

    -> richtiges OS. tztztz. wat iss hier richtig, wat iss falsch? oooh, die philosophie des abendlandes 😉 ich komme eigentlich von der heulsuse her, aber mein servermeister (strato), unterstützt die suse nicht mehr. ich hatte dann nur die wahl zwischen debil und urwald. und LTR 18.04 ist ja auch nicht verkehrt.

    grüße, klaus lehmann

  4. so. mein system steht!

    1. auf (und das sollte jeder mal tun!) den folgenden ssl-kontroll-seiten kann man testen:
    https://securityheaders.com/?q=vufindnet.de&followRedirects=on
    https://www.ssllabs.com/ssltest/analyze.html?d=vufindnet.de&latest
    (bei ssllabs cache clearen!)

    2. wer erst mal mit „staging“ rumprobiert, was sehr klug ist, weil er von letsencrypt nicht „blockiert“ wird, muss von „staging“ zum regulären betrieb später wechseln: das geht so:
    https://github.com/icing/mod_md/issues/99#issuecomment-409167507

    3. für die sicherheit mit ssl habe ich folgendes getan (die urheber der guten gedanken sind mitangegeben!)
    SSLEngine on
    #wohl erst ab apache 2.5.xx
    #SSLPolicy modern
    BrowserMatch „.*MSIE.*“ \
    nokeepalive ssl-unclean-shutdown \
    downgrade-1.0 force-response-1.0

    Header set Expires „Mon, 1 Jan 2020 12:00:00 GMT“
    Header set Cache-Control „max-age=29030400, public“
    SSLOptions +StdEnvVars

    #tip von scott helme https://scotthelme.co.uk/a-new-security-header-referrer-policy/ 2018/078
    Header always set Referrer-Policy strict-origin-when-cross-origin
    #kein tip von ihm. nurkaround, um Header always set Feature-Policy drin zu haben, mit nix! 😉
    Header always set Feature-Policy „“

    #tip von https://spacehost.de/10-apache-header-eintraege-webseite-sicher-machen/ 2018/07
    #===============================================================================
    # HSTS verwenden Pflichtangabe: max-age““ Optional: „includeSubDomains“
    Header set Strict-Transport-Security „max-age=31556926; includeSubDomains“
    # Verhindert mime based attacks, nur IE und Chrome
    Header set X-Content-Type-Options „nosniff“
    # Aktiviert XSS Praeventions-/Filter-Tools Optional: mode=block
    Header set X-XSS-Protection „1; mode=block“
    # Begrenzung der frame/iframe Darstellung
    Header append X-Frame-Options „SAMEORIGIN“
    # Cookies nur ueber SSL and kein Javascript Zugriff Optional: Expires, Max-Age, Path, Domain
    Header always edit Set-Cookie (.*) „$1; HttpOnly; Secure“
    # Kein PHP und System-Version ausgeben
    Header unset X-Powered-By
    # Download / Lade Inhalte nur von Seiten die explizit erlaubt sind. Beispiel das alles von der eigenen Domain erlaubt allerdings keinerlei Externas:
    Header set Content-Security-Policy „default-src ’none‘; script-src ’self‘; connect-src ’self‘; img-src ’self‘; style-src ’self‘;“
    # Referrer Policy (siehe oben!)
    #Header set Referrer-Policy „origin-when-cross-origin“
    # Expect-CT fuer kommende Implementation Okt 2017 wo wird diese datei „reportOnly“ landen?
    Header set Expect-CT „max-age=0; report-uri=https://vufindnet.de/reportOnly“

    grüße, an alle, vom Klausi

    ps: ich finds schaisse, daß man seine beiträge, die bei mir voll mit rächtschaipfählern sind, nicht nachträglich korrigieren kann. pech für den leser! 😉

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.