Bei GNU/Linux.ch gibt es die Möglichkeit, Artikelvorschläge einzureichen. Vor einiger Zeit habe ich dort diesen Vorschlag gepostet:
Jetzt habe ich eine viel einfachere Methode entdeckt, um meine Docker-Images unter einer Domain oder mehreren zu binden. Da ich Traefik zu kompliziert finde, da man an jedem Docker-Image Traefik-spezifische Zeilen hinzufügen muss, was man bei Caddyfile nicht machen muss. Somit hat man eine saubere Trennung zwischen dem Container und dieser Traeffic-Verwaltung.
Nun habe ich zu meinem Artikelvorschlag selbst einen Artikel geschrieben.
Caddy image
Dieses Image managed alle TLS-Abfragen automatisch. Dabei kann man verschiedene Varianten wählen:
- Let's Encrypted (Default)
- Self-Signed-Key
- DuckDNS (Um keine Portweiterleitung von Port 80/443 haben zu müssen)
Alle, die sich das erst auf Docker Hub anschauen möchten: Hub-Docker.
Vollständige Ordnerstrucktur
Daraus resultiert folgende Ordnerstruktur (in alter Manier: tree-output):
/ Domainname
├ docker-compose.yml
├ Caddyfile
├ / caddy_data
│ └ ...
└ / caddy_config
└ ...
Hier noch einige One-Liner zur Erstellung der Ordnerstruktur und der zwei Dateien:
# Ordner kurze Schreibweise (Flavor 01)
mkdir -p domainname/{caddy_data,caddy_config}
# Ordner lange Schreibweise (Flavor 02)
mkdir -p domainname/caddy_data domainname/caddy_config
# Dateien
touch domainname/docker-compose.yml domainname/Caddyfile
Konfigurationsdatei Caddyfile
Die Struktur ist sehr einfach gehalten.
# domain.de abändern in die gewünschte Adresse
domain.de {
# Zugriff beschränken
@allowed_ip {
# Ganze lokales Netzwerk zugriff geben
remote_ip 192.168.0.0/24
# Einzelne IP zugriff gewähren
remote_ip 192.168.0.1
# Oder falls es eine öffentliche IP sein soll
# dann diese eintragen. Ist es eine wechselnde öffentliche IP,
# dann immer vorher anpassen, bevor der Container gestartet wird!
}
# Richtige IP klopft an, dann weiter reichen an Webserver - Docker Container
handle @allow_ip {
reverse_proxy dockerName:dockerPort {
# Wenn man verschleiern möchte, dass man einen alten
# apache2 oder nginx Webserver nutzt.
header_down -Server
}
}
# Alle anderen IP's die nicht in der Liste stehen, landen hier:
handle {
respond "Access denied! (Forbidden - 403)" 403
}
# Falls der Service nicht läuft kann man eine eindeutige Nachricht geben:
handle_errors {
@unavailable expression `{err.status_code} >= 500`
respond @unavailable "Sorry, the website is currently unavailable due to maintenance. Please try again later." 503
}
# komprimierter Aufruf
encode gzip
# Fake Webserver Informationen zeigen
header {
-Server
Server "Web-Server-Name Versionsnummer"
Strict-Transport-Security "max-age=31536000;"
}
}
Dies wäre eine mögliche Konfiguration für eine Let's Encrypted Variante.
Konfigurationsdatei Docker-compose Yaml
caddy:
image: caddy:latest
restart: always
ports:
# HOST_PORT:DOCKER_PORT
# Sollten Ports schon verwendet werden, dann 80 in 8080
# das selbe mit dem SSL-Port 443 in 8443
#- "80:80"
- "8080:80"
#- "443:443"
- "8443:443"
volumes:
# Caddyfile neben der docker-compose.yml Datei
- ./Caddyfile:/etc/caddy/Caddyfile
# Einen Ordner 'caddy_data' neben der docker-compse.yml Datei
- ./caddy_data:/data
# Einen weiteren Ordner 'caddy_config' neben der docker-compose.yml Datei
- ./caddy_config:/config
networks:
- caddy_net
# Achtung: Sicherheitsrisiko! Steht nur hier als Vervollständigung
#extra_hosts:
# Ermöglicht Zugriff auf lokale Netzgeräte vom Container aus
# - "host.docker.internal:host-gateway"
networks:
caddy_net:
driver: bridge
Möchte man hingegen einen Self-Sign-Key verwenden
Dann muss im Caddyfile ganz oben stehen mit folgenden Parametern. (Es gibt noch andere Parameter, die man definieren kann. Diese können von folgender Webseite Caddyserver.com/docs/... geholt werden).:
{
# TLS options
#auto_https off|disable_redirects|ignore_loaded_certs|disable_certs
#email example@mydomain.de
local_certs
tls internal
#key_type ed25519|p256|p384|rsa2048|rsa4096
# Neu erzeugen nach 90 Tagen sind 129600 Minuten ( 60 Minuten * 24 Stunden * 90 Tage )
#renew_interval 129600m
}
Möchte man DuckDNS verwenden, benötigt man ein Konto bei DuckDNS
Folgendes Docker-Image ist nötig caddy-dns/duckdns und in der Caddyfile Datei muss folgendes stehen im globalen Bereich tls { dns duckdns DEIN_DUCKDNS_TOKEN } und folgender Eintrag darf in der docker-compose.yml auch nicht fehlen:
caddy:
# Wichtig, da ansonsten das Plugin fehlt, was gebraucht wird für DuckDNS
image: caddy-dns/duckdns
...
environment:
- DUCKDNS_TOKEN=DEIN_DUCKDNS_TOKEN
...
Dieses DuckDNS kann man nutzen, wenn man HTTPS verwenden möchte, ohne den NAG-Screen "Sicherheitsrisiko - Diese Webseite nutzt ein selbst erstelltes Zertifikat... Vertrauen?"
Dann noch ein Eintrag in die /etc/hosts Datei, um deine Domain bekannt zu machen.
# Wenn es nur lokal auf deinem Rechner laufen soll
127.0.0.1 domainname.de subdomain.domainname.de andererdomainname.de
# Ansonsten deine Netzwerk IP-Adresse nutzen
192.168.0.1 domainname.de subdomain.domainname.de andererdomainname.de
Hinweis für die Nutzung von Docker und podman
Bei Docker kann man die Namensgebung von der docker-compose.yml Datei als Hostnamen verwenden. Mit podyman als Docker-Image-Starter muss man die IP-Adresse verwenden, ganz wichtig, weil podyman keinen internen DNS-Handler besitzt, um die Namensauflösung zu steuern. Außerdem sei zu podyman zu sagen, dass es rootless (ohne Administratoren-Rechte) arbeitet, also muss man darauf achten, dass man die richtigen Docker-Images (rootless unterstützen) auswählt. Ansonsten wundert man sich über Fehlermeldungen, die man hätte im Vorfeld vermeiden können.
Weiterer Hinweis für das Anschauen der Logs innerhalb von containern
Mit folgendem Befehl kann man sich die Log-History ansehen, ob fehlerhafte Einträge in der Konfiguration sind oder andere nützliche Hinweise, die man vorher nicht gesehen hat.
# Neuste Docker Version
docker compose logs dockername
# Ältere Docker Version müssen folgendes eingeben:
docker-compose logs dockername
Fazit
Meiner Meinung nach ist Caddy ein viel einfacheres Tool als Traefik, da man nur einmal konfigurieren muss. Bei Traefik muss man bei jedem einzelnen Container immer 3-5 Zeilen extra hinzufügen, damit Traefik weiss, wie es damit umzugehen hat.
Titelbild: https://cdn.hackersandslackers.com/2019/03/caddy.jpg
Quellen:
[1] https://hub.docker.com/_/caddy - Docker Image
[2] https://caddyserver.com/docs/caddyfile/options - Caddyfile TLS Optionen Erklärungen
[3] https://caddyserver.com/docs/caddyfile - Allgemeine Konfigurationen



Bei mir hinterlässt der Abschnitt "Hinweis für die Nutzung von Docker und podman" einiges an Fragen:
podymangemeint? Mit ist im Umfeld von podman dieser Name noch nicht untergekommen. Auch eine Websuche nach diesem Begriff hat keine Aufklärung gebracht.> Bei Docker kann man die Namensgebung von der docker-compose.yml Datei als Hostnamen verwenden. Mit podyman als Docker-Image-Starter muss man die IP-Adresse verwenden, weil podyman keinen internen DNS-Handler besitzt, um die Namensauflösung zu steuern.
Dies gilt meines Wissens nach für Container, welche das podman default-bridge-Netzwerk nutzen: Debug Inter-Container DNS Resolution
Mit
podman network createangelegte dedizierte Netzwerke haben eine funktionierende DNS-Auflösung. Ich nutze z.B. Caddy in einem rootless podman Container als reverse proxy für Nextcloud und in der Caddy-Konfiguration steht statt einer IP der Hostname des Nextcloud-Containers.3. > Außerdem sei zu podyman zu sagen, dass es rootless (ohne Administratoren-Rechte) arbeitet, also muss man darauf achten, dass man die richtigen Docker-Images (rootless unterstützen) auswählt.
podmankann sowohlrootfulals auchrootlessgenutzt werden.rootfolbedeutet in dem Zusammenhang, dass die podman-Container als Benutzerrootbetrieben werden.rootlesswerden podman-Container mit einer Benutzerkennungrootbetrieben.Advanced containers with Podman
Nur sehr wenige Container-Images werden als rootless zur Verfügung gestellt. Wäre dies wirklich ein Problem, so dürfte keiner meiner aktuellen Container, welche alle rootless als
podman quadletslaufen, aktiv sein. Auchcaddyhabe ich bisher nicht als rootless-Container-Image gesehen.Keine Ahnung, worauf sich in diesem konkreten Fall bezogen wird, aber vielleicht hängt es eher an den Berechtigungen mit welchem auf das Host-Dateisystem bei Einbindung von
volumeszugegriffen wird. Dies ist in der Tat bei rootless podman mitunter ein Problem, insbesondere, wenn direkt auf Dateien im Dateisystem zugegriffen werden soll. Bei mir hat sich dafür erstmal die Nutzung einges dediziertenpodman volumeeingespielt, um in einem laufenden Container die User-ID ermitteln zu können, unter welcher der Prozess im Container läuft.Anhand dieser Information lässt sich in den meisten Fällen z.B. mit
--userns=keep-id:uid=999,gid=999die Berechtigung auf Dateien im Hostdateisystem so einstellen, dass die Dateien auch dem Benutzer gehören, unter welchem der rootless Container läuft. Damit ist dann ein einfacher Zugriff auf alle Dateien möglich.podman run
Hallo Tobias,
ich habe natürlich podman-docker gemeint gehabt. Ich habe nur noch mein Linux-Benutzernamen
podymanim Kopf gehabt als ich den Artikel schrieb, der zugriff auf podman bekommen hat.Ich habe es nur mit aufgenommen gehabt, weil ich große Probleme hatte. Es hat immer sehr lange gedauert bis der podman-container bereit war, weil die Namensfindung einfach Katastrophe war. Somit habe ich per inspect die jeweilige IP-Adresse rausgesucht und siehe da die podman-container sind unter 1 Sekunde bereit - als ich den Containernamen drin hatte, wie bei Docker, da hat es 2 Minuten gedauert. Deswegen diese Formulierung im Text. Ich hatte einige Container, die Ihre Services nicht starten konnten, weil kein root es gestartet hatte, sondern ein noraler User, der aber nicht die Berechtigung hatte es starten zu dürfen.
Zugriff auf Dateien war ebenso eine Qual, aber als ich es als Volume gemacht habe lief auf einmal alles. Ich habe es mir immer schwerer gemacht als es wirklich war. Ist halt, wenn man zu viel will und möglichst alles abgeschottet sein soll, wie möglich.
Ich hoffe, ich konnte Dir damit weiterhelfen, wie meine Gedanken dazu waren. Auch das werde ich bei den nächsten Artikeln berücksichtigen, was durch Probieren herauskam, dass es Nachvollziehbar bleibt.
Danke für den Artikel, ich habe auch dafür gestimmt. Seit einiger Zeit benutze ich den Nginx-Proxy im Docker aber wollte seit ich davon weiß einmal Traefik ausprobieren. Es gibt zwar einen Traefik-Manager im Docker, aber das hatte nie richtig funktioniert und deshalb nutze ich Nginx-Proxy. Caddy kenne ich nur vom Namen und werde es Aufgrund Ihres Artikels einmal testen. Eine kleines Beispiel für eine Verbindung hätte ich mir noch gewünscht. Danke. Gruß Rigo
Hallo Rigobert,
Im Caddyfile musst du die Zeile
reverse_proxy dockerName:dockerPortanpassen in Beispiel mäßig Container von dein nginx lautet webserver1 und läuft auf dem default Port 8080, dann müsste die Zeilereverse_proxy webserver1:8080. Es ist nicht der Exposed-Port gemeint, sondern der interne Port vom Container.Dann solltest du noch im Caddyfile
domain.dezu deiner aktuellen Domain umbenennen Beispielhaftexample.comoderexample.de.Dein Container muss dann nur noch im selben Netzwerkbereich liegen, somit in der Docker-compose.yml in der selben wie caddy, dann einfach:
Falls es in einer anderen Datei steht, dann einfach external network machen, aber achte darauf, dass caddy container schon läuft, da ansonsten das Netzwerk nicht vorhanden ist:
Ist damit die Beispiel-Frage beantwortet? Ich bin davon ausgegangen das es schon fortgeschrittene Docker Kenntnisse vorhanden sind. Ich werde das nächste mal bei meinem Artikel darauf achten, dass es für jeder man ist - Nachvollziehbar.