Der erste Schritt besteht darin, sich einen Server zu besorgen. In dieser Anleitung benutze ich einen Server, der sich nicht bei mir zu Hause befindet, dies bietet meistens einige Vorteile, wie beispielsweise:
Einfachere Erreichbarkeit durch eine feste IPv4 Adresse
Schnellere Internetgeschwindigkeit
Bessere Kostenprognose möglich
Keine Abnutzung eigener Hardware
Keine Stromkosten
Geringere Anschaffungskosten
Keine Geräuschemission zu Hause
Selbstverständlich sollte man sich auch den Nachteilen bewusst sein. Jedenfalls lässt sich diese Anleitung mit jedem Server befolgen, auf dem Docker installiert werden kann. Probleme hatte ich in der Vergangenheit mit virtuellen Servern, die mit OpenVZ statt KVM virtualisiert waren.
Ich werde hier einen VPS S SSD von Contabo nutzen (Kosten liegen bei ca. 6 € / Monat).
Empfehlenswert sind meiner Meinung auch die VPS und insbesondere auch Root-Server (dezidierte CPU) von Netcup. Falls du dort noch nicht Kunde sein solltest, bekommst du mit folgenden Gutscheinen einen Rabatt in Höhe von 5 €, oder einen VPS oder Root-Server günstiger. Ich bekomme eine Provision für geworbene Neukunden. Sollten die Gutscheine abgelaufen sein, kannst du mich gerne nach neuen anschreiben.
Nachdem der Zugriff auf den Server erteilt wird, lässt sich dieser aus dem Kundenbereich heraus neu installieren. Ich wähle als Betriebssystem Ubuntu 20.04 aus. Ein anderes wäre auch möglich, aber ich habe einige Erfahrung mit Ubuntu und sehe keinen Grund zu wechseln. Des Weiteren ist das Ende des Standard-Supports erst im April 2025, somit wird es vermutlich erst mal nicht nötig sein, das Betriebssystem auf die nächste Version zu upgraden.
Nach ein paar Minuten sollte der Server mit der Installation fertig sein. Nun wird es Zeit sich mit dem Server zu verbinden. Dies lässt sich unter Windows mit dem Windows Terminal, PuTTY oder einem ähnlichen Programm bewerkstelligen. Ich nutze das Windows Terminal.
Anschließend gegebenenfalls die Frage mit „yes“ beantworten und das Passwort eingeben. Während der Eingabe wird das Passwort nicht angezeigt. Folgende Meldung sollte nun erscheinen (oder ähnlich):
Als Erstes schauen wir mal, ob es Updates gibt:
apt update
apt upgrade
Die Frage, ob man updaten möchte, mit Y bestätigen.
Der Texteditor Nano sollte schon standardmäßig installiert sein, aber zur Sicherheit kann man ihn so installieren:
apt install nano
Als Nächstes benennen wir den Server um:
nano /etc/hostname
In Nano wird die Datei mit Strg + X verlassen. Nano wird euch Fragen, ob ihr die Änderungen speichern wollt (mit Y bestätigen) und wie ihr die Datei nennen wollt. Da wir sie überschreiben möchten, ändern wir den Namen nicht und bestätigen einfach mit Enter.
Sobald du den Server neu startest reboot
und dich anschließend wieder mit ihm verbindest ssh admin@{ip-Adresse}
ist der neue Host-Name sichtbar.
Jetzt ist es sinnvoll einen Nutzer anzulegen, der nicht root ist. Ohne Rechteeinschränkungen ist es mit root nämlich ziemlich einfach sein System zu zerstören.
useradd -m -G sudo namedesneuennutzerspasswd namedesneuennutzers
useradd
ist der Befehl, um einen neuen Nutzer anzulegen. Das -m
sorgt dafür, dass dieser Nutzer einen Home-Ordner eingerichtet bekommt. Das -G
packt den Nutzer in eine Gruppe, anschließend wird die Gruppe genannt. Die Sudo Gruppe darf Befehle mit sudo
ausführen. Zuletzt folgt der Name des neuen Nutzers. Mit passwd
wird ein Password für den Nutzer erstellt.
Sollte die sudo Gruppe nicht existieren, musst du eine Gruppe erstellen.
groupadd namederneuengruppe
Diese Gruppe sollte das Recht haben Sudo-Befehle auszuführen:
EDITOR=nano visudo
In der Datei muss dann die neue Gruppe erwähnt werden.
Nun stellen wir bei Bedarf die richtige Zeit ein.
date
Falls nicht die richtige Zeit angezeigt wird:
tzselect
Über die Zahlen könnt ihr eure Zeitzone auswählen.
timedatectl set-timezone 'Europe/Berlin'
Es ist an der Zeit den Server neu zu starten und sich als neu angelegter Nutzer anzumelden.
reboot
Als Nächstes installiere ich Htop (ein Monitoring-Programm).
apt install htop
Jetzt müsste der Server die Ausführung verweigern und fragen, ob man root sei.
Also noch mal mit Superuser do.
sudo apt install htop
Über die Eingabe von htop
kann man sich nun die Auslastung des Servers anschauen.
Damit lässt sich gut verfolgen, wie stark die CPU und der RAM des Servers beansprucht werden. Mit F10 oder STRG + C verlässt du die Ansicht wieder.
Ich möchte gerne noch eine andere Shell haben.
sudo apt install git curlsudo apt install zsh
Jetzt installiere ich zim und ändere die Standard-Shell auf zsh.
curl -fsSL https://raw.githubusercontent.com/zimfw/install/master/install.zsh | zshchsh -s /bin/zshexec bash
Jetzt müsste die Shell (subjektiv) besser aussehen.
Der wichtigste Part der Installation sind Docker und Docker Compose. Außerdem optional noch Lazydocker, für eine sehr einfache Möglichkeit sich seine Docker-Container und deren aktuelle Logs über SSH anzusehen.
sudo apt install ca-certificates curl gnupg lsb-releasecurl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpgecho \"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/nullsudo apt updatesudo apt-get install docker-ce docker-ce-cli containerd.io docker-composecurl https://raw.githubusercontent.com/jesseduffield/lazydocker/master/scripts/install_update_linux.sh | bash
Jetzt muss noch Docker aktiviert werden.
sudo systemctl enable --now docker
Sollte die nächste Zeile nicht erscheinen mit STRG + C nachhelfen. Nun noch den angelegten Nutzer der Dockergruppe zuordnen.
sudo gpasswd -a namedesneuennutzers docker
Sicherheitstechnisch sollte man das nicht so machen. Dann muss man aber immer wieder sudo
vor die Docker Befehle zu schreiben. Überprüfe noch, ob der Benutzer tatsächlich in der Gruppe gelandet ist.
id namedesneuennutzers
Als Letztes richten wir einen Ordner im Heimverzeichnis des neuen Nutzers an.
cd ~mkdir docker
Mit cd
(“change directory”) bewegen wir uns durch die Ordner, wobei die Tilde (~) für home steht. mkdir
erstellt einen neuen Ordner. Man kann Ordner bei Bedarf auch wieder löschen.
mkdir docker2# erstellt Ordnermkdir -rf docker2# löscht Ordner
Folgende Eingabe sollte nun den neu erstellten Ordner zeigen.
ls
Wir können uns auch die Berechtigungen des Ordners anschauen und den Eigentümer.
ls -l
Oder auch die ausgeblendeten Ordner.
ls -A# bzw.ls -Al
Wir können uns in den neuen Ordner bewegen.
cd docker# und wieder zurückcd ..
Im Übrigen kann man viel Zeit mit der Tabulator-Taste sparen. Diese Taste vervollständigt Befehle. Gibt man im Home-Ordner cd d
ein und drückt Tab, wird das Wort automatisch vervollständigt. Über die Pfeiltasten oben und unten lassen sich die letzten Befehle anzeigen. Pfeiltaste nach rechts übernimmt den vollständigen Befehl, wenn einer vorgeschlagen wird.
Sollte sich Lazydocker öffnen lassen, ist die Einrichtung abgeschlossen.
sudo lazydocker
Mit Q, ESC oder STRG + C lässt sich Lazydocker wieder beenden. Mit exit
verlassen wir den Server.
exit
Der Server besitzt nur eine IP-Adresse, wir möchten aber eventuell mehrere Dienste auf dem Server einrichten. Möglichkeit 1 wäre es, diese über verschiedene Ports laufen zu lassen, dann würde man die Dienste über die Port-Nummer ansteuern müssen (123.45.67.89:443, 123.45.67.89:9000, usw.). Das ist erstens schwer zu merken und zweitens gibt es ein Problem mit den SSL Zertifikaten, es entsteht ein Sicherheitsrisiko. Deswegen ist es besser einen Proxy Manager auf Port 80 und 443 (die Standardports für http und https) zu schalten, dieser leitet dann die Anfragen weiter.
Dafür benötigen wir (Sub-)Domains. So ist es beispielsweise mit der Subdomain “mail.domain1.de” das Mailprogramm zu erreichen und mit” blog.domain1.de” den Blog. Möglich wäre das zwar auch mit Unterverzeichnissen (wie z. B. “domain1.de/mail” und “domain1.de/blog”), aber ich finde Subdomains schöner.
Es gibt zwar kostenlose Domains bei bspw. DuckDNS, aber die sind meist mit Einschränkungen verbunden. Studenten können sich bei Github auch eine vollwertige kostenlose Domain für ein Jahr besorgen.
The best developer tools, free for students. Get your GitHub Student Developer Pack now.
Jedoch ist es auch nicht so teuer, sich eine Domain zu kaufen. Bei Netcup kostet eine .de Domain dauerhaft 5 € im Jahr. Jeder andere Registrar ist auch in Ordnung, solange das DNS und am besten auch die Nameserver eingestellt werden können. Achte auch darauf, dass es bei einigen Anbietern ab dem zweiten Jahr teurer wird, insbesondere dann, wenn es im ersten Jahr nur 1 € kostet.
Im CCP von Netcup rufe ich den Menüpunkt Domains auf:
Uns interessiert der Tab mit den DNS Einstellungen:
Ich verringere zunächst die TTL ein wenig (24h ist mir zu lang).
Anschließend müssen einige Einträge gemacht werden. Das Zeichen @ bedeutet, dass es um den Root der Zone geht, also meinedomain.de wird auf die IP-Adresse 160.90.80.80 (IP-Adresse meines Servers) verwiesen. Außerdem werden alle (deshalb das ””) Subdomains von meinedomain.de wiederum auf meinedomain.de geroutet. Statt könnte man auch einzelne Einträge machen. Das kann z. B. dann sinnvoll sein, wenn man mehrere Server unter unterschiedlichen IP-Adressen betreibt.
Die Einstellungen werden über die Schaltfläche gespeichert. Nun muss man warten. Es kann einen Tag dauern bis die Einstellungen übernommen werden, in seltenen Fällen sogar auch zwei. In der Regel geht es aber deutlich schneller.
Wir testen, ob es geklappt hat, indem wir das Terminal aufrufen und versuchen die Domain zu pingen.
ping dieeingerichtetedomain.de
Erscheint jetzt die Meldung, dass die Ping-Anforderung den Host nicht finden konnte, wissen wir, dass die Einträge noch nicht übernommen wurden.
Manchmal hilft es, die gecachten DNS im System zu leeren.
ipconfig /flushdns
Anschließend noch mal versuchen. Wenn die IP-Adresse des Servers angezeigt wird, ist die Domain fürs Erste eingerichtet.
Wir benötigen einen Proxy Manager, der jetzt die Anfragen in Abhängigkeit der (Sub-)Domain weiterleiten kann. Auch wenn ich mich in der Vergangenheit eigentlich mit dem Nginx Proxy Manager angefreundet habe, möchte ich hier Traefik v2 nutzen.
Traefik ist ebenfalls ein open source Reverse Proxy, der aber auf Docker aufsetzt. Zwar ist die Einrichtung meiner Meinung nach komplizierter, dafür ist es anschließend leichter, dynamisch weitere Container mit Traefik zu starten. An dieser Stelle verweise ich außerdem auf zwei andere Anleitungen, die ich zur Konfiguration genutzt habe. Das ist einmal ein Blogeintrag von DigitalOcean und einer von Smarthomebeginner.
Zunächst geben wir der Gruppe Docker die nötigen Rechte am Ordner docker. Dazu benötigen wir ACL.
sudo apt install aclcd ~sudo setfacl -Rdm g:docker:rwx dockersudo setfacl -Rm g:docker:rwx dockersudo chmod -R 775 dockergetfacl docker
Wir erstellen eine Datei mit Umgebungsvariablen. Es gibt zwar auch Stimmen, die meinen, man sollte Umgebungsvariablen vermeiden bzw. für sensible Daten nicht nutzen, aber mir erscheint es immer noch besser, als sie direkt einzusetzen (meiner Meinung nach noch unsicherer) oder so was wie Keywhiz zu nutzen (ist mir zu viel Aufwand). Deshalb erstellen wir die Datei mit den nötigen Variablen. Zuvor müssen wir unsere User ID und die Gruppen ID von Docker auslesen.
id
cd dockernano .env
PUID=1000PGID=114
Jetzt sollten noch die Zeitzone, die Verzeichnispfade und die eigene Domain eingefügt werden.
pwd
nano .env
PUID=1000PGID=114TZ=Europe/BerlinUSERDIR=/home/jewgeniDOCKERDIR=/home/jewgeni/dockerDOMAINNAME=deployn.de
In der Shell ist es überdies möglich über > bzw. >> Dinge direkt in eine Datei zu schreiben. Der Befehl echo wiederholt die Eingabe. Das testen wir mal.
cd ~echo "hallo"
Es sollte jetzt das Wort „hallo“ erscheinen.
echo "hallo" > test.txtls# Wir sehen, dass im Home-Verzeichnis unseres Benutzers nun eine Textdatei liegt.# Die Raute am Anfang der Zeile steht übrigens für einen Kommentar.# Dieser Code wird nicht ausgeführt.nano test.txt
In der Datei steht „hallo“ :) Diese Datei lässt sich durch weiteren Text ergänzen.
echo "tschüss" > test.txt# Das gibt einen Fehler, weil die Datei schon exisitiert.# Mit >> fügen wir einen Text in eine bestehende Datei als neue Zeile ein.echo "tschüss" >> test.txtnano test.txt
Wenn bei der Eingabe im Terminal grauer Text erscheint, lässt sich dieser mit der Pfeiltaste nach rechts übernehmen. In der Textdatei sind nun zwei Zeilen. Wir löschen sie wieder.
rm test.txtls
Es sollte nur noch der Ordner “docker” sichtbar sein. Nun zum weiteren Vorgang. Wir benötigen als nächstes eine Möglichkeit gehashte Passwörter zu erstellen, dafür installieren wir die Apache2 Hilfsprogramme und testen ob über htpasswd ein gehastes Passwort erstellt wird.
sudo apt install apache2-utilshtpasswd -nb benutzername passwort
Ergebnis (sollte ähnlich aussehen):
Jetzt nutzt du die Daten, die wirklich zur Authentifizierung dienen sollen (am besten ein neues Passwort über einen Passwortgenerator erstellen).
mkdir ~/docker/shared/htpasswd -nb einbenutzername neuespasswort > ~/docker/shared/.htpasswd
Als Nächstes benötigen wir die Daten für unsere Zertifikate. Wenn sich deine Domain nicht bei Netcup befindet, müssen die Angaben angepasst werden. Bei Netcup benötigen wir drei Angaben: Kundennummer, API-Schlüssel und API-Passwort.
In den Stammdaten des CCP gibt es den API-Menüpunkt. Hier kannst du dir einen API-Schlüssel generieren lassen. Lies jedoch vorher die Nutzungsbedingungen durch. Mit dem API-Schlüssel ist es möglich Domainkäufe zu tätigen. Allein aus diesem Grund sollte das Passwort geheim gehalten werden (im Übrigen auch dein Passwort zum CCP).
Diese Werte tragen wir in die .env Datei ein, außerdem noch eine eigene E-Mail Adresse für die Let’s encrypt Zertifikate
nano ~/docker/.env
PUID=1000PGID=114TZ=Europe/BerlinUSERDIR=/home/jewgeniDOCKERDIR=/home/jewgeni/dockerDOMAINNAME=einedomain.deNETCUP_CUSTOMER_NUMBER=50000NETCUP_API_KEY=LDBfH4MNyDlgBgww0lGry0OkouhkMqUI5ENETCUP_API_PASSWORD=uEzCAzISBUBwGqW9oSEzRJuL3D26Gm1yQFMlnqSB1Lhjjb5Z98EMAIL_ADDRESS=mail@hallo.de
Als Nächstes erstellen wir einen Ordner für Traefik und unsere Zertifikate. Außerdem noch die Zertifikat-Datei und eine Log-Datei.
mkdir ~/docker/traefikmkdir ~/docker/traefik/acmetouch ~/docker/traefik/traefik.logtouch ~/docker/traefik/acme/acme.jsonchmod 600 ~/docker/traefik/acme/acme.json
Jetzt erstellen wir uns ein neues Netzwerk in Docker.
docker network create webdocker network ls
Es sind nun vermutlich vier Einträge zu sehen (bridge, host, none und web). Zeit unsere hauptsächliche docker-compose-Datei zu erstellen.
nano ~/docker/docker-compose.yml
Jetzt den Text eingeben.
version: '3.7'### NETWORKS ###networks:web:external:name: webdefault:driver: bridge### SERVICES ###services:traefik:container_name: traefikimage: traefik:latestrestart: unless-stoppedcommand:- --global.checkNewVersion=true- --global.sendAnonymousUsage=false- --entryPoints.http.address=:80- --entryPoints.https.address=:443- --entryPoints.traefik.address=:8080- --api=true- --api.dashboard=true- --log=true- --log.level=DEBUG- --accessLog=true- --accessLog.filepath=/traefik.log- --accessLog.bufferingSize=100- --accessLog.filters.statusCodes=400-499- --providers.docker=true- --providers.docker.endpoint=unix:///var/run/docker.sock- --providers.docker.exposedByDefault=false- --providers.docker.network=web- --providers.docker.swarmMode=false- --providers.file.directory=/rules- --providers.file.watch=true- --certificatesResolvers.dns-netcup.acme.dnsChallenge.provider=netcup- --certificatesResolvers.dns-netcup.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory- --certificatesResolvers.dns-netcup.acme.email=$EMAIL_ADDRESS- --certificatesResolvers.dns-netcup.acme.storage=/acme.json- --certificatesResolvers.dns-netcup.acme.dnsChallenge.resolvers=1.1.1.1:53,8.8.8.8:53- --certificatesResolvers.dns-netcup.acme.dnschallenge.delayBeforeCheck=60networks:- websecurity_opt:- no-new-privileges:trueports:- target: 80published: 80protocol: tcpmode: host- target: 443published: 443protocol: tcpmode: host- target: 8080published: 8080protocol: tcpmode: hostvolumes:- $DOCKERDIR/traefik/rules:/rules- /var/run/docker.sock:/var/run/docker.sock:ro- $DOCKERDIR/traefik/acme/acme.json:/acme.json- $DOCKERDIR/traefik/traefik.log:/traefik.log- $DOCKERDIR/shared:/sharedenvironment:- NETCUP_CUSTOMER_NUMBER=$NETCUP_CUSTOMER_NUMBER- NETCUP_API_KEY=$NETCUP_API_KEY- NETCUP_API_PASSWORD=$NETCUP_API_PASSWORDlabels:- 'traefik.enable=true'- 'traefik.http.routers.http-catchall.entrypoints=http'- 'traefik.http.routers.http-catchall.rule=HostRegexp(`{host:.+}`)'- 'traefik.http.routers.http-catchall.middlewares=redirect-to-https'- 'traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https'- 'traefik.http.routers.traefik-rtr.entrypoints=https'- 'traefik.http.routers.traefik-rtr.rule=Host(`traefik.$DOMAINNAME`)'- 'traefik.http.routers.traefik-rtr.tls=true'- 'traefik.http.routers.traefik-rtr.tls.certresolver=dns-netcup'- 'traefik.http.routers.traefik-rtr.tls.domains[0].main=$DOMAINNAME'- 'traefik.http.routers.traefik-rtr.tls.domains[0].sans=*.$DOMAINNAME'- 'traefik.http.routers.traefik-rtr.service=api@internal'- 'traefik.http.routers.traefik-rtr.middlewares=middlewares-basic-auth@file'
Damit die letzte Zeile funktioniert, müssen wir die Middleware konfigurieren. Das passiert mit einer Datei im Ordner mit allen Regeln.
mkdir ~/docker/traefik/rulesnano ~/docker/traefik/rules/middlewares.toml[http.middlewares][http.middlewares.middlewares-basic-auth][http.middlewares.middlewares-basic-auth.basicAuth]realm = "Traefik Basic Auth"usersFile = "/shared/.htpasswd"
Jetzt ist der Zeitpunkt gekommen alles zu testen.
cd ~/dockerdocker-compose up -d
Sobald alles fertig ist, schauen wir uns das automatische Protokoll im rechten Fenster an.
lazydocker
Wenn die Meldung kommt, dass auf eine DNS record propagation gewartet wird, ist alles soweit richtig.
In diesem Fall schrauben wir den Log-Level zurück auf WARN und holen echte Zertifikate, indem wir die Zeile mit --certificatesResolvers.dns-netcup.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
auskommentieren.
nano ~/docker/docker-compose.yml
nano ~docker/traefik/acme/acme.json
In dieser Datei den kompletten Inhalt löschen (auch die Leerzeilen). Jetzt Traefik neu starten.
cd ~/dockerdocker-compose downdocker-compose up -d
Warten (5 Minuten), danach versuchen das Dashboard über traefik.dieeigenedomain.de zu öffnen. Falls es nicht klappt, noch mal den Log-Level auf DEBUG setzen, Container neu starten und schauen, was das Log anzeigt.
Um ins Dashboard zu gelangen, musst du den Benutzernamen und das nicht gehashte Passwort eingeben, das du in die htpasswd Datei geschrieben hast. Wenn alles geklappt hat, können wir Traefik dazu bewegen ein Wildcard-Zertifikat zu nutzen.
nano ~/docker/docker-compose.yml
Diesmal muss das Label “traefik.http.routers.traefik-rtr.tls.certresolver=dns-netcup” auskommentiert werden.
cd ~/dockerdocker-compose downdocker-compose up -d
Wir ergänzen unsere Middleware um ein Rate-Limit zur Anmeldung und Security Headers. Hier oder hier sind zwei Beispiele für weitere Informationen über Security Headers.
nano ~/docker/traefik/rules/middlewares.toml
[http.middlewares][http.middlewares.middlewares-basic-auth][http.middlewares.middlewares-basic-auth.basicAuth]realm = "Traefik Basic Auth"usersFile = "/shared/.htpasswd"[http.middlewares.middlewares-rate-limit][http.middlewares.middlewares-rate-limit.rateLimit]average = 100burst = 50[http.middlewares.middlewares-secure-headers][http.middlewares.middlewares-secure-headers.headers]accessControlAllowMethods= ["GET", "OPTIONS", "PUT"]accessControlMaxAge = 100hostsProxyHeaders = ["X-Forwarded-Host"]sslRedirect = truestsSeconds = 63072000stsIncludeSubdomains = truestsPreload = trueforceSTSHeader = truecustomFrameOptionsValue = "allow-from https:dieeigenedomain.de"contentTypeNosniff = truebrowserXssFilter = truereferrerPolicy = "same-origin"featurePolicy = "camera 'none'; geolocation 'none'; microphone 'none'; payment 'none'; usb 'none'; vr 'none';"[http.middlewares.middlewares-secure-headers.headers.customResponseHeaders]X-Robots-Tag = "none,noarchive,nosnippet,notranslate,noimageindex,"server = ""
Wir haben drei Middlewares eingerichtet:
Um sie zu nutzen, können wir sie einzeln in die docker-compose Datei eintragen oder Ketten mit Kombinationen aus ihnen einrichten.
Um eine Kette zu erstellen, kreieren wir eine neue Datei im Rules-Ordner:
nano ~/docker/traefik/rules/middlewares-chains.toml
[http.middlewares][http.middlewares.chain-no-auth][http.middlewares.chain-no-auth.chain]middlewares = [ "middlewares-rate-limit", "middlewares-secure-headers"][http.middlewares.chain-basic-auth][http.middlewares.chain-basic-auth.chain]middlewares = [ "middlewares-rate-limit", "middlewares-secure-headers", "middlewares-basic-auth"]
Natürlich könnt ihr auch andere Kombinationen ergänzen, aber dann ist es eventuell einfacher, die oben genannte Code-Zeile zu nutzen und die Dinge zu kombinieren, die man haben möchte. Durch die Ketten ist es möglich, die drei oben gezeigten Zeilen in eine zu ändern:
Nachdem dieses „Etikett“ in der docker-compose Datei eingetragen ist, muss Traefik neu gestartet werden, damit die Änderungen in Kraft treten.
cd ~/dockernano docker-compose# Datei anpassen (- "traefik.http.routers.traefik-rtr.middlewares=middlewares-chain-basic-auth@file" in die labels einfügen)# Auf die Einrückung beim Einfügen achtendocker-compose downdocker-compose up -d
Jetzt im Dashboard nachsehen, ob die Middleware richtig geladen werden.
Und prüfen, ob die Kette als Middleware im HTTP-Router von Traefik geladen wird:
Die Authentifizierung, die wir bereits eingerichtet haben, bringt zwar zusätzliche Sicherheit, aber es gibt Authentifizierungsmethoden mit mehr Funktionen. Deshalb möchten wir uns mal Google OAuth anschauen.
Meet your business challenges head on with cloud computing services from Google, including data management, hybrid & multi-cloud, and AI & ML.
— Cloud Computing Services | Google Cloud
Es ist nötig sich in der Cloud-Konsole zu registrieren (kostenlos). Nach einigen Klicks gelangt man ins Dashboard.
Hier erstellen wir ein neues Projekt und benennen es so, dass wir es wiederfinden.
Zunächst muss ein Zustimmungsbildschirm erstellt werden.
Dahin gelangen wir über den Menüpunkt APIs & Dienste.
Ein Name muss angegeben werden, eine E-Mail-Adresse auch. Optional kann auch ein Logo hochgeladen werden.
Eine Domain muss autorisiert werden. Das Limit liegt bei 10.
Bei den Bereichen wählen wir die drei nicht kritischen Bereiche aus. E-Mail, Profile und OpenID.
Bei den Testnutzern haben wir eine Obergrenze von 100. Diese 100 Nutzer dürfen die Anwendung dahinter nutzen. Also trag dich da am besten selbst ein, andere Nutzer lassen sich später nachtragen.
Über APIs & Dienste wählen wir Anmeldedaten.
Nun fügen wir eine OAuth-Client-ID hinzu.
Wir tragen eine autorisierte Weiterleitungsdomain ein.
Die Schlüssel, die uns soeben angezeigt werden, speichern wir in die .env Datei ab.
nano ~/docker/.env
Die neuen Einträge nennen wir GOOGLE_CLIENT_ID
und GOOGLE_CLIENT_SECRET
. Danach erstellen wir ein GOOGLE_OAUTH_SECRET
.
echo "GOOGLE_OAUTH_SECRET=$(openssl rand -hex 16)" >> ~/docker/.envnano ~/docker/.env
Die Datei dürfte jetzt ungefähr so aussehen:
PUID=1000PGID=114TZ=Europe/BerlinUSERDIR=/home/jewgeniDOCKERDIR=/home/jewgeni/dockerDOMAINNAME=irgendeinedomain.deNETCUP_CUSTOMER_NUMBER=50000NETCUP_API_KEY=lksjfrjgoigKJLKjlhirNETCUP_API_PASSWORD=SlkjiojKLhiuuzvTFDENgdeuEMAIL_ADDRESS=mail@gmail.comGOOGLE_CLIENT_ID=uhrlkiaungopterktdsnf.apps.googleusercontent.comGOOGLE_CLIENT_SECRET=lkiz537JkhkrGOOGLE_OAUTH_SECRET=leaj7GjejmG
Jetzt muss diese Authentifizierungsmethode auch Traefik hinzugefügt werden. Zunächst als Middleware.
nano ~/docker/traefik/rules/middlewares.toml
[http.middlewares][http.middlewares.middlewares-basic-auth][http.middlewares.middlewares-basic-auth.basicAuth]realm = "Traefik Basic Auth"usersFile = "/shared/.htpasswd"[http.middlewares.middlewares-oauth][http.middlewares.middlewares-oauth.forwardAuth]address = "http://oauth:4181"trustForwardHeader = trueauthResponseHeaders = ["X-Forwarded-User"][http.middlewares.middlewares-rate-limit][http.middlewares.middlewares-rate-limit.rateLimit]average = 100burst = 50[http.middlewares.middlewares-secure-headers][http.middlewares.middlewares-secure-headers.headers]accessControlAllowMethods= ["GET", "OPTIONS", "PUT"]accessControlMaxAge = 100hostsProxyHeaders = ["X-Forwarded-Host"]sslRedirect = truestsSeconds = 63072000stsIncludeSubdomains = truestsPreload = trueforceSTSHeader = truecustomFrameOptionsValue = "allow-from https:example.com"contentTypeNosniff = truebrowserXssFilter = truereferrerPolicy = "same-origin"featurePolicy = "camera 'none'; geolocation 'none'; microphone 'none'; payment 'none'; usb 'none'; vr 'none';"[http.middlewares.middlewares-secure-headers.headers.customResponseHeaders]X-Robots-Tag = "none,noarchive,nosnippet,notranslate,noimageindex,"server = ""
Und dann als Kette.
nano ~/docker/traefik/rules/middlewares-chains.toml
[http.middlewares][http.middlewares.chain-no-auth][http.middlewares.chain-no-auth.chain]middlewares = [ "middlewares-rate-limit", "middlewares-secure-headers"][http.middlewares.chain-basic-auth][http.middlewares.chain-basic-auth.chain]middlewares = [ "middlewares-rate-limit", "middlewares-secure-headers", "middlewares-basic-auth"][http.middlewares.chain-oauth][http.middlewares.chain-oauth.chain]middlewares = [ "middlewares-rate-limit", "middlewares-secure-headers", "middlewares-oauth"]
Bevor wir das testen können, müssen wir aber den OAuth-Dienst in Docker starten. Also ergänzen wir die docker-compose Datei.
nano ~/docker/docker-compose.yaml
version: '3.7'### NETWORKS ###networks:web:external:name: webdefault:driver: bridge### SERVICES ###services:### TRAFIK - REVERSE PROXY ###traefik:container_name: traefikimage: traefik:latestrestart: unless-stoppedcommand:- --global.checkNewVersion=true- --global.sendAnonymousUsage=false- --entryPoints.http.address=:80- --entryPoints.https.address=:443- --entryPoints.traefik.address=:8080- --api=true- --api.dashboard=true- --log=true- --log.level=WARN- --accessLog=true- --accessLog.filepath=/traefik.log- --accessLog.bufferingSize=100- --accessLog.filters.statusCodes=400-499- --providers.docker=true- --providers.docker.endpoint=unix:///var/run/docker.sock- --providers.docker.exposedByDefault=false- --providers.docker.network=web- --providers.docker.swarmMode=false- --providers.file.directory=/rules- --providers.file.watch=true- --certificatesResolvers.dns-netcup.acme.dnsChallenge.provider=netcup# - --certificatesResolvers.dns-netcup.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory- --certificatesResolvers.dns-netcup.acme.email=$EMAIL_ADDRESS- --certificatesResolvers.dns-netcup.acme.storage=/acme.json- --certificatesResolvers.dns-netcup.acme.dnsChallenge.resolvers=1.1.1.1:53,8.8.8.8:53- --certificatesResolvers.dns-netcup.acme.dnschallenge.delayBeforeCheck=10networks:- websecurity_opt:- no-new-privileges:trueports:- target: 80published: 80protocol: tcpmode: host- target: 443published: 443protocol: tcpmode: host- target: 8080published: 8080protocol: tcpmode: hostvolumes:- $DOCKERDIR/traefik/rules:/rules- /var/run/docker.sock:/var/run/docker.sock:ro- $DOCKERDIR/traefik/acme/acme.json:/acme.json- $DOCKERDIR/traefik/traefik.log:/traefik.log- $DOCKERDIR/shared:/sharedenvironment:- NETCUP_CUSTOMER_NUMBER=$NETCUP_CUSTOMER_NUMBER- NETCUP_API_KEY=$NETCUP_API_KEY- NETCUP_API_PASSWORD=$NETCUP_API_PASSWORDlabels:- 'traefik.enable=true'- 'traefik.http.routers.http-catchall.entrypoints=http'- 'traefik.http.routers.http-catchall.rule=HostRegexp(`{host:.+}`)'- 'traefik.http.routers.http-catchall.middlewares=redirect-to-https'- 'traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https'- 'traefik.http.routers.traefik-rtr.entrypoints=https'- 'traefik.http.routers.traefik-rtr.rule=Host(`traefik.$DOMAINNAME`)'- 'traefik.http.routers.traefik-rtr.tls=true'# - "traefik.http.routers.traefik-rtr.tls.certresolver=dns-netcup"- 'traefik.http.routers.traefik-rtr.tls.domains[0].main=$DOMAINNAME'- 'traefik.http.routers.traefik-rtr.tls.domains[0].sans=*.$DOMAINNAME'- 'traefik.http.routers.traefik-rtr.service=api@internal'- 'traefik.http.routers.traefik-rtr.middlewares=chain-oauth@file'### GOOGLE OAUTH ###oauth:container_name: oauthimage: thomseddon/traefik-forward-auth:latestrestart: unless-stoppednetworks:- websecurity_opt:- no-new-privileges:trueenvironment:- CLIENT_ID=$GOOGLE_CLIENT_ID- CLIENT_SECRET=$GOOGLE_CLIENT_SECRET- SECRET=$GOOGLE_OAUTH_SECRET- COOKIE_DOMAIN=$DOMAINNAME- INSECURE_COOKIE=false- AUTH_HOST=oauth.$DOMAINNAME- URL_PATH=/_oauth- WHITELIST=$EMAIL_ADDRESS- LOG_LEVEL=info- LOG_FORMAT=text- LIFETIME=2592000labels:- 'traefik.enable=true'- 'traefik.http.routers.oauth-rtr.entrypoints=https'- 'traefik.http.routers.oauth-rtr.rule=Host(`oauth.$DOMAINNAME`)'- 'traefik.http.routers.oauth-rtr.tls=true'- 'traefik.http.routers.oauth-rtr.service=oauth-svc'- 'traefik.http.services.oauth-svc.loadbalancer.server.port=4181'- 'traefik.http.routers.oauth-rtr.middlewares=chain-oauth@file'
Sollen mehrere E-Mail-Adressen sich anmelden dürfen, muss die Umgebung bei WHITELIST
erweitert werden. WHITELIST=$EMAIL_ADDRESS, $EMAIL_ADDRESS2, …
Diese Konstanten müssen dann auch in die .env Datei eingetragen werden und ggf. in die Google Cloud Konsole.
Als einfachen Beispieldienst möchte ich jetzt eine Webseite (kann auch jeder andere Dienst sein) hosten. Dazu nutze ich das offizielle Docker Image von Nginx.
cd ~/dockermkdir appdatamkdir appdata/testseitecd ~/docker/appdata/testseitenano index.html
Hier kann nun ein einfacher HTML-Code zum Test eingetragen werden.
<!DOCTYPE html><html><head><title>Testseite</title></head><body><p>Hallo</p></body></html>
Danach benötigen wir eine neue docker-compose Datei für den Dienst.
cd ~/dockernano docker-compose-testseite.yml
version: '3.7'### NETWORKS ###networks:web:external:name: webdefault:driver: bridge### SERVICE ###services:testseite:container_name: testseiteimage: nginx:latestrestart: unless-stoppednetworks:- websecurity_opt:- no-new-privileges:truevolumes:- $DOCKERDIR/appdata/testseite:/usr/share/nginx/html:rolabels:- 'traefik.enable=true'- 'traefik.http.routers.testseite-rtr.entrypoints=https'- 'traefik.http.routers.testseite-rtr.rule=Host(`testseite.$DOMAINNAME`)'- 'traefik.http.routers.testseite-rtr.tls=true'- 'traefik.http.routers.testseite-rtr.service=testseite-svc'- 'traefik.http.services.testseite-svc.loadbalancer.server.port=80'- 'traefik.http.routers.testseite-rtr.middlewares=chain-no-auth@file'
docker-compose -f docker-compose-testseite.yml up -d
Die Seite müsste daraufhin wenige Augenblicke später unter testseite.domain.de erreichbar sein. Doch was ist, wenn man eine zweite Seite online haben möchte? Gleiche Prozedur:
cd ~/dockermkdir appdatamkdir appdata/testseite2cd ~/docker/appdata/testseite2nano index.html
<!DOCTYPE html><html><head><title>Testseite</title></head><body><p>Hallo</p><p>Das ist die zweite Seite.</p></body></html>
cd ~/dockernano docker-compose-testseite2.yml
version: '3.7'### NETWORKS ###networks:web:external:name: webdefault:driver: bridge### SERVICE ###services:testseite2:container_name: testseite2image: nginx:latestrestart: unless-stoppednetworks:- websecurity_opt:- no-new-privileges:truevolumes:- $DOCKERDIR/appdata/testseite2:/usr/share/nginx/html:rolabels:- 'traefik.enable=true'- 'traefik.http.routers.testseite2-rtr.entrypoints=https'- 'traefik.http.routers.testseite2-rtr.rule=Host(`testseite2.$DOMAINNAME`)'- 'traefik.http.routers.testseite2-rtr.tls=true'- 'traefik.http.routers.testseite2-rtr.service=testseite2-svc'- 'traefik.http.services.testseite2-svc.loadbalancer.server.port=80'- 'traefik.http.routers.testseite2-rtr.middlewares=chain-basic-auth@file'
docker-compose -f docker-compose-testseite2.yml up -dlazydocker
In Lazydocker sollten jetzt beide Dienste zu sehen sein.
Über testseite.domain.de und testseite2.domain.de sind die beiden Seiten erreichbar, Portfreigaben mussten nicht erstellt werden. Wenn die Einstellungen meiner Docker-Compose Dateien übernommen werden, erscheint bei der testseite2 das Anmeldefenster mit der einfachen Authentifizierung. Die Container können indessen wieder gestoppt werden.
cd ~/dockerdocker-compose -f docker-compose-testseite.yml downdocker-compose -f docker-compose-testseite2.yml down
Die Einrichtung ist damit fertig 😃