HomeBlogÜber

VPS (Server) mit Traefik Reverse Proxy einrichten

Von Jewgeni
Kategorie: Server
April 16, 2021
10 Minuten
VPS (Server) mit Traefik Reverse Proxy einrichten

Inhaltsverzeichnis

01
Server installieren
02
Einrichtung einer Domain
03
Einrichtung von Traefik
04
Google Authentifizierung
05
Webseite hosten

Server installieren

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.

Windows Terminal
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):

Login
Login

Als Erstes schauen wir mal, ob es Updates gibt:

apt update

Apt Update
Apt Update
Falls Updates existieren, installieren wir diese anschließend:

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

Hostname
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.

Neuer Hostname
Neuer Hostname

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 namedesneuennutzers
passwd 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.

Visudo
Visudo

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.

Unzureichende Berechtigung
Unzureichende Berechtigung

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 curl
sudo 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 | zsh
chsh -s /bin/zsh
exec bash

Jetzt müsste die Shell (subjektiv) besser aussehen.

zsh
zsh

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-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
"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/null
sudo apt update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose
curl 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 Ordner
mkdir -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ück
cd ..

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

Einrichtung einer Domain

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:

CCP
CCP

Uns interessiert der Tab mit den DNS Einstellungen:

DNS Einstellungen
DNS Einstellungen

Ich verringere zunächst die TTL ein wenig (24h ist mir zu lang).

TTL
TTL

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.

DNS Einstellungen
DNS Einstellungen

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

DNS-Flush
DNS-Flush

Anschließend noch mal versuchen. Wenn die IP-Adresse des Servers angezeigt wird, ist die Domain fürs Erste eingerichtet.

Einrichtung von Traefik

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 acl
cd ~
sudo setfacl -Rdm g:docker:rwx docker
sudo setfacl -Rm g:docker:rwx docker
sudo chmod -R 775 docker
getfacl docker

ACL
ACL
Es müsste nun angezeigt werden, dass die Gruppe Docker am Ordner docker die Rechte zum Lesen ( read), Schreiben (write) und Ausführen (execute) hat.

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

id
id
Diese beiden Zahlen müssen wir in die Umgebungsdatei einfügen.

cd docker
nano .env
PUID=1000
PGID=114

Jetzt sollten noch die Zeitzone, die Verzeichnispfade und die eigene Domain eingefügt werden.

pwd

PWD
PWD

nano .env
PUID=1000
PGID=114
TZ=Europe/Berlin
USERDIR=/home/jewgeni
DOCKERDIR=/home/jewgeni/docker
DOMAINNAME=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.txt
ls
# 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.txt
nano 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.txt
ls

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-utils
htpasswd -nb benutzername passwort

Ergebnis (sollte ähnlich aussehen):

htpasswd
htpasswd

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).

API Aktivierung im CCP
API Aktivierung im 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=1000
PGID=114
TZ=Europe/Berlin
USERDIR=/home/jewgeni
DOCKERDIR=/home/jewgeni/docker
DOMAINNAME=einedomain.de
NETCUP_CUSTOMER_NUMBER=50000
NETCUP_API_KEY=LDBfH4MNyDlgBgww0lGry0OkouhkMqUI5E
NETCUP_API_PASSWORD=uEzCAzISBUBwGqW9oSEzRJuL3D26Gm1yQFMlnqSB1Lhjjb5Z98
EMAIL_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/traefik
mkdir ~/docker/traefik/acme
touch ~/docker/traefik/traefik.log
touch ~/docker/traefik/acme/acme.json
chmod 600 ~/docker/traefik/acme/acme.json

Jetzt erstellen wir uns ein neues Netzwerk in Docker.

docker network create web
docker 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: web
default:
driver: bridge
### SERVICES ###
services:
traefik:
container_name: traefik
image: traefik:latest
restart: unless-stopped
command:
- --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=60
networks:
- web
security_opt:
- no-new-privileges:true
ports:
- target: 80
published: 80
protocol: tcp
mode: host
- target: 443
published: 443
protocol: tcp
mode: host
- target: 8080
published: 8080
protocol: tcp
mode: host
volumes:
- $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:/shared
environment:
- NETCUP_CUSTOMER_NUMBER=$NETCUP_CUSTOMER_NUMBER
- NETCUP_API_KEY=$NETCUP_API_KEY
- NETCUP_API_PASSWORD=$NETCUP_API_PASSWORD
labels:
- '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/rules
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"

Jetzt ist der Zeitpunkt gekommen alles zu testen.

cd ~/docker
docker-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.

Traefik Log
Traefik Log

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

Geänderte Docker-Compose Datei
Geänderte Docker-Compose Datei
Anschließend löschen wir den Inhalt der acme-Datei.

nano ~docker/traefik/acme/acme.json

In dieser Datei den kompletten Inhalt löschen (auch die Leerzeilen). Jetzt Traefik neu starten.

cd ~/docker
docker-compose down
docker-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.

Traefik Dashboard
Traefik Dashboard

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

Auskommentiertes Label
Auskommentiertes Label

Diesmal muss das Label “traefik.http.routers.traefik-rtr.tls.certresolver=dns-netcup” auskommentiert werden.

cd ~/docker
docker-compose down
docker-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 = 100
burst = 50
[http.middlewares.middlewares-secure-headers]
[http.middlewares.middlewares-secure-headers.headers]
accessControlAllowMethods= ["GET", "OPTIONS", "PUT"]
accessControlMaxAge = 100
hostsProxyHeaders = ["X-Forwarded-Host"]
sslRedirect = true
stsSeconds = 63072000
stsIncludeSubdomains = true
stsPreload = true
forceSTSHeader = true
customFrameOptionsValue = "allow-from https:dieeigenedomain.de"
contentTypeNosniff = true
browserXssFilter = true
referrerPolicy = "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:

  • Authentifizierung
  • Rate-Limit
  • Security-Headers

Um sie zu nutzen, können wir sie einzeln in die docker-compose Datei eintragen oder Ketten mit Kombinationen aus ihnen einrichten.

Eintrag ohne Kette
Eintrag ohne Kette

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:

Kette aus Middlewares in docker-compose
Kette aus Middlewares in docker-compose

Nachdem dieses „Etikett“ in der docker-compose Datei eingetragen ist, muss Traefik neu gestartet werden, damit die Änderungen in Kraft treten.

cd ~/docker
nano 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 achten
docker-compose down
docker-compose up -d

Jetzt im Dashboard nachsehen, ob die Middleware richtig geladen werden.

Dashboard Middleware
Dashboard Middleware

Und prüfen, ob die Kette als Middleware im HTTP-Router von Traefik geladen wird:

Middleware im HTTP Router
Middleware im HTTP Router

Google Authentifizierung

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.

Google Cloud Dashboard
Google Cloud Dashboard

Hier erstellen wir ein neues Projekt und benennen es so, dass wir es wiederfinden.

Neues Projekt
Neues Projekt

Zunächst muss ein Zustimmungsbildschirm erstellt werden.

Zustimmungsbildschirm
Zustimmungsbildschirm

Dahin gelangen wir über den Menüpunkt APIs & Dienste.

Externer Usertype
Externer Usertype

Ein Name muss angegeben werden, eine E-Mail-Adresse auch. Optional kann auch ein Logo hochgeladen werden.

Anwendungsinformation
Anwendungsinformation

Eine Domain muss autorisiert werden. Das Limit liegt bei 10.

Domainauthorisierung
Domainauthorisierung

Bei den Bereichen wählen wir die drei nicht kritischen Bereiche aus. E-Mail, Profile und OpenID.

Bereiche
Bereiche

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.

Testnutzer
Testnutzer

Über APIs & Dienste wählen wir Anmeldedaten.

Anmeldedaten
Anmeldedaten

Nun fügen wir eine OAuth-Client-ID hinzu.

OAuth-Client-ID
OAuth-Client-ID

Wir tragen eine autorisierte Weiterleitungsdomain ein.

Weiterleitungsdomain
Weiterleitungsdomain

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/.env
nano ~/docker/.env

Die Datei dürfte jetzt ungefähr so aussehen:

PUID=1000
PGID=114
TZ=Europe/Berlin
USERDIR=/home/jewgeni
DOCKERDIR=/home/jewgeni/docker
DOMAINNAME=irgendeinedomain.de
NETCUP_CUSTOMER_NUMBER=50000
NETCUP_API_KEY=lksjfrjgoigKJLKjlhir
NETCUP_API_PASSWORD=SlkjiojKLhiuuzvTFDENgdeu
EMAIL_ADDRESS=mail@gmail.com
GOOGLE_CLIENT_ID=uhrlkiaungopterktdsnf.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=lkiz537Jkhkr
GOOGLE_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 = true
authResponseHeaders = ["X-Forwarded-User"]
[http.middlewares.middlewares-rate-limit]
[http.middlewares.middlewares-rate-limit.rateLimit]
average = 100
burst = 50
[http.middlewares.middlewares-secure-headers]
[http.middlewares.middlewares-secure-headers.headers]
accessControlAllowMethods= ["GET", "OPTIONS", "PUT"]
accessControlMaxAge = 100
hostsProxyHeaders = ["X-Forwarded-Host"]
sslRedirect = true
stsSeconds = 63072000
stsIncludeSubdomains = true
stsPreload = true
forceSTSHeader = true
customFrameOptionsValue = "allow-from https:example.com"
contentTypeNosniff = true
browserXssFilter = true
referrerPolicy = "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: web
default:
driver: bridge
### SERVICES ###
services:
### TRAFIK - REVERSE PROXY ###
traefik:
container_name: traefik
image: traefik:latest
restart: unless-stopped
command:
- --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=10
networks:
- web
security_opt:
- no-new-privileges:true
ports:
- target: 80
published: 80
protocol: tcp
mode: host
- target: 443
published: 443
protocol: tcp
mode: host
- target: 8080
published: 8080
protocol: tcp
mode: host
volumes:
- $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:/shared
environment:
- NETCUP_CUSTOMER_NUMBER=$NETCUP_CUSTOMER_NUMBER
- NETCUP_API_KEY=$NETCUP_API_KEY
- NETCUP_API_PASSWORD=$NETCUP_API_PASSWORD
labels:
- '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: oauth
image: thomseddon/traefik-forward-auth:latest
restart: unless-stopped
networks:
- web
security_opt:
- no-new-privileges:true
environment:
- 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=2592000
labels:
- '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.

Webseite hosten

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 ~/docker
mkdir appdata
mkdir appdata/testseite
cd ~/docker/appdata/testseite
nano 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 ~/docker
nano docker-compose-testseite.yml
version: '3.7'
### NETWORKS ###
networks:
web:
external:
name: web
default:
driver: bridge
### SERVICE ###
services:
testseite:
container_name: testseite
image: nginx:latest
restart: unless-stopped
networks:
- web
security_opt:
- no-new-privileges:true
volumes:
- $DOCKERDIR/appdata/testseite:/usr/share/nginx/html:ro
labels:
- '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 ~/docker
mkdir appdata
mkdir appdata/testseite2
cd ~/docker/appdata/testseite2
nano index.html
<!DOCTYPE html>
<html>
<head>
<title>Testseite</title>
</head>
<body>
<p>Hallo</p>
<p>Das ist die zweite Seite.</p>
</body>
</html>
cd ~/docker
nano docker-compose-testseite2.yml
version: '3.7'
### NETWORKS ###
networks:
web:
external:
name: web
default:
driver: bridge
### SERVICE ###
services:
testseite2:
container_name: testseite2
image: nginx:latest
restart: unless-stopped
networks:
- web
security_opt:
- no-new-privileges:true
volumes:
- $DOCKERDIR/appdata/testseite2:/usr/share/nginx/html:ro
labels:
- '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 -d
lazydocker

In Lazydocker sollten jetzt beide Dienste zu sehen sein.

Zwei Nginx Container
Zwei Nginx Container

Ü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 ~/docker
docker-compose -f docker-compose-testseite.yml down
docker-compose -f docker-compose-testseite2.yml down

Die Einrichtung ist damit fertig 😃


Tags

#anleitung#docker#linux#oauth#server#traefik#ubuntu#vps
Nächster Blogpost
Plausible Analytics hinter Traefik installieren

Kategorien

Aktuelles
Excel
Finanzen
Gaming
Gatsby
Git
Google
Homeserver
Server
Tailwind
Werbung

Inhaltsverzeichnis

1
Server installieren
2
Einrichtung einer Domain
3
Einrichtung von Traefik
4
Google Authentifizierung
5
Webseite hosten

Related Posts

Proxmox Installation
March 25, 2022
2 min

Links

KontaktÜber

Social Media