Webserver mit Traefik als Proxyserver und Letsencrypt-Zertifikat

Auf der Seite  https://goneuland.de/traefik-v2-3-reverse-proxy-mit-crowdsec-im-stack-einrichten bin ich auf eine Anleitung zum Thema Letsencrypt-Zertifikate für einen eigenen Webserver unter Ubuntu gekommen.

Hierzu wurde Traefik als Docker-Container verwendet. Dabei wird zur Sperrung von unwerwünschten IP-Adressen auf den Datenbestand von https://www.crowdsec.net/ zugegriffen. Wenn die aktuelle zugreifende IP darin enthalten ist, wird die Abfrage nicht von Traefik an den Webserver weitergeleitet.

Informationen zu Traefik sind auf der offiziellen Projektseite https://traefik.io/traefik zu finden.
Am Ende dieses Beitrages finden Sie Downloads mit Textdateien mit den zusammgefassten Befehlen.
Zum besseren Verständnis würde ich Ihnen für die erste Einrichtung aber empfehlen, die Schritte manuell einzeln wie unten ausgeführt durchzuführen.

Nachfolgend die Beschreibung meiner Vorgehensweise auf einem Raspberry Pi 4:

Voraussetzungen

  • Domain die beim Aufruf auf unseren Proxy weiterleitet
    Wir benötigen eine Domain, die auf unseren Router per DynDNS weitergeleitet wird.
    Ich bekomme von meinem Provider eine feste IP-Adresse zugewiesen und konnte bei www.netbeat.de meinen Domainnamen direkt dieser IP-Adresse zuweisen.

    Falls Sie keine feste IP-Adresse erhalten, müssen Sie mit der Methode DynDNS arbeiten. Ein kostenloser Weg wäre folgende Vorgehensweise:

    Ich habe hierzu auf den kostenlosen Dienst von IPv64.net zugegriffen und mir auf der Seite 
    https://ipv64.net/account.php die Domäne demo.ipv64.net  angelegt.

    Wie man den Router DNS-fähig bekommt, erhält man auf der Seite https://ipv64.net/dyndns_helper gleich zur soeben erstellten Domain angezeigt.

  • Rechner auf dem ein Linux als Betriebssystem läuft
    Ich habe hierzu einen Rasperry Pi 4 verwendet. Das passende Betriebssystem kann auf der Seite https://www.raspberrypi.com/software/operating-systems/ heruntergeladen werden. Bitte beachten, dass derzeit von Docker nur die 32-Bit-Variante unterstützt wird. Es genügt, die OS-Light-Version. Da ich mich manchmal auch direkt an meinem Raspi 4 anmelde um ihn als PC-Ersatz zu verwenden, läuft bei mir die Desktop-Variante.

  • Dockerinstallation auf dem Raspberry
    Zur Installation von Docker habe ich die Anleitung auf der Seite  
    https://docs.docker.com/engine/install/raspberry-pi-os/#install-using-the-repository verwendet und in der Kommandozeile folgende Befehle ausgeführt:
    (Das für Version 1 benötigte docker-compose habe ich weggelassen, da es veraltet ist.
    Bei Bedarf kann es mit sudo apt install docker-compose-plugin installiert werden.)


Ablauf der Installation von Docker

Das offizielle Docker-Repository zu den Paketquellen hinzufügen
(die mit dem #-Zeichen markierten Zeilen stellen nur Kommentare dar und müssen nicht in der Konsole eingegeben werden !)

# Inhalt von docker_install.txt

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/raspbian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Set up Docker's APT repository:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/raspbian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin

Damit Docker auch ohne root-Rechte gestartet werden kann, müssen wir dem aktuellen Benutzer der Gruppe docker zuweisen.

sudo usermod -aG docker $USER

falls die Gruppe docker noch nicht vorhanden ist, kann diese mit folgendem Aufruf erzeugt werden:

sudo groupadd docker

Damit die Zuordnung wirksam wird, muss der aktuelle Benutzer einmal ab- und wieder angemeldet werden. Auch ein reboot bewirkt den gleichen Effekt.

Anschließend sollten wir uns problemlos die installierte Docker-Version anzeigen können:

docker version

Einrichtung des Linux-Kommandos „tree“

Dieser Befehl ist zum Ablauf von Docker nicht notwendig. Er dient nur dem Zweck, die Verzeichnisse übersichtlich darzustellen damit Sie Ihren Installationsstand mit meiner Anleitung vergleichen können

sudo apt install tree

Anlegen der Verzeichnisstruktur

Ich möchte die Installation auf dem Raspi in das Verzeichnis /srv/web legen.

Nun melden wir uns mittels ssh auf dem Raspberry Pi an und führen folgende Befehle auf der Eingabezeile durch:

# Hauptverzeichnis erstellen und zusätzliche Unterordner in 'crowdsec' erstellen
#Mein Benutzername lautet pi - ggf. mmüssen Sie natürlich Ihren abweichenden Benutzernamen verwenden

sudo mkdir /srv

sudo chown pi:root /srv

mkdir -p /srv/web/{traefik,html/demo,var/log,php,crowdsec/{config,data},config}

# .env Datei im Hauptverzeichnis erstellen
touch /srv/web/.env

# Umgebungsspezifische .env Dateien in 'config' erstellen
touch /srv/web/config/{crowdsec.env,traefik.env,traefik-crowdsec-bouncer.env}

# Zusätzliche Dateien in 'traefik' erstellen und Zugriffsrechte für bestimmte Dateien festlegen
touch /srv/web/traefik/{acme_letsencrypt.json,traefik.yml,dynamic_conf.yml,tls_letsencrypt.json}

#Zugriffsrechte aus json-Datei passend setzen, damit die Letsencrypt-Zertifikate erstellt werden
sudo chmod 600 /srv/web/traefik/{acme_letsencrypt.json,tls_letsencrypt.json}

#Verzeichnisstruktur anzeigen lassen und prüfen
tree -L 2 -a /srv/web/

Das Ergebnis sollte folgende Struktur ergeben:

Verzeichnisstruktur /srv

Exkurs: Minimalversion von Traefik

Um Traefik als Reversproxy einzurichten genügt für eine Minimalversion eine docker-compose.yaml mit folgendem Inhalt:

services:
  traefik:
    image: "traefik:v3.0"
    container_name: "traefik"
    command:
      #- "--log.level=DEBUG"
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
      - "--certificatesresolvers.myresolver.acme.email=test@gmx.de"
      #Um Sperre duch Fehlversuche zu vermeiden, anfangs den Testserver von LetsEncrypt eintragen
      #- "--certificatesresolvers.myresolver.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
    expose:
      # traefik dashboard port
      - 8080
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.rule=Host(`traefik.demo.ipv64.net`)"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.tls.certresolver=myresolver"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.routers.traefik.middlewares=traefik-auth"
      - "traefik.http.middlewares.traefik-auth.basicauth.users=admin:$$apr1$$fMgwWnWq$$9lSqU1k0nWhLE0NF.eFIS0"
      # Erzeugung Kennwort mit echo $(htpasswd -nb admin 'traefik!admin!2024')
      # vorher das Paket apache2-util installieren: apt update && apt install apache2-utils

    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt

    restart: "always"
networks:
  default:
    name: webproxy

Beachten Sie den Hinweis zur Verwendung des Testservers von Letsencrypt. Nachdem der Bezug vom Testserver funktioniert, können Sie auf den Echt-Server umschalten. Dieser sperrt nämlich aus Sicherheitsgründen die Kommunikation nach einer bestimmten Anzahl von Fehlversuchen für eine gewissen Zeitspanne. Zum Test als Zeile 17 aktivieren und Zeile 18 auskommentieren.

Die Angabe zur Domain und E-Mail-Adresse sowie die Zugangsdaten bitte auf Ihre Verhältnisse anpassen.
Nach dem Start mittels docker compose up dauert es ein paar Minuten, bis im Unterverzeichnis letsencrypt die json-Datei gefüllt wird.

Nun kann im Browser durch Aufruf der angegeben Domain das Traefik-Dashboard aufgerufen werden und die Übertragung wird mittels HTTPS-Verschlüsselung gesichert.

Nun kommen wir aber zur etwas ausführlicheren Variante:

Anlegen der Konfigurationsdateien

Variablen zur Verwendung in der docker-compose.yaml
Hier werden die einzelnen Container und Services benannt
nano /srv/web/.env

# Service Crowdsec
SERVICES_CROWDSEC_CONTAINER_NAME=crowdsec
SERVICES_CROWDSEC_HOSTNAME=crowdsec
SERVICES_CROWDSEC_IMAGE=crowdsecurity/crowdsec
SERVICES_CROWDSEC_IMAGE_VERSION=latest
SERVICES_CROWDSEC_NETWORKS_CROWDSEC_IPV4=172.31.254.254

# Service Traefik
SERVICES_TRAEFIK_CONTAINER_NAME=traefik
SERVICES_TRAEFIK_HOSTNAME=traefik
SERVICES_TRAEFIK_IMAGE=traefik
#SERVICES_TRAEFIK_IMAGE_VERSION=2.10
SERVICES_TRAEFIK_IMAGE_VERSION=v3.0
SERVICES_TRAEFIK_LABELS_TRAEFIK_HOST=`demo.ipv64.net`
SERVICES_TRAEFIK_NETWORKS_CROWDSEC_IPV4=172.31.254.253
SERVICES_TRAEFIK_NETWORKS_PROXY_IPV4=172.30.255.254

# Service Traefik Crowdsec Bouncer
SERVICES_TRAEFIK_CROWDSEC_BOUNCER_CONTAINER_NAME=traefik_crowdsec_bouncer
SERVICES_TRAEFIK_CROWDSEC_BOUNCER_HOSTNAME=traefik-crowdsec-bouncer
SERVICES_TRAEFIK_CROWDSEC_BOUNCER_IMAGE=fbonalair/traefik-crowdsec-bouncer
SERVICES_TRAEFIK_CROWDSEC_BOUNCER_IMAGE_VERSION=latest
SERVICES_TRAEFIK_CROWDSEC_BOUNCER_NETWORKS_CROWDSEC_IPV4=172.31.254.252

# Netzwerkeinstellungen
NETWORKS_PROXY_NAME=proxy
NETWORKS_PROXY_SUBNET_IPV4=172.30.0.0/16
NETWORKS_CROWDSEC_NAME=crowdsec
NETWORKS_CROWDSEC_SUBNET_IPV4=172.31.0.0/16

# Service ip64.net
SERVICES_IPV64_CONTAINER_NAME= ip64
SERVICES_IPV64_HOSTNAME=demo-ipv64-net
SERVICES_IPV64_DOMAIN_DEMO="`demo.ipv64.net`"
SERVICES_IPV64_NETWORKS_CROWDSEC_IPV4=172.31.254.251

Definiton der Dienste, Netzwerke und Verzeichnisse
nano /srv/web/docker-compose.yaml


# Die Version der Docker Compose-Datei. Hier verwenden wir Version 3.9.
version: "3.9"
# Der Beginn des 'services' Abschnitts.
services:
  # Name des Services.
  crowdsec:
    # Der Name des Containers, der aus diesem Service erzeugt wird
    container_name: ${SERVICES_CROWDSEC_CONTAINER_NAME}
    # Umgebungsvariablen für den Container.
    env_file: ./config/crowdsec.env
    # Hostname des CrowdSec-Containers, kann über eine Umgebungsvariable angepasst werden.
    hostname: ${SERVICES_CROWDSEC_HOSTNAME}
    # Gesundheitsüberprüfung für den CrowdSec-Service
    healthcheck:
      test: ["CMD", "cscli", "version"]
      interval: 10s
      timeout: 2s
      retries: 5
      start_period: 10s
    # Docker-Image, das für den Container verwendet wird.
    image: ${SERVICES_CROWDSEC_IMAGE}:${SERVICES_CROWDSEC_IMAGE_VERSION}
    # Netzwerke, zu denen der Container gehört.
    networks:
      crowdsec:
        # Feste IPv4-Adresse vergeben.
        ipv4_address: ${SERVICES_CROWDSEC_NETWORKS_CROWDSEC_IPV4}
    # Restart-Strategie für den Container.
    restart: unless-stopped
    # Sicherheitsoptionen für den Container.
    security_opt:
      - no-new-privileges=true
    # Volumes, die vom Container verwendet werden.
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /var/log/auth.log:/var/log/auth.log:ro
      - ./var/log/traefik:/var/log/traefik:ro
      - ./crowdsec/config:/etc/crowdsec
      - ./crowdsec/data:/var/lib/crowdsec/data
      - ./var/log/crowdsec/:/var/log/
      - ./var/log/ip64net/:/var/log/ip64net

  # Der Name des Service
  traefik:
    # Der Name des Containers, der aus diesem Service erzeugt wird
    container_name: ${SERVICES_TRAEFIK_CONTAINER_NAME}
    # Diese Option sorgt dafür, dass der Traefik-Service erst gestartet wird,
    # nachdem der Crowdsec-Service healthy ist
    depends_on:
      crowdsec:
        condition: service_healthy
    # Umgebungsvariablen für den Container.
    env_file: ./config/traefik.env
    # Hostname des Traefik-Containers, kann über eine Umgebungsvariable angepasst werden.
    hostname: ${SERVICES_TRAEFIK_HOSTNAME}
    # Gesundheitsüberprüfung für den Traefik-Service
    healthcheck:
      test: ["CMD", "traefik", "healthcheck", "--ping"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 10s
    # Das Docker-Image, das für diesen Service verwendet wird
    # Version kann über eine Umgebungsvariable angepasst werden.
    image: ${SERVICES_TRAEFIK_IMAGE}:${SERVICES_TRAEFIK_IMAGE_VERSION}
    # Docker Labels für den Traefik-Service. Diese werden für die Traefik-Konfiguration verwendet
    labels:
      traefik.docker.network: proxy
      traefik.enable: "true"
      traefik.http.routers.traefik.entrypoints: websecure
      traefik.http.routers.traefik.middlewares: default@file,traefikAuth@file
      traefik.http.routers.traefik.rule: Host(${SERVICES_TRAEFIK_LABELS_TRAEFIK_HOST})
      traefik.http.routers.traefik.service: api@internal
      traefik.http.routers.traefik.tls: "true"
      traefik.http.routers.traefik.tls.certresolver: http_resolver
      traefik.http.services.traefik.loadbalancer.sticky.cookie.httpOnly: "true"
      traefik.http.services.traefik.loadbalancer.sticky.cookie.secure: "true"
      traefik.http.routers.pingweb.rule: PathPrefix(`/ping`)
      traefik.http.routers.pingweb.service: ping@internal
      traefik.http.routers.pingweb.entrypoints: websecure
    # Die Netzwerke, zu denen dieser Service gehört
    networks:
      crowdsec:
        # IPv4-Adresse des CrowdSec Containers im Traefik-Netwerk: crowdsec
        ipv4_address: ${SERVICES_TRAEFIK_NETWORKS_CROWDSEC_IPV4}
      proxy:
        # IPv4-Adresse des Traefik Containers im Traefik-Netwerk: proxy
        ipv4_address: ${SERVICES_TRAEFIK_NETWORKS_PROXY_IPV4}
    # Die Ports, die diesem Service zugeordnet sind
    ports:
      - "80:80"  # HTTP
      - "443:443"  # HTTPS
    # Der Restart-Policy dieses Service
    restart: unless-stopped
    # Sicherheitsoptionen für diesen Service
    security_opt:
      - no-new-privileges:true
    # Die Volumes, die diesem Service zugeordnet sind
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./var/log/traefik/:/var/log/traefik/
      - ./traefik/traefik.yml:/traefik.yml:ro
      - ./traefik/acme_letsencrypt.json:/acme_letsencrypt.json
      - ./traefik/tls_letsencrypt.json:/tls_letsencrypt.json
      - ./traefik/dynamic_conf.yml:/dynamic_conf.yml

  # Definition des Traefik CrowdSec Bouncer-Dienstes
  traefik_crowdsec_bouncer:
    # Der Name des Containers, der aus diesem Service erzeugt wird
    container_name: ${SERVICES_TRAEFIK_CROWDSEC_BOUNCER_CONTAINER_NAME}
    # Abhängigkeitsdefinition: Dieser Service wird erst gestartet, wenn der Crowdsec-Service als "healthy" gekennzeichnet ist
    depends_on:
      crowdsec:
        condition: service_healthy
    # Pfad zur .env-Datei für den Traefik CrowdSec Bouncer-Dienst
    env_file: ./config/traefik-crowdsec-bouncer.env
    # Der Hostname des Containers, kann über eine Umgebungsvariable angepasst werden
    hostname: ${SERVICES_TRAEFIK_CROWDSEC_BOUNCER_HOSTNAME}
    # Docker-Image, das für den Container verwendet wird. Die Version kann über eine Umgebungsvariable angepasst werden
    image: ${SERVICES_TRAEFIK_CROWDSEC_BOUNCER_IMAGE}:${SERVICES_TRAEFIK_CROWDSEC_BOUNCER_IMAGE_VERSION}
    # Netzwerke, zu denen der Container gehört
    networks:
      crowdsec:
        # Feste IPv4-Adresse für den Container in diesem Netzwerk
        ipv4_address: ${SERVICES_TRAEFIK_CROWDSEC_BOUNCER_NETWORKS_CROWDSEC_IPV4}
    # Restart-Strategie für den Container. 'unless-stopped' bedeutet, dass der Container immer neu gestartet wird, es sei denn, er wird manuell gestoppt
    restart: unless-stopped

    volumes:
    - ./var/log/crowdsec_bouncer:/var/log

  ipv64:
    build:
      dockerfile: dockerfile_php8
      context: php
    hostname: ${SERVICES_IPV64_HOSTNAME}

    container_name: ${SERVICES_IPV64_CONTAINER_NAME}
    restart: unless-stopped

    depends_on:
      traefik:
        condition: service_healthy
    networks:
      crowdsec:
        # Feste IPv4-Adresse für den Container in diesem Netzwerk
        ipv4_address: ${SERVICES_IPV64_NETWORKS_CROWDSEC_IPV4}

    labels:
      traefik.enable: "true"
      traefik.docker.network: $NETWORKS_CROWDSEC_NAME
      traefik.http.routers.ipv64.entrypoints: websecure
      traefik.http.routers.ipv64.rule: "HOST($SERVICES_IPV64_DOMAIN_DEMO)"

      traefik.http.routers.ipv64.tls.certresolver: http_resolver
      traefik.http.routers.ipv64.tls: "true"

    volumes:
      - ./html/demo:/var/www/html
      - ./var/log/ip64net:/var/log                                                 #log-files

# Definition der Netzwerke, die von den Services verwendet werden
networks:
  # Definition des 'proxy' Netzwerks
  proxy:
    # Der Name des Netzwerks, kann über eine Umgebungsvariable angepasst werden
    name: ${NETWORKS_PROXY_NAME}
    # Der Treiber, der für das Netzwerk verwendet wird, hier ist es 'bridge'
    driver: bridge
    # IP-Adress-Management-Konfiguration (IPAM)
    ipam:
      # Konfiguration des IP-Adress-Subnetzes für das Netzwerk
      config:
      - subnet: ${NETWORKS_PROXY_SUBNET_IPV4}
    # Wenn 'attachable' auf 'true' gesetzt ist, können Standalone-Container an dieses Netzwerk angehängt werden
    attachable: true
  # Definition des 'crowdsec' Netzwerks
  crowdsec:
    # Der Name des Netzwerks, kann über eine Umgebungsvariable angepasst werden
    name: ${NETWORKS_CROWDSEC_NAME}
    # Der Treiber, der für das Netzwerk verwendet wird, hier ist es 'bridge'
    driver: bridge
    # IP-Adress-Management-Konfiguration (IPAM)
    ipam:
      # Konfiguration des IP-Adress-Subnetzes für das Netzwerk
      config:
      - subnet: ${NETWORKS_CROWDSEC_SUBNET_IPV4}
    # Wenn 'attachable' auf 'true' gesetzt ist, können Standalone-Container an dieses Netzwerk angehängt werden
    attachable: true

Konfiguration des Containers crowdsec
nano /srv/web/config/crowdsec.env

PGID="1000"
COLLECTIONS="crowdsecurity/traefik crowdsecurity/http-cve crowdsecurity/whitelist-good-actors crowdsecurity/apache2"

Konfiguration des Containers crowdsec-bouncer
sudo nano /srv/web/config/traefik-crowdsec-bouncer.env

# Access-Token damit Bouncer und CrowdSec kommunizieren können
CROWDSEC_BOUNCER_API_KEY=lXIL3TtsLzypyp3Vdpclszh7iVj4KjkZGUlKhFAhR4E

# Hostname mit richtigem Port von CrowdSec
#CROWDSEC_AGENT_HOST=${SERVICES_CROWDSEC_HOSTNAME}:8080

# Bei Debian funktioniert ${SERVICES_CROWDSEC_HOSTNAME} nicht
# In Debian müssen wir ${SERVICES_CROWDSEC_HOSTNAME} durch einen Wert ersetzen:
CROWDSEC_AGENT_HOST=crowdsec:8080
# alternativ:
# CROWDSEC_AGENT_HOST=172.31.254.254:8080

Leere Datei erzeugen, welche für die Erzeugung des Containers benötigt wird

touch /srv/werb/config/traefik.env

Definition der Logdateien, deren Ereignisse mit Crowdsec ausgewertet werden
nano /srv/web/crowdsec/config/acquis.yaml

 #filenames:
#  - /var/log/nginx/*.log
#  - ./tests/nginx/nginx.log
#this is not a syslog log, indicate which kind of logs it is
#labels:
#  type: nginx
#----
filenames:
 - /var/log/auth.log
 - /var/log/syslog
labels:
  type: syslog
---
filenames:
 - /var/log/ip64net/apache2/*.log
labels:
  type: apache2
---
filenames:
 - /var/log/traefik/*.log
labels:
 type: traefik
---
filenames:
 - /var/log/crowdsec/*.log
labels:
 type: crowdsec
---

Fixe Einstellungen für den Betrieb von Traefik
Bitte an zwei Stellen die E-Mail-Adresse Testmail@gmx.de durch Ihre E-Mail-Adresse ersetzen. Diese wird von Letsencrypt verwendet, um notfalls wichtige Informationen an den Zertifikatsinahber zu senden.
nano /srv/web/traefik/traefik.yml

api:
  dashboard: true

metrics:
  prometheus:
    addRoutersLabels: true

certificatesResolvers:
  http_resolver:
    acme:
      email: "Testmail@gmx.de"
      storage: "acme_letsencrypt.json"
      #Testserver:
      caServer: "https://acme-staging-v02.api.letsencrypt.org/directory"
      #Normaler Server
      #caServer: "https://acme-v02.api.letsencrypt.org/directory"
      
      httpChallenge:
        entryPoint: web
  tls_resolver:
    acme:
      email: "Testmail@gmx.de"
      storage: "tls_letsencrypt.json"
      #Testserver:
      caServer: "https://acme-staging-v02.api.letsencrypt.org/directory"
      #Normaler Server
      #caServer: "https://acme-v02.api.letsencrypt.org/directory"

      tlsChallenge: {}
entryPoints:
  ping:
    address: ':88'
  web:
    address: ':80'
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
      middlewares:
        - traefik-crowdsec-bouncer@file
  websecure:
    address: ':443'
    http:
      middlewares:
        - traefik-crowdsec-bouncer@file
    proxyProtocol:
      #insecure: true
      trustedIPs:
       - 10.0.0.0/8
       - 172.30.0.0/16
       - 172.31.0.0/16
       - 192.168.178.0/16
    forwardedHeaders:
      #insecure: true
      trustedIPs:
       - 10.0.0.0/8
       - 172.30.0.0/16
       - 172.31.0.0/16
       - 192.168.178.0/16

ping:
  entryPoint: "ping"

global:
  checknewversion: true
  sendanonymoususage: false

experimental:
  plugins:
    real-ip:
      moduleName: github.com/Paxxs/traefik-get-real-ip
      version: "v1.0.2"

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
    network: proxy
  file:
    filename: "./dynamic_conf.yml"
    watch: true
  providersThrottleDuration: 10s

log:
  level: "INFO"
  filePath: "/var/log/traefik/traefik.log"
  #Logrotate-Einstellungen
  maxAge: 30
  maxBackups: 12
accessLog:
  filePath: "/var/log/traefik/access.log"
  bufferingSize: 100

Dynamische Einstellungen für den Betrieb von Traefik
Ddiese werden im laufenden Betrieb regelmäßig neu eingelesen.
Dadurch können also Konfigurationen ohne Neustart des Containers erfolgen.
sudo nano /werv/web/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:
    default:
      chain:
        middlewares:
          - default-security-headers
          - gzip

    default-security-headers:
      headers:
        browserXssFilter: true
        contentTypeNosniff: true
        forceSTSHeader: true
        frameDeny: true
        stsIncludeSubdomains: true
        stsPreload: true
        stsSeconds: 31536000
        customFrameOptionsValue: "SAMEORIGIN"

    gzip:
      compress: {}

    traefik-crowdsec-bouncer:
      forwardauth:
        address: http://traefik-crowdsec-bouncer:8080/api/v1/forwardAuth
        trustForwardHeader: true

    real-ip-cf:
      plugin:
        real-ip:
          Proxy:
            - proxyHeadername: "*"
              realIP: Cf-Connecting-Ip
              OverwriteXFF: true
    # A basic authentification middleware, to protect the Traefik dashboard to anyone except myself
    # Use with traefik.http.routers.myRouter.middlewares: "traefikAuth@file"
    traefikAuth:
      basicAuth:
        users:
          - "admin:$apr1$fMgwWnWq$9lSqU1k0nWhLE0NF.eFIS0"
          # Erzeugung mit echo $(htpasswd -nb admin 'traefik!admin!2024')
          # vorher das Paket apache2-util installieren: apt update && apt install apache2-utils

Icon für die Test-Webseite. Bitte im Verzeichnis
/srv/web/html/demo/favicon.ico
speichern
Icons können am einfachsten kostenlos auf der Seite
https://www.ionos.de/tools/favicon-generator
erzeugt werden.

Definition zur Erzeugung eines Containers mit Apache und PHP 7
sudo nano /srv/web/php/dockerfile_php7

FROM php:7.2-apache
#RUN docker-php-ext-install mysqli pdo pdo_mysql
#RUN a2enmod userdir
#RUN a2enmod rewrite
#ADD userdir.conf /etc/apache2/mods-enabled/

RUN echo "ServerName intranet" >> /etc/apache2/apache2.conf

Definition zur Erzeugung eines zweiten Containers mit Apache und PHP 8
sudo nano /srv/web/php/dockerfile_php8

FROM php:8.3-apache
#RUN docker-php-ext-install mysqli pdo pdo_mysql
#RUN a2enmod userdir
#RUN a2enmod rewrite
#ADD userdir.conf /etc/apache2/mods-enabled/

RUN echo "ServerName web8" >> /etc/apache2/apache2.conf

Nach Ausführung der genannten Schritte sollte sich folgende Datei- und Verzeichnisstruktur ergeben:

Verzeichnisstruktur mit den erstellten Konfigurationsdatien

In nachstehender ZIP-Datei finden Sie eine Sicherung des gezeigten Verzeichnisses. Die Angaben in den Konfigurationsdateien müssen natürlich noch auf Ihre Bedürfnisse umgestellt werden.

Dateirechte korrekt setzen

Nun setzen wir sicherheitshalber nochmal die Dateizugriffsrechte in unserem Verzeichnis auf den lokalen User (bei mir pi) und der Gruppe root:

sudo chown pi:root /srv/web -R

Container erstmalig erzeugen

Wir gehen in unser Verzeichnis und testen die Definitionsdatei für docker compose:

cd /srv/web
docker compose config

Wenn uns jetzt der vollständige Dateiinhalt ohne Fehlermeldung angezeigt wird, können wir mit dem ersten Start fortfahren

docker compose up -d

Nun werden die Container erzeugt und im Hintergrund gestartet. Beim ersten Aufruf dauert der Vorgang etwas länger, da die Images vom Docker-Hub heruntergeladen und daraus die Container gebaut werden. Die Ergebnisse der dabei angewandten Zwischenschritte werden von Docker lokal gespeichert und müssen erst nach Änderungen an den Images oder den dockkerfiles erneut gebaut werden.

Wenn der Vorgang erfolgreich war, werden die Container mit „gestarted“ oder „healthy“ markiert. Die gerade aktiven Container können Sie sich anzeigen lassen mit dem Kommando:

docker ps

Wie müssen aber erst noch diverse Einstellungen vornehmen und beenden deshalb alle Container mit der Anweisung:

docker compose down

API-Key für Crowdsec-bouncer erzeugen

Um von Crowdsec aktuelle Datenbanken zu lesen, benötigen wir einen API-Key. Hieru starten wir manuell nur den Crowdsec-Container und lassen eine bestimmte Anweisung darin laufen:

# Den Container im Hintergrund laufen lassen
docker compose up crowdsec -d

# Generierung des API-Key
docker compose exec -t crowdsec cscli bouncers add traefik-crowdsec-bouncer

# danach den Crowdsec-Container wieder beenden
docker compose down

Es erscheint eine Anzeige mit dem generierten API-Key:

Diesen API-Key übernehmen wir in die Zwischenablage und um ihn in die Konfigurationsdatei einzutragen. Um Probleme beim Speichern zu vermeiden, gleich nochmal die Dateirechte auch für die neu erzeugten Dateien aktualisieren.

sudo chown pi:root /srv/web/ -R
sudo nano /srv/web/config/traefik-crouwdsec-bouncer.env

und in die erste Zeile bei CROWDSEC_BOUNCER_API_KEY= den API-Key hinzufügen

Jetzt können Sie die Logging-Funktion des crowdsec-sontainers festlegen. Wenn er in eine Log-Datei schreiben soll die Datei /srv/web/crowdsec/config/config.yaml öffen und im Abschnitt common den Eintrag log_media von stdout auf file ändern:

Jetzt sollte nochmal die Datei /srv/crowdsec/config/acquis.yaml auf Übereinstimmung mit oben gezeigten Inhalt überprüft werden.

Letsencrypt-Zertifikat für den Traefik-Server erzeugen

Nun erzeugen wir ein durch Letsencrypt erzeugtes Zertifikat für unseren Server.

Hierzu muss der Aufruf unserer Domain auf unserem Raspi weitergeiletet werden. Dies haben wir ja weiter oben durch die Einrichtung der Verbindung zu IP64.net auf unserem Router erledigt. Weiterhin haben wir zur Speicherung der LetsEncrypt-Daten die beiden json-Datein zur Verfügung gestellt und mit der Zugriffstufe 0600 versehen.

sudo chmod 0600 /srv/web/traefik/*letsencrypt.json

Zuvor müssen Sie in der Datei /srv/web/traefik/traefik.yaml in den zwei Resolvern jeweils im Abschnitt acme die Angaben der E-Mail-Adresse auf eine gültige E-Mail-Adresse ändern.

Starten Sie die Container im Hintergund mit dem Kommando:

cd /srv/web
docker compose up -d&

Nach einer kurzen Zeitspanne von wenigen Minuten sollten alle Container als „Healthy“ oder „gestartet“ erscheinen. Die können Sie auch mit dem Befehldocker ps kontrollieren.

Im Fehlerfall stoppen Sie durch Eingabe von docker compose down alle Container.

Ob der Bezug des Zertifikates funkionierte, können Sie z. B. in der Datei /srv/web/var/log/traefik/traefik.log erkennen.

sudo cat /srv/werb/var/log/traefik/traefig.log

Bei Erfolg finden Sie Angaben zum Zertifikat in der Datei /srv/web/traefik/acme_letsencrypt.json .

sudo cat /srv/web/traefik/acme_letsencrypt.json