Docker, WordPress und SSL

Wie man in Debian einen Blog aufbaut und diesen ein bisschen absichert.

Da stand ich nun. Ich wollte auch einen Blog haben. Insperiert und Naive. Geblendet von der Idee und dem Blog von Germancoding (blog.germancoding.com). Nicht mal nach 3 Einträgen bei meiner Suchmaschiene des Vertrauens wurde mir klar: „WordPress muss es sein!“. Also weiter gesucht und…oh nein WordPress ist gar nicht mal so sicher. Aber da soll nun eine kleine Software helfen: Docker.

Was ist Docker?

Docker ist eine Software welche Software mittels Container virtualisiert. Vorteil ist also das die Software, wenn sie in Docker läuft, auf allen Systemen laufen wird auf denen Docker laufen wird. Vorteil ist zu dem das es ein bisschen Sicherheit gegeben ist.

Docker installieren

Ich möchte mich in diesem Beispiel besonders mit der Installation bei Debian beschäftigen.

Wir fügen docker in unserer Repository hinzu. Dazu brauchen wir allerdings ein paar Programme darunter apt-transport-https, ca-certificates, curl, gnupg-agent und software-properties-commen.

$ sudo apt-get update

$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common

Nun fügen wir Docker’s offiziellen GPG-Schlüssel unserm Schlüsselbund hinzu.

$ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -

Um zu schauen ob wir den richtigen Schlüssel haben 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88 suchen wir einfach mal nach dem:

$ sudo apt-key fingerprint 0EBFCD88

pub   4096R/0EBFCD88 2017-02-22
      Key fingerprint = 9DC8 5822 9FC7 DD38 854A  E2D8 8D81 803C 0EBF CD88
uid                  Docker Release (CE deb) <docker@docker.com>
sub   4096R/F273FCD8 2017-02-22

Damit haben wir schon mal den Schlüssel! Als nächstes möchten wir das stable Repository hinzufügen. Dort enthalten sind alle aktuellen Versionen für Docker.

$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/debian \
   $(lsb_release -cs) \
   stable"

Jetzt nur noch docker installieren:

 $ sudo apt-get update
 $ sudo apt-get install docker-ce docker-ce-cli containerd.io

Jetzt nur noch ein kleiner Test ob Docker auch wirklich funktioniert:

$ sudo docker run hello-world

Jetzt sollte alles laufen und bereit sein für WordPress.

WordPress und MariaDB in Docker

Zuerst machen wir uns ein Verzeichnis in dem wir unseren ganzen Krempel rein tun.

mkdir ~/wordpress && cd ~/wordpress

Nun müssen wir unsere Datenbank anlegen in den WordPress sein Zeugs reinspeichert. Ich nutze MariaDB. Die einrichtung und installation kann mithilfe von nur einem Befehl erledigt werden. Die Variablenfür MAriaDB werden mit -e makiert.
-e MYSQL_ROOT_PASSWORD= Hier kannst du dein Passwort für die Datenbank setzen (Bitte merken ;-P )
-e MYSQL_DATABASE= Hier kannst du deiner Datenbank benennen.
Docker Parameter die ich nutze:
-name wordpressdb Name von dem Container
-v „$PWD/database“:/var/lib/mysql Das legt einen Ordner an indem wir unser Zeugs abspeichern damit die Daten persistent sind
-d Sagt Docker das der Container als daemon ausgeführt werden soll
mariadb:latest Hier sagen wir Docker er soll die aktuellste Version laden
Der Befehl ist also:

docker run -e MYSQL_ROOT_PASSWORD=<passwort> -e MYSQL_DATABASE=wordpress --name wordpressdb -v "$PWD/database":/var/lib/mysql -d mariadb:latest

Puhh das war schon ein bisschen Arbeit und WordPress ist noch nicht mal auf dem System! Keine sorge wir sind fast da! Also belohnung hier ein Bild von nem Süßem Hund:

Genug getrödelt weiter gehts:

Auch bei wordpress können wir wieder ein paar Präferenzen setzten:
-e WORDPRESS_DE_PASSWORD= Hier das Passwort von Oben einsetzen (Du hast es dir doch gemerkt oder?)
-name wordpress Name des Containers
-link wordpressdb:mysql Hier verbinden wir die beiden Container miteinander damit die beidem miteinander reden können
-p 5555:80 Hier sagen wir Docker dass wir jede HTTP anfragen von Port 5555 (such dir gerne einen eigenen aus) auf den Container internen Port 80 umleiten
-v „$PWD/database“:/var/www/html Das legt einen Ordner an indem wir unser Zeugs abspeichern damit die Daten persistent sind
-d Sagt Docker das der Container als daemon ausgeführt werden soll
Also ist der Befehl:

docker run -e WORDPRESS_DB_PASSWORD=<passwort> --name wordpress --link wordpressdb:mysql -p 5555:80 -v "$PWD/html":/var/www/html -d wordpress

Nun muss man nur noch mit dem Browser seiner Wahl auf deiner Server aufschalten (natürlich mit dem Port den du gewählt hattest). Das tolle an WordPress: Es gibt einen kleinen Wizard der dir helfen wird.
http://[deine ip]/wp-admin/install.php

Toll jetzt hab ich einen Blog und wie krieg ich das jetzt auf ne Domain?

Ruhig Blut Tiger! Wenn du jetzt eine Domain gekauft hast z.b. von hosting.de dann lasse deine Domain oder Subdomain deiner dein Server zeigen.

Als nächstes schnappen wir uns Apache um uns einen reverse Proxy zubasteln.

$sudo apt install apache2

und am besten noch die mods für den proxy hinterher:

$sudo apt install libapache2-mod-proxy-html

Nun aktivieren wir nur noch die Mods und schon kann es eigentlich los gehen!

$ sudo a2enmod proxy
$ sudo a2enmod proxy_http
$ sudo a2enmod proxy_ajp
$ sudo a2enmod rewrite
$ sudo a2enmod deflate
$ sudo a2enmod headers
$ sudo a2enmod proxy_balancer
$ sudo a2enmod proxy_connect
$ sudoa2enmod proxy_html

Vieles davon wird betreits Aktiv sein aber wir gehen mal hier auf Nummer sicher!

Im Ordner /etc/apache2/sites-available werden wir ein paar Seiten finden. Natürlich können wir diese Bearbeiten oder uns eine Neue anlegen. Dabei muss jedoch beachtet werden, dass die seite danach mit

$ sudo a2ensite neuerproxy
$ sudo systemctl restart apache2

aktiviert werden muss (neuerproxy ist hier nur ein Platzhalter).
Also die config mit dem Tool unserer Wahl öffnen und los gehts:

<VirtualHost *:80>
ServerName http://deinedomain.eu
ServerAlias http://deineanderedomain.eu
Redirect permanent / https://deinedomain.eu/
</VirtualHost>

Mit einem kleinen restart sollte nun jeder HTTP Traffic der für http://deinedomain.eu an Port 80 ankommt an https://deinedomain.eu weitergeleitet.

Was soll das denn?

Keine Sorge ich hab mir dabei schon was gedacht! Wir wollen doch nachher ein SSL-Zertifikat haben und jeden Traffic verschlüsseln. Jeder Traffic der jetzt an Port 80 ankommt und somit nicht verschlüsselt ist wird nun umgeleitet auf https://.

SSL und Reverse Proxy

Damit wir jetzt ein „kleines grünes Schloss“ bekommen nutzen wir ein Programm von Let’s Encrypt. Certbot nimmt uns viel Arbeit (und kosten) ab. Zuerst holen wir uns Certbot von den Debian Repositories. Da wir apache nutzen holen wir uns das entsprechende Modul dazu.

$ sudo apt install certbot python-certbot-apache

Na dem das dann auch durch ist starten wir Certbot und sagen Ihm das wir Apache nutzen

$ sudo certbot certonly --apache

Jetzt sollte Certbot in unserem System unsere grade angelegte Datei finden und uns anbieten für die Domain (hier in dem Beispiel deinedomain.eu) ein Zertifikat zu erzeugen. Wir wählen aus der Liste jetzt die Domains aus. Die Zahlen können mithilfe von Leerzeichen getrennt werden. So sind mehrfach auswahlen möglich.

Which names would you like to activate HTTPS for?- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  1: www.example.com- - - - - - - - - - - - - - - - - - - Select the appropriate numbers separated by commas and/or spaces, or leave inputblank to select all options shown (Enter 'c' to cancel): 

Im nächsten Schritt fragt uns Certbot was mit den HTTP Anfragen passieren sollen. wir wollen diese ja an HTTPS weiterleiten und das wählen wir dann auch aus (2)

Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Enabled Apache rewrite module
Redirecting vhost in /etc/apache2/sites-enabled/www.example.com.conf to ssl vhost in /etc/apache2/sites-available/www.example.com-le-ssl.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations! You have successfully enabled https://www.example.com

Reverse Proxy die 2.

Im Ordner /etc/apache2/sites-available/ sollten wir nun eine weitere conf finden die etwa so ausschaut neuerproxy-le-ssl.conf.
Zur Sicherheit einmal Aktivieren und nochmal neu starten:

$ sudo a2ensite neuerproxy-le-ssl.conf
$ sudo service restart apache2

Und jetzt endlich kommen wir endlich zum einstellen vom Reverse Proxy zu WordPress. Öffnen wir also die neue Datei und Spielen etwas rum:

<IfModule mod_ssl.c>
<VirtualHost *:443>
        ServerName deinedomain.eu
        ServerAlias deineanderedomain.eu
        RequestHeader set X-Forwarded-Proto "https"
        ProxyPreserveHost ON

        ProxyPass / http://localhost:5555/
        ProxyPassReverse / http://localhost:5555/
        ProxyRequests Off


Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateFile /etc/letsencrypt/live/deinedomain.eu/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/deinedomain.eu/privkey.pem
</VirtualHost>
</IfModule>

Und was bringt uns das? Nun das tolle an dem Aufbau ist nun dass WordPress sich nicht mehr um die Zertifikate kümmern muss sondern diese Aufgabe von Apache übernommen wird. Das spart seeeehr viel Zeit und Aufwand. Jetzt nochmal neustarten und ab gehts zu WordPress Einstellungsseite welche sich jetzt noch über http://deine-ipadresse:5555
aufrufen lässt. Unter Einstellungen->Allgemein muss nun die Wunschdomain noch eingestellt werden. Also in die Felder für Website und WordPress Adresse http://deinedomain.eu eingeben und speichern. ACHTUNG: Sollte nun deine Seite nicht erreichbar sein lösche bitte den Cache deines Browsers!
Und Tadaa deine Seite ist Live Glückwunsch erstmal!

SSL noch besser machen

Super wir haben nun ein Zertifikat und Seite. Um das zu feiern gehen wir auf https://www.ssllabs.com/ssltest/ und lassen uns die eine Wertung für unsere Seite geben.
Hmm irgentwie nicht so ein tolles Ergebnis oder? Scheinbar müssen wir noch ein bisschen weiter basteln. Es scheint ja fast so als ob wir sehr veraltete Protokolle nutzen und dann noch mit Schlüsseln die nicht mal gut sind! Und wo sind eigentlich die Security Header?
Also wieder in die Shell und los gepfuscht!
Gehen wir am besten mal in die Einstellungen von Certbot unter /etc/letsencrypt/options-ssl-apache.conf und setzen:

# Intermediate configuration, tweak to your needs
SSLProtocol             ALL -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite          ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY13$
SSLHonorCipherOrder     off
SSLCompression          off
SSLSessionTickets       off
SSLOptions +StrictRequire

Was tun wir da? SSLProtokol ALL aktiviert alle Protokolle aber wir schränken das ganze dann ein in dem wir SSLv2 SSLv3 TLSv1 und TLSv1.1 untersagen. Warum? Eine einfache Google suche wird zeigen das dies nur Vernüntig ist! Was bleibt ist als TLS2 und 3. Zu den Cipher Suits kann ich nur sagen Eliptische Kurven sind grade voll im Trend.
SSLHonorCipherOrder MUSS aus sein. Wäre es an könnte der Client dem Server sagen wie er sich verbinden will. Wir wollen dem Clienten aber sagen was zu tun ist. Ist ja auch unser Server!
SSLCompression sollte auch aus sein. Ja ein paar alte Geräte haben dann Schwierigkeiten mit dem Verbinden dafür sind wir dann abgesichert gegen CRIME.


SSLSessionTickets wollen wir auch auschalten da wir bei jeder Verbindung eine neue sichere Verbindung aufbauen wollen.
Mit SSLOptions +StrictRequire sperren wir alle aus die es nicht schaffen sich sicher zu verbinden.
Jetzt schalten wir noch das SSL Stapling an. Dies ist dafür da damit der Client nicht nochmal bei letsencrypt.org nachfragen muss ob unser Zertifikat noch gültig ist. dies kann der Client einfach direkt bei uns machen.
Dazu gehen wir einfach in /etc/apache2/conf-available/security.conf und fügen SSLUseStapling On hinzu.
Zuletzt gehen wir noch schnell in die /etc/apache2/sites-availible/neuerproxy-le-ssl.conf um ein paar Header zuerstellen.

#HSTS-Header Hier wird dem User gesagt das der Server #HTTPS hat und der User diese Info für eine gewisse zeit #speichern soll
        Header always set Strict-Transport-Security "max-      age=63072000"
        
# Aktivierung von HTTP/2
        Protocols h2 h2c http/1.1
        #CSP Header- Das dient dazu das nur dinge geladen werden die
        #auch wirklich nur von meiner Seite kommen. Cross Site Scripting
        #wird dadurch erschwert.

        Header always set Content-Security-Policy "upgrade-insecure-requests; frame-ancestors 'self'"


        #Cross Site Scripting Protection blockert die Seite wenn es zu einem
        # angriff kommt
        Header set X-XSS-Protection 1

        #Hier mit wird blockiert das andere Sites diese Websites einbinden
        Header always set X-Frame-Options "sameorigin"


#MIME sniffing verbieten
        Header set X-Content-Type-Options: nosniff

Mit einem beherztem Reload von Apache sollte nun alles für einen zweiten SSL Test bereit sein. Sollte man aber seine Bewertung nochmal verbessern wollen sollte man außerdem DNS CAA einrichten!

Sooo und das war es auch schon! Jetzt sollte dein Worpress sicherer sein. Nun müsste man eigentlich nur noch sein WordPress sicher machen aber das ist eine andere Geschichte!

1 Kommentar zu „Docker, WordPress und SSL“

  1. Paar Notizen. Keine vollständige Liste, sondern nur wild durcheinander ein paar Punkte die mir aufgefallen sind:

    Das Port Mapping -p 5555:80 bedeutet, dass der WordPress-Container „world-reachable“ ist, d.h man kann ganz einfach den Reverse Proxy (und damit auch TLS) bypassen, indem man die Webseite direkt unter http://domain:5555 aufruft. Bessere Isolierung wäre hier, wenn der Container nur unter localhost zu erreichen wäre, da er ja nur mit dem Reverse Proxy reden muss. Syntax bei Docker sieht dann so aus:

    -p 127.0.0.1:5555:80

    Ansonsten: WordPress verschickt standartmäßig hier und da ein paar Emails. Per Default mit PHP’s mail() Funktion, welche normalerweise die Mailfunktionalität im OS benutzt. In Docker geht das natürlich aufgrund der Isolierung nicht, daher funktionieren Emails in WordPress häufig erstmal gar nicht. Ähnliche Problematik hat man auch auf manchen Shared Hostern. Die Lösung hier ist, ein Plugin zu installieren, welches WordPress beibringt, Mails auf andere Art zu versenden. Üblicherweise via SMTP.

    Der Punkt mit SSLHonorCipherOrder stimmt so nicht – Details dazu hatten wir ja schon privat besprochen.

    Thema SSLCompression – OpenSSL 1.1 und up unterstützen Compression überhaupt gar nicht erst, sprich es einzuschalten produziert sowieso nur Fehler. Kompatibilitätsprobleme mit Clients sind mir keine bekannt. Compression wurde nie flächendeckend genutzt – der Default war immer off – und laut Qualys unterstützen 99.7% aller Webseites keine TLS Kompression (mehr).

    Das deaktivieren von SSLSessionTickets deaktiviert nicht (zwangsweise) Session Resumption insgesamt – es gibt nach wie vor Resumption via Session IDs. Der Grund Session Tickets auszuschalten ist nicht, keine Resumption haben zu wollen, sondern die Tatsache das Session Tickets Stateless sind. Im Session Ticket sind sämtliche Secrets der Verbindung kodiert (verschlüsselt mit einem Session Ticket Key). Gerät das Session Ticket (+ Key) in die falschen Hände kann viel passieren, selbst Perfect Forward Secrecy kann nachträglich gebrochen werden. Session Tickets sind nur lohnenswert für große CDN’s, die zwingend serverübergreifende Resumption brauchen.

    SSLOptions +StrictRequire hat nichts mit dem Verbinden zu tun, sondern ändert gewisse Apache-Interne Policies im Bezug auf die (veraltete) SSLRequire Direktive. Damit lassen sich interne Regelsets setzen, die Access/Deny auf eine Ressource regeln.

Kommentar verfassen

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

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.