Traefik Reverse Proxy – HTTPS Webservices selber hosten

26. April 2021

Um Web-Services, die bei uns zu Hause laufen, aus dem Internet zu erreichen gibt es viele Möglichkeiten.
Die schlechteste Möglichkeit wäre die Portfreigabe und Weiterleitung auf ein Gerät mit unverschlüsseltem Protokoll wie beispielsweise das HTTP oder FTP. Versuche es unbedingt zu vermeinden eine einfache Portfreigabe auf HTTP oder FTP zu nutzen!
Besser wäre da schon das SFTP und bzw. oder ein VPN. Beide verlangen Zertifikate um Clients zu autorisieren und die Daten werden anschließend verschlüsselt übertragen.
Bei vielen Routern und Endgeräten ist die Einrichtung jedoch aufwendig und beispielsweise ist das Hauseigene VPN einer FritzBox, welches auf IPSec setzt, ist nicht besonders modern oder schnell.
Abhilfe schafft da PiVPN, womit sich einfach WireGuard oder OpenVPN installieren lässt.
Dazu habe ich bereits ein Video Tutorial erstellt wie dies mit dem ei23 Smart Home Server funktioniert.

Ein VPN löst nicht alle Probleme
Was ist aber wenn unbestimmt viele Clients auf unser System zugreifen sollen, oder wir nicht jedem Nutzer ein Zertifikat zur Verfügung stellen möchten?
Oder was ist wenn wir einen Service betreiben, der Zeitweise mehr Ressourcen benötigt, als beispielsweise ein kleiner Raspberry Pi zur Verfügung stellen kann?
Hier kommen Reverse Proxies und SSL Zertifikate ins Spiel.
Mit einem Reverse Proxy kann beispielsweise eine Domain oder Subdomain aufgerufen werden und der Reverse-Proxy entscheidet dann welcher Inhalt dem Nutzer zugänglich gemacht wird. Zusätzlich kann der Inhalt über SSL verschlüsselt werden.
So ist es beispielsweise möglich eine IP hinter mehreren Domains zu verstecken und trotzdem mehrere Services aufzurufen.

Traefik Architecture

Traefik Architecture (Quelle: traefik.io)

Es gibt natürlich auch andere Lösungen, wie z.B. Nginx. Da der ei23 Smart Home Server jedoch viele Programme als Docker-Container betreibt, nutzen wir Traefik, welches für Programme im Docker nochmal einige Vorteile bringt und zu dem eine gutes visuelles Interface hat.

Proxy Konfiguration von Docker-Containern

Um einen Docker Container für Traefik zu konfigurieren ist es lediglich notwendig die /home/pi/ei23-dockerdocker-compose.yaml anzupassen.
Wie das geht, zeige ich im Video zu Version 1 vom Smart Home Skript

Beispiel docker-compose Bitwarden

  bitwarden:
    image: bitwardenrs/server:latest
    container_name: bitwarden
    restart: unless-stopped
    # ports:*
        # - 2223:80
    labels:
        - traefik.enable=true
        - traefik.http.routers.bitwarden.rule=Host(`example.com`)
        - traefik.http.routers.bitwarden.entrypoints=web-secured
        - traefik.http.routers.bitwarden.tls=true
        - traefik.http.routers.bitwarden.tls.certresolver=letsEncrypt
    volumes:
        - ./volumes/bitwarden:/data

Mit Traefik ist es nicht zwingend notwendig die *Ports über Docker freizugeben, so lange Traefik sich im gleichen Dockernetzwerk wie die Container befindet. Daher sind diese im Beispiel oben mit # auskommentiert.

Beispiel: Webseite im LAN (in diesem Fall das ei23 Dashboard)

labels:
    - traefik.enable=true
    - traefik.http.routers.ei23-lan.rule=(Host(`192.168.178.10`) || Host(`smarthome`))
    - traefik.http.routers.ei23-lan.priority=1
    - traefik.http.routers.ei23-lan.entrypoints=lan

Beispiel docker-compose Grafana

labels:
    - traefik.enable=true
    - traefik.http.routers.grafana.rule=Host(`grafana.example.com`)
    - traefik.http.services.grafana.loadbalancer.server.port=3000
    - traefik.http.routers.grafana.entrypoints=web-secured
    - traefik.http.routers.grafana.tls=true
    - traefik.http.routers.grafana.tls.certresolver=letsEncrypt

Beispiel docker-compose Nextcloud

labels:
    - traefik.enable=true
    - traefik.http.routers.nextcloud.middlewares=nextcloud,nextcloud_redirect
    - traefik.http.routers.nextcloud.tls.certresolver=letsEncrypt
    - traefik.http.routers.nextcloud.rule=Host(`nextcloud.example.com`)
    - traefik.http.routers.nextcloud.entrypoints=web, web-secured
    - traefik.http.routers.nextcloud.tls=true
    - traefik.http.middlewares.nextcloud.headers.stsSeconds=15552000
    - traefik.http.middlewares.nextcloud.headers.stsPreload=true
    - traefik.http.middlewares.nextcloud_redirect.redirectregex.permanent=true
    - traefik.http.middlewares.nextcloud_redirect.redirectregex.regex=^https://(.*)/.well-known/(card|cal)dav
    - traefik.http.middlewares.nextcloud_redirect.redirectregex.replacement=https://$${1}/remote.php/dav/

Wie man erkennen kann, gibt es für Traefik „Router, Services, und Middlewares“. Jeder Dienst benöntigt mindestens einen eigenen Traefik-Router.

Proxy Konfiguration von LoadBalancern / externen (IP) Adressen und weiteren Geräten im Netzwerk

In /home/pi/ei23-docker/volumes/traefik/traefik/dynamic/config.yml ist beispielsweise ein externer „Loadbalancer“ für Home Assistant erstellt, da Home Assistant nicht innerhalb des Dockernetzwerk sondern im Hostnetzwerk läuft. (Achtung! Die Zeileneinrückung muss stimmen – der Yaml Parser will das so)

http:
  routers:
    home-assistant:
      rule: Host(`homeassistant.example.com`)
      service: home-assistant
      tls:
        certresolver: letsEncrypt
        
  services:
    home-assistant:
      loadBalancer:
        servers:
          - url: http://172.17.0.1:8123 # 172.17.0.1 ist das Standard Docker Gateway

Dieser verweist statt auf einen Docker Container auf eine http Url. Diese wird über den „certresolver“ anschließend auch über SSL verschlüsselt.

Inbetriebnahme von Traefik / SSL Zertifikate erstellen

Traefik selbst wird als Docker Container betrieben und in der aktuellen Version des ei23 Smart Home Servers ist Traefik bereits vorkonfiguriert, es müssen nur noch kleine Änderungen vorgenommen werden.

Docker-Compose für Traefik

traefik:
    image: traefik:v2.4
    container_name: traefik
    ports:
      - "80:80" # as internal http
      - "591:591" # as external http
      - "2280:8080" # config port
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./volumes/traefik/traefik/:/etc/traefik/
      - ./volumes/traefik/letsencrypt:/letsencrypt
    restart: unless-stopped

Unter /home/pi/ei23-docker/volumes/traefik/traefik/ befindet sich die traefik.yaml

[...]
entryPoints:
  lan:
    address: :80
  web:
    address: :591
    http:
      redirections:
        entrypoint:
          to: web-secured
          scheme: https
  web-secured:
    address: :443

certificatesResolvers:
  letsEncrypt:
    acme:
      email: mail@example.com
      storage: /letsencrypt/acme.json
      caserver: https://acme-staging-v02.api.letsencrypt.org/directory # Dies ist der caserver zum Testen
      # caserver: https://acme-v02.api.letsencrypt.org/directory
      httpChallenge:
        entryPoint: web
[...]

Hier sind drei Eingangspunkte, die so genannten Entrypoints, definiert.

  • Port 80 (lan) ist für HTTP im Heimnetz (LAN) reserviert
  • Port 591 (web) ist für HTTP nach Außen (WAN) reserviert – Der externe Port 80 muss auf den internen Port 591 des Raspberry Pi weitergeleitet werden. Da wir ausschließlich verschlüsselte Verbindungen nutzen wollen, erzwingen wir mit Traefik eine Weiterleitung auf den web-secured Entrypoint (443).
  • Port 443 (web-secured) ist schließlich der Port für HTTPS. Dieser muss nach außen am Router freigegeben und eine Weiterleitung zum Raspberry Pi auf der Port 443 eingerichtet werden.

Die Verschlüsselung wird realisiert mit SSL.
Nach Ausführen von Docker Compose (ei23 dc) und einem Neustart von Traefik werden die Labels von Traefik eingelesen und die Zertifikate erstellt.
Bevor aber ein Zertifikat erstellt werden kann, sollte eine Email Adresse hinterlegt werden. Darüber wird man ggf. über den Ablauf eines Zertifikates, oder andere Warnungen bezüglich der Sicherheit informiert.

Der Dienst Let’s Encrypt ist so freundlich uns die sonst sehr teuren Zertifikate kostenlos zur Verfügung zu stellen. Allerdings gibt es ein Tages- und Wochenlimit, so lange wir nur testen ob alles klappt, sollten wir den “staging” caserver nutzen, hier wird kein offizielles Zertifikat ausgestellt.

Ein guter Webbrowser gibt beim Aufruf einer Adresse eine Warnung aus, wenn das Zertifikat nicht sicher bzw. echt ist.
Wenn man diese Warnung akzeptiert und alle gewünschten Dienste dennoch erreichbar sind, dann kann man vom staging caserver zum offiziellen wechseln.
Anschließend werden offizielle Zertifikate erstellt und die Webdienste sind nach kurzer Zeit offiziell verschlüsselt erreichbar.

Wenn Traefik und die Portweiterleitungen gemäß dieser Anleitung richtig konfiguriert worden ist, dann tauchen in der Datei /home/pi/ei23-docker/volumes/traefik/letsencrypt/acme.json
nach und nach die Zeilen mit den Schlüsseln zu deinen Verschiedenen Domains / Subdomains auf
Das sieht dann in etwa so aus:

[...]
   "Certificates": [
      {
        "domain": {
          "main": "nextcloud.example.com"
        },
        "certificate":
[...]

Diese Basis Konfiguration sollte für den Heimserver bereits mehr als genug Funktionalität bieten, wer dennoch weiter in die Materie einsteigen will:
Traefik hat eine sehr gute Dokumentation:
doc.traefik.io/traefik


7 Antworten zu “Traefik Reverse Proxy – HTTPS Webservices selber hosten”

  1. Andreas Meier sagt:

    Vielen Dank für die Anleitung – jetzt heißt es probieren….. der Abend ist gesichert 😉

  2. Hans sagt:

    Danke für die Anleitung und vor allem das tolle Skript =) leider bekomme ich meine Nextcloud nicht mit meiner Domain über den Traefik Reverse Proxy zum laufen 🙁 Gibt es eventuell ein neues Tutorial? Bei YouTube finde ich nur das alte für NextcloudPi. Sehr cooles Projekt!!! weiter so und viele Grüße

    • Felix sagt:

      Gerne!
      Ist richtig, ein Tutorial gibt es nur für NextcloudPi, das für das offizielle Nextcloud steht noch aus.
      Es fehlt aber nicht viel:
      In den docker compose templates ist die config für traefik ja schon drin und dann fehlen nur noch ein paar Zeilen in der /home/pi/ei23-docker/volumes/nextcloud/html/config/config.php

        array (
          0 => 'example.com',
        ),
        'trusted_proxies' => 
        array (
          0 => '172.18.0.0/16',
        ),
        'overwrite.cli.url' => 'https://example.com',
        'overwritehost' => 'example.com',
        'overwriteprotocol' => 'https',
      
  3. Hans sagt:

    Vielen Dank für die schnelle Antwort. So wirklich will es bei mir noch nicht funktionieren >.
    array (
    0 => ‚IP:8080‘,
    1 => ‚MeineDomein.de‘,
    ),
    erweiter werden,
    ‚overwrite.cli.url‘ => ‚https://MeineDomein.de‘, wird abgeändert
    ‚overwritehost‘ => ‚MeineDomein.de‘,
    ‚overwriteprotocol‘ => ‚https‘,
    und die beiden Zeilen hinzugefügt oder hab ich da was falsch verstanden?
    Traefik als revers Proxy habe ich glaube ich soweit zu laufen bekommen. Also Nextcloud wird mir dort angezeigt und das sieht für mich soweit gut aus 😛

    • Felix sagt:

      Sieht alles richtig aus.
      Beachte, dass du die Domain nur für eine Seite nutzen kannst. Für andere Seiten bräuchtest du dann subdomains wie grafana.meinedomain.de.
      Ggf. musst du prüfen, ob Subdomains / Wildcard-domains bei deinem dyndns Anbieter funktionieren.
      Wenn die docker-compose.yml auch passt, sollte es laufen.
      Veileicht einfach nochmal „docker restart nextcloud“ um nextcloud neu zu starten…

  4. Hans sagt:

    Wahnsinn wie schnell du antwortest =)
    Also wenn ich bei Treafik die Log Datei angucke gibt er mit diesen Fehler aus:
    time=“2021-05-27T16:57:45Z“ level=error msg=“Unable to obtain ACME certificate for domains \“MeineDomain.de\“: unable to generate a certificate for the domains [MeineDomain.de]: error: one or more domains had a problem:\n[MeineDomain.de] acme: error: 400 :: urn:ietf:params:acme:error:connection :: Fetching http://MeineDomain.de/.well-known/acme-challenge/DERSCHLüSSEL-g: Error getting validation data\n“ routerName=nextcloud@docker rule=“Host(`MeineDomain.de`)“ providerName=letsEncrypt.acme
    Nach etwas Google finde ich den Hinweis, dass letsEncrpt den Port 80 benötigt, um ein gültiges Zertifikat zu erstellen und deine Konfiguration Port 80 nur für den Internen Verkehr freigibt. Ist das Korrekt? Somit bekomme ich auch kein gültiges Zertifikat in der acme.json :/
    Wie bekomme ich das hin, dass letsEncrpt ein Zertifikat über den Port 80 erstellen kann? Ich Danke dir für deine Zeit =)

    • Felix sagt:

      Ok, das mit dem Port wäre auch mein nächster Hinweis gewesen.
      Das ist allerdings auch bereits grob im Artikel beschrieben:
      Am Router musst du Port 80 (extern) offenlegen und diesen an den Port 591 an den Pi weiterleiten.
      Port 80 wird intern bereits für das Dashboard genutzt, 591 ist ein alternativer http port.

      Die Erstellung des Zertifikates etwa läuft wie folgt:
      Traefik auf deinem Raspberry sagt zu Letsencrpyt: „Hey, ich bin meinedomain.de und will ein Zertifikat!“
      Letsencrypt geht auf http://meinedomain.de (also port 80) und guckt ob es Traefik dort erreichen kann und prüft so, ob traefik mit der domain auch nicht gelogen hat.
      Technisch passiert da noch etwas mehr, aber darum muss Port 80(internet) -> raspberry:591 (lan)