Änderungsstand: 2021-05-05
2021-05-05: Crontab angepasst. Ein eingeschlichener Fehler wurde behoben.
Dieser Raspi wird später als Server mit traefik2 als Reverse-Proxy eingerichtet, mit zertifiziertem SSL – Zugriff von aussen und ohne separater Serversoftware. Ich installiere einige Programme nach, die ich im späteren Verlauf noch benötige. Dann installiere ich Portainer und DuckDNS. Mit Portainer kann ich schnell meine Docker-Konfigurationen überprüfen, ohne das Terminal zu verwenden.
Ich wählte für die Installation der folgenden Sachen das Raspian-Lite BUSTER als Betriebssystem aus. Hier geht’s zum Guide.
Vorbereitungen:
Zu Beginn erstelle ich ein DuckDNS-Konto um einen DynDns-Dienst zu haben. Dort erstelle ich 1 Hostnamen.
Jetzt installiere ich folgende Sachen:
sudo apt install docker docker-compose curl jq mc
portainer:
Jetzt installiere ich den ersten Docker. Das wird Portainer sein. Ich erstelle meine Docker-*.yml Dateien im /home/dockervolumes-Verzeichnis:
sudo mkdir /home/dockervolumes
Nun folgende Befehle im Terminal eingeben ( Ordner wird erstellt und die *.yml wird dort geöffnet).
sudo mkdir /home/dockervolumes/portainer
cd /home/dockervolumes/portainer && sudo nano docker-compose.yml
Folgendes dort einfügen (vorher Eure Werte überprüfen):
version: '2'
services:
portainer:
image: portainer/portainer
command: -H unix:///var/run/docker.sock
restart: always
ports:
- 9000:9000
- 8000:8000
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /var/lib/PortainerData:/data
Speichern mit Strg-x, y, Enter
Quelle: https://portainer.readthedocs.io/en/latest/deployment.html
Anschließend die „yml“ starten mit
cd /home/dockervolumes/portainer && sudo docker-compose up -d
Mit IP-Adresse:9000 kann man die Portainer-Web-Oberfläche im Browser starten. Auf die Funktionsweise des Portainers gehe ich hier nicht ein.
duckdns:
Meine verwendete Domain in diesem Guide ist knilix.duckdns.org.
sudo mkdir -p /home/dockervolumes/duckdns
nano /home/dockervolumes/duckdns/docker-compose.yml
---
version: "2"
services:
duckdns:
image: linuxserver/duckdns
container_name: duckdns
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Berlin
- SUBDOMAINS=knilix
- TOKEN=DASTOKEN
- LOG_FILE=true
volumes:
- /var/lib/DuckdnsConfig:/config
restart: unless-stopped
STRG-x, y, Enter
Quelle: https://hub.docker.com/r/linuxserver/duckdns
Anschließend die „yml“ starten:
cd /home/dockervolumes/duckdns &&sudo docker-compose up -d
Die Vorbereitungen wären abgeschlossen.
Traefik:
Hier nur die kompletten Befehle und Infos, die nötig sind.
sudo mkdir -p /home/dockervolumes/traefik
sudo touch /home/dockervolumes/traefik/acme.json
sudo chmod 600 /home/dockervolumes/traefik/acme.json
sudo touch /var/log/traefik.log
sudo chmod 600 /var/log/traefik.log
sudo docker network create traefik-net
sudo docker network create intern
Middlewares (Passwortschutz der Weboberfläche):
Auf der Seite https://hostingcanada.org/htpasswd-generator/ geben wir einen Usernamen und ein Passwort ein. Ich verwende für diesen Guide 1 User und 1 dazugehöriges Passwort. Verwendet bitte richtige Passwörter!!! karldall ist kein passwort – er ist ein Komiker 🙂 Dies dient nur zur Demonstration!!!
User) admin Passwort: karldall
Diesen User und das Passwort gebe ich jetzt auf der zuvor verlinkten Seite ein und generiere meine Passwort, welches ich später für die traefik-docker.yml benötige. Ich verwende die Methode *Apache spezific….. Folgendes bekomme ich heraus:
admin:$apr1$zejclqhm$OdhdcM9CR37zSovA7o6mq1
Jetzt ist wichtig, vor JEDEM $ noch ein $ zu setzen!!!
admin:$$apr1$$zejclqhm$$OdhdcM9CR37zSovA7o6mq1
Diese Ausgabe merken bzw. kopieren.
Jetzt eine neue Datei anlegen. Nennen wir diese dynamic_conf.yml.
dynamic_conf.yml
sudo nano /home/dockervolumes/traefik/dynamic_conf.yml
tls:
options:
default:
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
- TLS_AES_128_GCM_SHA256
- TLS_AES_256_GCM_SHA384
- TLS_CHACHA20_POLY1305_SHA256
curvePreferences:
- CurveP521
- CurveP384
sniStrict: true
http:
middlewares:
secHeaders:
headers:
browserXssFilter: true
contentTypeNosniff: true
frameDeny: true
sslRedirect: true
#HSTS Configuration
customFrameOptionsValue: "SAMEORIGIN"
stsIncludeSubdomains: true
stsPreload: true
stsSeconds: 31536000
https-redirect:
redirectScheme:
scheme: https
STRG-x, y, Enter
Vielen Dank an dieser Stelle an https://goneuland.de/traefik-v2-https-verschluesselung-sicherheit-verbessern/. Ohne diesen Tip wäre es hier nicht so einfach weiter gegangen. Ganz speziell die Header-Sache hat mich am WordPress fast zum Verzweifeln gebracht. Ich habe diese Datei um ein kleines Extra erweitert (die letzten 3 Zeilen).
Traefik.yml
Nicht vergessen, Eure Email einzutragen!
sudo nano /home/dockervolumes/traefik/traefik.yml
## STATIC CONFIGURATION
log:
level: INFO
filePath: "/traefik.log"
api:
insecure: true
dashboard: true
entryPoints:
http:
address: ":80"
https:
address: ":443"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
network: traefik-net
exposedByDefault: false
file:
filename: "/dynamic_conf.yml"
certificatesResolvers:
lets-encr:
acme:
storage: acme.json
email: EMAIL-ADRESSE
httpChallenge:
entryPoint: http
STRG-x, y, Enter
Passen wir die traefik-docker-compose.yml noch an. Traefik wird nun von Innen und von Aussen erreichbar sein. Natürlich hier unbedingt von Aussen Passwortgeschützt und mit einer sicheren Verbindung. Wartet bitte ca. 1 Minute nach dem Start von Traefik, dass Letsencrypt genügent Zeit hat, die Zertifikate anzulegen. Das gilt auch für die anderen Docker.
Traefik-Compose
Hier tritt die Wirksamkeit des Passwortschutzes in Kraft (In Zeile 32, hinter users= die Ausgabe des vorhin erstellten Passwortes eingeben (das “ am Ende nicht vergessen!))!
sudo nano /home/dockervolumes/traefik/docker-compose.yml
version: '3'
services:
traefik:
image: "traefik:v2.2"
container_name: "traefik"
hostname: "traefik"
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- traefik-net
ports:
- 80:80
- 443:443
- 8080:8080
volumes:
- "/etc/localtime:/etc/localtime:ro"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "/var/log/traefik.log:/traefik.log"
- "./traefik.yml:/traefik.yml:ro"
- "./acme.json:/acme.json"
- "./dynamic_conf.yml:/dynamic_conf.yml:ro"
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.entrypoints=http"
- "traefik.http.routers.traefik.rule=Host(`traefik.knilix.duckdns.org`)"
- "traefik.http.middlewares.traefik-auth.basicauth.users=admin:$$apr1$$2W74R0Cf$$HD9TjPUXTtDIfbdFU3FtN."
- "traefik.http.routers.traefik.middlewares=https-redirect@file"
- "traefik.http.routers.traefik-secure.entrypoints=https"
- "traefik.http.routers.traefik-secure.rule=Host(`traefik.knilix.duckdns.org`)"
- "traefik.http.routers.traefik-secure.middlewares=traefik-auth"
- "traefik.http.routers.traefik-secure.tls=true"
- "traefik.http.routers.traefik-secure.tls.certresolver=lets-encr"
- "traefik.http.routers.traefik-secure.service=api@internal"
networks:
traefik-net:
external: true
STRG-x, y, Enter
Falls sich jemand fragt, was es mit der letzten Zeile external: true auf sich hat, bekommt jetzt die Antwort. Damit wird dem Docker nur angegeben, dass es sich um ein selbst-angelegtes Netwerk handelt und bei beenden und/oder Löschen des Dockers, das Netzwerk, in diesem Fall traefik-net, nicht mit gelöscht werden soll. Oder so ähnlich…
cd /home/dockervolumes/traefik && sudo docker-compose up -d
Mit Aufruf von traefik.knilix.duckdns.org sehen wir, nach Passworteingabe, die Benutzeroberfläche von Traefik, natürlich mit SSL. Von Innen ist Traefik weiterhin mit VM-Server-IP:8080 erreichbar.
MariaDB
Erstellen wir unsere Datenbank, die auf dem System zuständig sein wird:
sudo mkdir /home/dockervolumes/mariadb
cd /home/dockervolumes/mariadb && sudo nano docker-compose.yml
---
version: "2"
services:
mymariadb:
image: linuxserver/mariadb
container_name: mariadb
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- MYSQL_ROOT_PASSWORD=123458789
- TZ=Europe/Berlin
volumes:
- /var/lib/MariaDB:/config
ports:
- 3306:3306
networks:
- intern
networks:
intern:
external: true
STRG-x, y, Enter
MariaDB wird nur im Netzerk intern verfügbar sein. Und das auch richtig so! Keiner will, dass eine Datenbank irgendwo Frei-Zugänglich herumliegt!
Nach dem Start, zur ersten Initialisierung, benötigt MariaDB eine Weile. 1 Minute warten, bevor man darauf zugreift.
cd /home/dockervolumes/mariadb && sudo docker-compose up -d
Schön, dass wir jetzt die Datenbank installiert haben. Mit unserer Angabe von „volumes“ haben wir den Vorteil, dass die Datenbankbankdaten ausserhalb des Dockers abgelegt werden. Somit bleiben die Daten erhalten, auch wenn der Docker mal gelöscht werden sollte. Mit Hilfe von phpMyAdmin lege ich mir meine Datenbanken selbst an.
PhpMyAdmin
Da wir in unserer eigenen VM die Sachen installieren, verzichte ich auch bei PhpMyAdmin auf den Zugriff von Aussen.
sudo mkdir -p /home/dockervolumes/phpmyadmin
sudo nano /home/dockervolumes/phpmyadmin/docker-compose.yml
---
version: "2"
services:
phpmyadmin:
image: jackgruber/phpmyadmin
container_name: phpmyadmin
restart: unless-stopped
environment:
# IP des Raspi verwenden!
- PMA_HOST=192.168.252.118
- PMA_PORT=3306
ports:
- 8081:80
- 9001:9000
networks:
- intern
networks:
intern:
external: true
STRG-x, y, Enter
Starten wir den Docker:
cd /home/dockervolumes/phpmyadmin && sudo docker-compose up -d
Jetzt lege ich meine Datenbank per Hand an. Aufruf von PhpMyAdmin im Browser mit VM-IP:8081
Login mit root und das Passwort, welches im mariadb-Dockerfile angegeben wurde. !
Auch hier machte der Browsercache, bei meinen unendlichen Versuchen, immer wieder den Spaß zunichte. Aber irgendwann gewöhnt man sich dran…
Phpmyadmin startet und knallt uns prompt einen kleinen Warnhinweis um die Ohren. Doch das ist normal.

Einfach auf „Finden Sie heraus, warum“ klicken

Und jetzt auf „anlegen“ klicken – Fertig. Es erscheinen ganz viele „OK’s“.
Dann auf Datenbanken (oberes Menufeld) klicken, einen Datenbanknamen im Eingabefeld eintragen (nextcloud) und utf8_general_ci und auf „Anlegen“ klicken.

Jetzt im linken Menu auf „Neu“ und im oberen Menu auf „Benutzerkonten“ klicken. Nun auf „Neu – Benutzerkonto hinzufügen“

Benutzername und Passwort angeben. Hostname gebe ich mit % an. Authentifizierung ist „Native MYSQL.Authentifizierung“.
Jetzt ganz runter scrollen und auf „OK“ klicken. Alles was mit „Globale Rechte“ zu tun hat, bleibt bitte AUS! Auch Datenbank für Benutzerrechte lassen wir unberührt.
Jetzt legen wir noch für unseren soeben erstellten Benutzer die Datenbankrechte an. Links im Menu auf „Neu“ und oben im Menu auf „Benutzerkonten“. Jetzt an den soeben erstellten Benutzernamen „Rechte ändern“ klicken, oben, in der zweiten Reihe, den Reiter „Datenbank“ wählen, im Auswahlmenu „nextcloud“ selektieren und OK. Jetzt bei „Datenbankspezifife Rechte“ – „Alle auswählen“ und OK.
Soeben wurde eine Datenbank für nextcloud erstellt, mit einem Benutzer, der NUR Rechte für die Datenbank nextcloud besitzt. Was wir heute nicht alles lernen 🙂
Da wir jetzt wissen, wie es funktioniert, legen wir auch gleich eine Datenbank und einen Benutzernamen für WordPress an
Wenn fertig, kann das phpmadmin-Fenster geschlossen werden.
Nextcloud
Hier wäre der Eintrag external_links wichtig: Hier kommt die Datenbankzuweisung des Services der MariaDB rein. In meinen Fall mymariadb. Die Passwortabfrage kann man sich bei einer Cloud sparen. Aber ich habe diese absichtlich reingesetzt, damit man mit den einzelnen Befehlen erste „Gehversuche“ machen kann. Die Zeilen 17 bis 19 und 24 bis 26 können theoretisch raus. Oder man setzt ein # vor der Zeile 18 und Zeile 25.
sudo mkdir /home/dockervolumes/nextcloud
cd /home/dockervolumes/nextcloud && sudo nano docker-compose.yml
version: "3.3"
services:
nextcloud:
image: nextcloud
container_name: nextcloud
hostname: "nextcloud"
restart: unless-stopped
ports:
- 8082:80
volumes:
- /var/lib/Nextcloud/html:/var/www/html
labels:
- "traefik.enable=true"
- "traefik.http.routers.nextcloud.entrypoints=http"
- "traefik.http.routers.nextcloud.rule=Host(`nc.knilix.duckdns.org`)"
## Passwortabfrage
- "traefik.http.middlewares.nextcloud-auth.basicauth.users=admin:$$apr1$$2W74R0Cf$$HD9TjPUXTtDIfbdFU3FtN."
##
- "traefik.http.routers.nextcloud.middlewares=https-redirect@file"
- "traefik.http.routers.nextcloud-secure.entrypoints=https"
- "traefik.http.routers.nextcloud-secure.middlewares=calcarddav,secHeaders@file"
- "traefik.http.routers.nextcloud-secure.rule=Host(`nc.knilix.duckdns.org`)"
## gehört zu Passwortabfrage
- "traefik.http.routers.nextcloud-secure.middlewares=nextcloud-auth"
##
- "traefik.http.routers.nextcloud-secure.tls=true"
- "traefik.http.routers.nextcloud-secure.tls.certresolver=lets-encr"
# Traefik-Redirects für CalDAV / CardDAV:
- "traefik.http.middlewares.calcarddav.redirectregex.permanent=true"
- "traefik.http.middlewares.calcarddav.redirectregex.regex=https://(.*)/.well-known/(card|cal)dav"
- "traefik.http.middlewares.calcarddav.redirectregex.replacement=https://$$1/remote.php/dav/"
external_links:
- mymariadb
networks:
- intern
- traefik-net
networks:
intern:
external: true
traefik-net:
external: true
STRG-x, y, Enter
cd /home/dockervolumes/nextcloud && sudo docker-compose up -d
Auf zur Einrichtung Nextcloud
Im Browser gebe ich jetzt nc.knilix.duckdns.org ein und erhalte das Konfigurationsmenu von Nextcloud – natürlich mit zertifiziertem https, wenn man kurz wartet! Bevor mann die Einrichtung beginnt, unbedingt schauen, dass man auch https verwendet!
Administrator-Konto anlegen: lasst Euch was einfallen
Datenverzeichnis: /var/www/html/data
Speicher & Datenbank: MySQL/MARIADB
Datenbankbenutzer: Euren Benutzer, der soeben in phpmyadmin erstellt wurde
Datenbankpasswort: Das dazugehörige Passwort
Datenbankname: nextcloud
Datenbank-Host: Raspi-IP:3306

Jetzt auf „Installation abschließen“ klicken. Da dauert dauert jetzt etwas. Nach ca. 1-2 Minuten springt das Anmeldefenster um und es werden noch empfohlene Apps installiert, weil ich den Haken bei „Empfohlene Apps installieren“ gesetzt habe. Irgendwas ist doch immer^^.
Die gesamte Ersteinrichtung dauerte dann doch gefühlte 4 Minuten.

Fertig!
Ein Tip!
Das Login mit dem Benutzernamen, welchen wir für Nextcloud in der *.yml angelegt haben, gilt NUR für den Datenbankzugriff (z.B über phpmyadmin). Der Login im Nextcloud ist das Administrator-Login, welches vor der Ersteinrichtung im Browser angelegt wurde.
Sicherheits- & Einrichtungswarnungen in Nextcloud:
Einige Sachen werden hier behoben.
Ein weiterer Tip (Login-Problem Handy-App)!
Nach dem Browser-Login im Nextcloud, erstelle ich einen weiteren Benutzer, der keine admin-Rechte hat. Dann logge ich mit diesem Benutzer ein, gehe auf Einstellungen (rechts oben) und dann links auf Sicherheit. Dort im Hauptfenster, ziemlich weit unten auf App-Name, dort gebe ich einen Namen ein, klicke auf „Neues App-Passwort erstellen“ und dann auf „QR-Code für mobile Apps anzeigen“. Diesen QR-Code verwende ich dann für das erste Login in der Nextcloud-App am Handy. Somit wäre binnen Sekunden die Einrichtung der Handy-App erledigt.
WordPress
Wichtig! DB_Host ist der Name des Services, welcher in der maraidb-compose hinterlegt ist. Ebenso external_links.
sudo mkdir -p /home/dockervolumes/wordpress
sudo nano /home/dockervolumes/wordpress/docker-compose.yml
version: '3.1'
services:
wordpress:
image: wordpress
restart: unless-stopped
ports:
- 8083:80
environment:
# Daten müssen in der Datenbank schon angelegt sein!
WORDPRESS_DB_HOST: mymariadb
WORDPRESS_DB_USER: wie_in_Datenbank
WORDPRESS_DB_PASSWORD: 'wie_in_Datenbank'
WORDPRESS_DB_NAME: wordpress
volumes:
- /var/lib/Wordpress:/var/www/html
labels:
- "traefik.enable=true"
- "traefik.http.routers.wordpress.entrypoints=http"
- "traefik.http.routers.wordpress.rule=Host(`wordpress.knilix.duckdns.org`)"
- "traefik.http.routers.wordpress.middlewares=https-redirect@file"
- "traefik.http.routers.wordpress-secure.entrypoints=https"
- "traefik.http.routers.wordpress-secure.rule=Host(`wordpress.knilix.duckdns.org`)"
- "traefik.http.routers.wordpress-secure.middlewares=secHeaders@file"
- "traefik.http.routers.wordpress-secure.tls=true"
#
- "traefik.http.routers.wordpress-secure.tls.certresolver=lets-encr"
external_links:
- mymariadb
networks:
- traefik-net
- intern
networks:
traefik-net:
external: true
intern:
external: true
STRG-x, y, Enter
cd /home/dockervolumes/wordpress && sudo docker-compose up -d
Aufruf mit wordpress.knilix.duckdns.org. Es erfolgt eine automatische Umleitung zu https und die Ersteinrichtung kann beginnen.
Fertig. Im wahrsten Sinne des Wortes^^.

Jetzt noch ein Check auf SSL Labs (https://www.ssllabs.com/ssltest/)

Update der Docker.
.
Wie aktualisiert man jetzt die Docker? Ich verwende dafür ein kleines Script. Bedenkt, dass ALLE Docker, von denen sich in /home/dockervolumes die Scripte befinden, geupdated und gestartet werden, auch wenn diese nicht relevant sind. Soll heissen, dass die Compose-Files von Dockern, die keine Verwendung mehr haben oder momentan nicht benötigt werden, nicht im Verzeichnis /home/dockervolumes sein sollten. Als Bonus kann man in einem Logfile schauen, wann das letzte Update mit diesem Script getätigt wurde.
Im Terminal:
sudo touch /home/updatelog.txt && echo // Updates mit "updatescript" | sudo tee -a /home/updatelog.txt
#!/bin/bash
# Automatisches Update ALLER Docker der Docker-Compose-Scriptfiles: /home/dockervolumes/*
# Dateipfad: /scriptfiles/updatescript.sh
# Logfile: /home/updatelog.txt
# System-Update, falls gewünscht
###sudo apt update && sudo apt upgrade -y
# Datenbank stoppen
##cd /home/dockervolumes/mariadb
##sudo docker-compose stop
# Datenbank gestoppt (wird automatisch wieder gestartet)
# Angabe Docker-Hauptverzeichnis
cd /home/dockervolumes
readarray -d '' composeConfigs < <(find . -type f -name docker-compose.y* -print0)
for cfg in "${composeConfigs[@]}"; do
docker-compose -f "$cfg" pull
docker-compose -f "$cfg" up -d
done
# Alte Images automatisch löschen
docker image prune -f
Strg-x, y, Enter
chmod 700 updatescript.sh
Zum Aufruf des Scriptes folgenden Befehl eingeben:
sudo ./updatescript.sh
Entfernt man bei den beiden Befehlen mit ## davor die ##, wird die Datenbank vor der Überprüfung der Docker gestoppt. Anschließend wird diese automatisch wieder gestartet. Dies dient zur Sicherheit, um einen Crash oder Datenverlust vorzubeugen, falls bei der Aktualisierung im selben Moment darauf geschrieben werden sollte.
Unter /home/updatelog.txt kann man den Zeitpunkt der Updates einsehen. Mein erster Eintrag sieht folgendermaßen aus:
// Updates mit updatescript
d=21-05-04_23:44:03
Möchte man dieses Script täglich durchlaufen lassen, dann hilft folgendes:
sudo crontab -e
Falls noch nicht geschehen, den Editor wählen. Ich verwende die 1 (nano). Folgendes am Ende der Datei einfügen (das Hashtag am Ende nicht vergessen):
0 5 * * * sudo ~/scriptfiles/updatescript.sh >/dev/null 2>&1
#
STGR-x, y, Enter
Jetzt wird täglich 05:00 Uhr geschaut, ob ein Update des Dockers verfügbar ist und gegebenenfalls geupdated.
Möchte man nur einmal pro Woche updaten, z.B Montag 05:00 Uhr, dann:
0 5 * * 1 sudo ~/scriptfiles/updatescript.sh >/dev/null 2>&1
#
STGR-x, y, Enter
Oder nur einmal im Monat, z.B. am 01. jeden Monats um 05:00 Uhr:
0 5 * 1 * sudo ~/scriptfiles/updatescript.sh >/dev/null 2>&1
#
STGR-x, y, Enter
.Quelle(n):
Quelle(n):
https://greenitsolutions.at/traefik-docker-lets-encrypt-tutorial/
https://goneuland.de/traefik-v2-reverse-proxy-fuer-docker-unter-debian-10-einrichten/
https://github.com/DoTheEvo/Traefik-v2-examples
https://thibmaek.com/post/updating-all-docker-compose-container
https://www.smarthomebeginner.com/traefik-reverse-proxy-tutorial-for-docker
https://chriswiegman.com/2020/01/running-nextcloud-with-docker-and-traefik-2