Caddy – Konzepte und Konfiguration: Unterschied zwischen den Versionen

Aus Tuxipedia
Keine Bearbeitungszusammenfassung
 
(5 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 20: Zeile 20:
Grundstruktur:
Grundstruktur:


<syntaxhighlight lang="caddy">
<pre>
 
example.com {
example.com {
     # Direktiven für diese Domain
     # Direktiven für diese Domain
}
}
</syntaxhighlight>
</pre>


Alles innerhalb der geschweiften Klammern gilt nur für diese Domain. Mehrere Domains können in derselben Datei konfiguriert werden.
Alles innerhalb der geschweiften Klammern gilt nur für diese Domain. Mehrere Domains können in derselben Datei konfiguriert werden.
Zeile 36: Zeile 37:
'''Kurzfassung:''' Leitet eingehende Anfragen an einen anderen Dienst weiter.
'''Kurzfassung:''' Leitet eingehende Anfragen an einen anderen Dienst weiter.


<syntaxhighlight lang="caddy">
reverse_proxy localhost:8080
reverse_proxy localhost:8080
reverse_proxy myapp:3000
reverse_proxy myapp:3000
</syntaxhighlight>


'''Erklärung:'''
'''Erklärung:'''
Zeile 52: Zeile 51:
Bei Docker-Containern kann der Containername als Hostname verwendet werden:
Bei Docker-Containern kann der Containername als Hostname verwendet werden:


<syntaxhighlight lang="caddy">
<pre>
reverse_proxy nextcloud:443
reverse_proxy nextcloud:443
</syntaxhighlight>
</pre>


Caddy löst <code>nextcloud</code> über das interne Docker-Netz auf.
Caddy löst <code>nextcloud</code> über das interne Docker-Netz auf.
Zeile 64: Zeile 63:
'''Kurzfassung:''' Steuert wie Caddy intern mit dem Zieldienst kommuniziert.
'''Kurzfassung:''' Steuert wie Caddy intern mit dem Zieldienst kommuniziert.


<syntaxhighlight lang="caddy">
<pre>
reverse_proxy nextcloud:443 {
reverse_proxy nextcloud:443 {
     transport http {
     transport http {
Zeile 70: Zeile 69:
     }
     }
}
}
</syntaxhighlight>
</pre>


'''Erklärung:'''
'''Erklärung:'''
Zeile 89: Zeile 88:
'''Kurzfassung:''' Setzt, entfernt oder verändert HTTP-Response-Header – also Header die Caddy an den Browser zurückschickt.
'''Kurzfassung:''' Setzt, entfernt oder verändert HTTP-Response-Header – also Header die Caddy an den Browser zurückschickt.


<syntaxhighlight lang="caddy">
<pre>
header {
header {
     -Server
     -Server
Zeile 97: Zeile 96:
     Referrer-Policy "no-referrer"
     Referrer-Policy "no-referrer"
}
}
</syntaxhighlight>
</pre>


'''Erklärung:'''
'''Erklärung:'''
Zeile 109: Zeile 108:
* <code>Referrer-Policy "no-referrer"</code> – Der Browser sendet beim Klick auf einen Link keine Information woher der Nutzer kam. '''Konkret:''' Die externe Seite erfährt nicht, woher du kommst.
* <code>Referrer-Policy "no-referrer"</code> – Der Browser sendet beim Klick auf einen Link keine Information woher der Nutzer kam. '''Konkret:''' Die externe Seite erfährt nicht, woher du kommst.


Gemeinsam bilden die drei Schichten eine Defense in Depth – jede Schicht unabhängig, jede macht eine andere Angriffsart schwerer. 🏰
Diese Header bilden gemeinsam eine erste Verteidigungslinie: Jeder schützt gegen eine andere Angriffsart, jeder unabhängig vom anderen. Das Prinzip dahinter heißt '''Defense in Depth''' Sicherheit durch mehrere unabhängige Schichten. Fällt eine aus, stehen die anderen noch. 🏰




Zeile 118: Zeile 117:
'''Kurzfassung:''' Setzt Header die Caddy an den '''Zieldienst''' (upstream) weiterleitet – nicht an den Browser.
'''Kurzfassung:''' Setzt Header die Caddy an den '''Zieldienst''' (upstream) weiterleitet – nicht an den Browser.


<syntaxhighlight lang="caddy">
<pre>
reverse_proxy myapp:8080 {
reverse_proxy myapp:8080 {
     header_up X-Real-IP {remote_ip}
     header_up X-Real-IP {remote_ip}
}
}
</syntaxhighlight>
</pre>


'''Erklärung:'''
'''Erklärung:'''
Zeile 138: Zeile 137:
'''Kurzfassung:''' Definiert eine benannte Bedingung die auf Anfragen zutreffen kann oder nicht.
'''Kurzfassung:''' Definiert eine benannte Bedingung die auf Anfragen zutreffen kann oder nicht.


<syntaxhighlight lang="caddy">
<pre>
@xmlrpc path /xmlrpc.php
@xmlrpc path /xmlrpc.php
respond @xmlrpc 403
respond @xmlrpc 403
Zeile 149: Zeile 148:
     reverse_proxy backend:8080
     reverse_proxy backend:8080
}
}
</syntaxhighlight>
</pre>


'''Erklärung:'''
'''Erklärung:'''
Zeile 171: Zeile 170:
'''Kurzfassung:''' Führt Direktiven nur dann aus wenn ein Matcher zutrifft. Nicht-passende Anfragen fallen durch zum nächsten Block.
'''Kurzfassung:''' Führt Direktiven nur dann aus wenn ein Matcher zutrifft. Nicht-passende Anfragen fallen durch zum nächsten Block.


<syntaxhighlight lang="caddy">
<pre>
@static path /static/*
@static path /static/*
handle @static {
handle @static {
Zeile 180: Zeile 179:
     reverse_proxy backend:8080
     reverse_proxy backend:8080
}
}
</syntaxhighlight>
</pre>


'''Erklärung:'''
'''Erklärung:'''
Zeile 202: Zeile 201:
'''Kurzfassung:''' Leitet den Browser auf eine andere URL um.
'''Kurzfassung:''' Leitet den Browser auf eine andere URL um.


<syntaxhighlight lang="caddy">
<pre>
redir https://new.example.com{uri} 301
redir https://new.example.com{uri} 301
redir https://new.example.com{uri} 308
redir https://new.example.com{uri} 308
</syntaxhighlight>
</pre>


'''Erklärung:'''
'''Erklärung:'''
Zeile 230: Zeile 229:
'''Kurzfassung:''' Antwortet direkt mit einem HTTP-Statuscode – ohne den Zieldienst zu kontaktieren.
'''Kurzfassung:''' Antwortet direkt mit einem HTTP-Statuscode – ohne den Zieldienst zu kontaktieren.


<syntaxhighlight lang="caddy">
<pre>
@xmlrpc path /xmlrpc.php
@xmlrpc path /xmlrpc.php
respond @xmlrpc 403
respond @xmlrpc 403


respond 404
respond 404
</syntaxhighlight>
</pre>


'''Erklärung:'''
'''Erklärung:'''
Zeile 264: Zeile 263:
'''Kurzfassung:''' Liefert statische Dateien aus einem Verzeichnis aus.
'''Kurzfassung:''' Liefert statische Dateien aus einem Verzeichnis aus.


<syntaxhighlight lang="caddy">
<pre>
root * /var/www/html
root * /var/www/html
file_server
file_server
</syntaxhighlight>
</pre>


'''Erklärung:'''
'''Erklärung:'''
Zeile 279: Zeile 278:
'''Kurzfassung:''' Steuert TLS-Einstellungen für eine Domain.
'''Kurzfassung:''' Steuert TLS-Einstellungen für eine Domain.


<syntaxhighlight lang="caddy">
<pre>
tls {
tls {
     alpn http/1.1
     alpn http/1.1
}
}
</syntaxhighlight>
</pre>


'''Erklärung:'''
'''Erklärung:'''
Zeile 297: Zeile 296:
Am Anfang des Caddyfiles können globale Einstellungen gesetzt werden:
Am Anfang des Caddyfiles können globale Einstellungen gesetzt werden:


<syntaxhighlight lang="caddy">
<pre>
{
{
     admin 127.0.0.1:2019
     admin 127.0.0.1:2019
}
}
</syntaxhighlight>
</pre>


<code>admin</code> aktiviert die Caddy-Admin-API auf dem angegebenen Port. Mit <code>127.0.0.1:2019</code> ist sie nur lokal erreichbar – nicht von außen. Über diese API kann Caddy zur Laufzeit neu geladen werden:
<code>admin</code> aktiviert die Caddy-Admin-API auf dem angegebenen Port. Mit <code>127.0.0.1:2019</code> ist sie nur lokal erreichbar – nicht von außen. Über diese API kann Caddy zur Laufzeit neu geladen werden:
Zeile 307: Zeile 306:
<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
caddy reload --config /etc/caddy/Caddyfile
caddy reload --config /etc/caddy/Caddyfile
</syntaxhighlight>
</pre>


----
----
Zeile 340: Zeile 339:


Docker's internes DNS löst myAwesomeContainer zur richtigen IP auf – automatisch. Das ist die unsichtbare Brücke zwischen beiden Files.
Docker's internes DNS löst myAwesomeContainer zur richtigen IP auf – automatisch. Das ist die unsichtbare Brücke zwischen beiden Files.
Und das gemeinsame docker_network ist der Raum in dem alle miteinander reden dürfen. Wer nicht drin ist, hört nichts. 🏰
Und das gemeinsame Docker-Netzwerk ist der Raum in dem alle miteinander reden dürfen. Wer nicht drin ist, hört nichts. 🏰


=== Siehe auch ===
=== Siehe auch ===


* [[Webserver, Reverse Proxy und Container – wie die Schichten zusammenspielen]] – Konzepte hinter Reverse Proxy, Pfad-Whitelist, Security-Header und Container-Netzwerken
* [[Hairpin-NAT – Collabora und Nextcloud hinter Caddy]]
* [[Hairpin-NAT – Collabora und Nextcloud hinter Caddy]]
* [https://caddyserver.com/docs Caddy-Dokumentation (englisch)]
* [https://caddyserver.com/docs Caddy-Dokumentation (englisch)]

Aktuelle Version vom 25. April 2026, 12:34 Uhr

Caddy – Konzepte und Konfiguration

Was macht ein Webserver?

Ein Webserver wartet auf eingehende Netzwerkanfragen (HTTP/HTTPS) und beantwortet sie. Das kann bedeuten:

  • Eine Datei ausliefern (HTML, Bild, CSS)
  • Eine Anfrage an einen anderen Dienst weiterzuleiten (Reverse Proxy)
  • Eine Anfrage umzuleiten (Redirect)
  • Eine Anfrage abzulehnen (403, 404)

Klassische Webserver wie Apache und nginx können all das – benötigen aber für HTTPS externe Werkzeuge (z.B. Certbot) und manuelle Zertifikatsverwaltung.

Caddy übernimmt die Zertifikatsverwaltung automatisch: Er bezieht TLS-Zertifikate von Let's Encrypt, erneuert sie selbstständig, und erzwingt HTTPS ohne manuellen Eingriff.

Das Caddyfile

Die Konfiguration von Caddy erfolgt in einer einzigen Textdatei: dem Caddyfile. Die Syntax ist bewusst einfach gehalten.

Grundstruktur:


example.com {
    # Direktiven für diese Domain
}

Alles innerhalb der geschweiften Klammern gilt nur für diese Domain. Mehrere Domains können in derselben Datei konfiguriert werden.


Direktiven

reverse_proxy

Kurzfassung: Leitet eingehende Anfragen an einen anderen Dienst weiter.

reverse_proxy localhost:8080 reverse_proxy myapp:3000

Erklärung:

Ein Reverse Proxy sitzt zwischen dem Browser des Nutzers und dem eigentlichen Dienst. Der Browser spricht immer mit Caddy – nie direkt mit dem Dienst dahinter. Caddy nimmt die Anfrage entgegen, leitet sie weiter, bekommt die Antwort, und schickt sie zurück an den Browser.

Vorteile:

  • Nur Caddy ist nach außen sichtbar – interne Dienste bleiben verborgen
  • TLS wird einmalig bei Caddy terminiert
  • Mehrere Dienste können hinter einer einzigen IP laufen

Bei Docker-Containern kann der Containername als Hostname verwendet werden:

reverse_proxy nextcloud:443

Caddy löst nextcloud über das interne Docker-Netz auf.


transport http

Kurzfassung: Steuert wie Caddy intern mit dem Zieldienst kommuniziert.

reverse_proxy nextcloud:443 {
    transport http {
        tls_insecure_skip_verify
    }
}

Erklärung:

Standardmäßig kommuniziert Caddy mit Zieldiensten über HTTP. Spricht der Zieldienst selbst HTTPS (z.B. ein Container der intern TLS verwendet), muss Caddy das wissen – und bekommt den transport http-Block.

tls_insecure_skip_verify deaktiviert die Zertifikatsprüfung für die interne Verbindung. Das klingt unsicher – ist es intern aber nicht, weil:

  • Die Verbindung innerhalb des Docker-Netzes stattfindet
  • Kein externer Angreifer Zugriff auf diesen internen Kanal hat
  • Das Zertifikat des Zieldienstes selbstsigniert sein darf

Nach außen (Browser → Caddy) bleibt HTTPS mit gültigem Zertifikat bestehen.


Kurzfassung: Setzt, entfernt oder verändert HTTP-Response-Header – also Header die Caddy an den Browser zurückschickt.

header {
    -Server
    Strict-Transport-Security "max-age=31536000"
    X-Content-Type-Options "nosniff"
    X-Frame-Options "SAMEORIGIN"
    Referrer-Policy "no-referrer"
}

Erklärung:

HTTP-Header sind Metadaten die jede Antwort begleiten. Sie sagen dem Browser wie er die Antwort behandeln soll.

  • -Server – Das Minuszeichen entfernt den Header. Ohne diese Zeile würde Caddy Server: Caddy mitsenden – ein Hinweis für Angreifer welche Software läuft. Konkret: Caddy würde normalerweise mitteilen: "Hallo, ich bin Caddy v2.10.2". Der Minus löscht diesen Header. Der Besucher sieht nichts.
  • Strict-Transport-Security – Sagt dem Browser: „Verbinde dich für die nächsten 31536000 Sekunden (1 Jahr) ausschließlich über HTTPS mit dieser Domain." Konkret: Sagt dem Browser: "Diese Seite gibt es nur per HTTPS, für die nächsten 365 Tage – frag gar nicht erst per HTTP an."
  • X-Content-Type-Options "nosniff" – Verhindert dass der Browser den Dateityp einer Antwort „errät". Schutz gegen bestimmte Angriffe über präparierte Dateien. Konkret: Klassischer Angriffsvektor: Server schickt eine HTML-Datei als text/plain, Browser erkennt sie trotzdem als HTML und führt JavaScript aus.
  • X-Frame-Options "SAMEORIGIN" – Verhindert dass die Seite in einem <iframe> einer fremden Domain eingebettet wird. Schutz gegen Clickjacking. Konkret: Jemand baut deine Seite unsichtbar in seine ein und du klickst unwissentlich auf deren Buttons.
  • Referrer-Policy "no-referrer" – Der Browser sendet beim Klick auf einen Link keine Information woher der Nutzer kam. Konkret: Die externe Seite erfährt nicht, woher du kommst.

Diese Header bilden gemeinsam eine erste Verteidigungslinie: Jeder schützt gegen eine andere Angriffsart, jeder unabhängig vom anderen. Das Prinzip dahinter heißt Defense in Depth – Sicherheit durch mehrere unabhängige Schichten. Fällt eine aus, stehen die anderen noch. 🏰



header_up

Kurzfassung: Setzt Header die Caddy an den Zieldienst (upstream) weiterleitet – nicht an den Browser.

reverse_proxy myapp:8080 {
    header_up X-Real-IP {remote_ip}
}

Erklärung:

Ohne zusätzliche Header sieht ein Dienst hinter Caddy als Absender immer Caddys interne IP – nie die echte Client-IP. Mit header_up X-Real-IP {remote_ip} reicht Caddy die echte IP des Clients weiter.

{remote_ip} ist eine Caddy-Variable die zur Laufzeit durch die tatsächliche IP ersetzt wird.

Caddy setzt X-Forwarded-For, X-Forwarded-Proto und X-Forwarded-Host automatisch – diese müssen nicht manuell gesetzt werden.


@matcher (Named Matchers)

Kurzfassung: Definiert eine benannte Bedingung die auf Anfragen zutreffen kann oder nicht.

@xmlrpc path /xmlrpc.php
respond @xmlrpc 403

@admin {
    method POST
    path /admin/*
}
handle @admin {
    reverse_proxy backend:8080
}

Erklärung:

Matcher sind das Herzstück von Caddy-Konfigurationen. Sie ermöglichen es, Regeln nur auf bestimmte Anfragen anzuwenden.

Das @-Zeichen leitet einen Matcher ein. Der Name danach ist frei wählbar. Matcher können prüfen:

  • path – welcher Pfad wird angefragt?
  • method – GET, POST, PUT, DELETE...?
  • query – enthält die URL bestimmte Parameter?
  • remote_ip – kommt die Anfrage von einer bestimmten IP?
  • header – enthält die Anfrage bestimmte Header?

Mehrere Bedingungen in einem Block werden mit UND verknüpft – alle müssen zutreffen.


handle

Kurzfassung: Führt Direktiven nur dann aus wenn ein Matcher zutrifft. Nicht-passende Anfragen fallen durch zum nächsten Block.

@static path /static/*
handle @static {
    file_server
}

handle {
    reverse_proxy backend:8080
}

Erklärung:

handle ohne Matcher ist der Standardfall – er greift für alles was kein anderer Block behandelt hat. In Kombination mit Matchern entsteht eine Art Weiche: statische Dateien werden direkt ausgeliefert, alles andere geht an den Backend-Dienst.


route

Kurzfassung: Wie handle, aber die Reihenfolge der Direktiven wird strikt eingehalten.

Erklärung:

Caddy verarbeitet Direktiven normalerweise in einer intern festgelegten Reihenfolge – unabhängig davon wie sie im Caddyfile stehen. route erzwingt die Reihenfolge wie sie im Caddyfile steht. Nützlich wenn die Reihenfolge der Verarbeitung wichtig ist.


redir

Kurzfassung: Leitet den Browser auf eine andere URL um.

redir https://new.example.com{uri} 301
redir https://new.example.com{uri} 308

Erklärung:

Der Unterschied zwischen 301 und 308:

Code Name Bedeutung
301 Moved Permanently Browser darf die HTTP-Methode ändern (POST → GET)
308 Permanent Redirect Browser behält die HTTP-Methode bei (POST bleibt POST)

Für einfache Browser-Weiterleitungen ist 301 der Klassiker. Für API-Weiterleitungen oder Formulare ist 308 wichtig.

{uri} ist eine Caddy-Variable für den vollständigen Pfad inkl. Query-String – so bleibt der ursprüngliche Pfad nach der Weiterleitung erhalten.


respond

Kurzfassung: Antwortet direkt mit einem HTTP-Statuscode – ohne den Zieldienst zu kontaktieren.

@xmlrpc path /xmlrpc.php
respond @xmlrpc 403

respond 404

Erklärung:

Nützlich um bestimmte Pfade hart zu blockieren ohne dass die Anfrage den eigentlichen Dienst erreicht. Caddy antwortet sofort – schnell und ressourcenschonend.

Gängige Statuscodes:

Code Bedeutung
200 OK
301/308 Weiterleitung (permanent)
403 Forbidden – Zugriff verweigert
404 Not Found – nicht vorhanden
500 Internal Server Error

file_server

Kurzfassung: Liefert statische Dateien aus einem Verzeichnis aus.

root * /var/www/html
file_server

Erklärung:

root legt das Basisverzeichnis fest. file_server aktiviert die Auslieferung. Caddy sucht dann für jeden Request die entsprechende Datei im Verzeichnis und liefert sie aus – wie ein klassischer Webserver.


tls

Kurzfassung: Steuert TLS-Einstellungen für eine Domain.

tls {
    alpn http/1.1
}

Erklärung:

Caddy verwaltet TLS-Zertifikate automatisch. Der tls-Block ist nur nötig wenn vom Standard abgewichen werden soll.

alpn (Application-Layer Protocol Negotiation) legt fest welche HTTP-Version ausgehandelt wird. Standardmäßig bietet Caddy HTTP/2 an. Mit alpn http/1.1 wird HTTP/2 deaktiviert – nützlich wenn ein Zieldienst damit nicht zurechtkommt.


Globale Optionen

Am Anfang des Caddyfiles können globale Einstellungen gesetzt werden:

{
    admin 127.0.0.1:2019
}

admin aktiviert die Caddy-Admin-API auf dem angegebenen Port. Mit 127.0.0.1:2019 ist sie nur lokal erreichbar – nicht von außen. Über diese API kann Caddy zur Laufzeit neu geladen werden:

<syntaxhighlight lang="bash"> caddy reload --config /etc/caddy/Caddyfile


Abschluss-"Meditation"

Auch, wenn's in diesem Beitrag "nur" um Caddy selbst geht, ist der Webserver dennoch mit allen anderen Schichten verbunden. Die Komplexität eines Webservers kommt daher dass jede dieser Schichten eine eigene Sprache spricht:

Docker denkt in Containern und Netzwerken Caddy denkt in Requests und Routen Collabora denkt in WOPI und Aliases Der Browser denkt in Headers und Policies

Die sprechen alle miteinander – aber keiner erklärt dem anderen was er tut. Die Übersetzung geschieht, bei einer Realisation über Docker-Container, im Zusammenspiel von Kombination aus docker-compose.yml und Caddyfile. Das ist das Schwierige.

Die beiden müssen zusammenpassen:

Compose definiert wer existiert und wie die Container heißen Caddy nutzt genau diese Namen als interne Adressen.

Beispiel (yml):

container_name: myAwesomeContainer


Im Caddyfile spricht Caddy myAwesomeContainer mit diesem Namen an:

reverse_proxy myAwesomeContainer:12345 (-> 12345 = Portnummer, unter der der Container erreichbar ist)


Docker's internes DNS löst myAwesomeContainer zur richtigen IP auf – automatisch. Das ist die unsichtbare Brücke zwischen beiden Files. Und das gemeinsame Docker-Netzwerk ist der Raum in dem alle miteinander reden dürfen. Wer nicht drin ist, hört nichts. 🏰

Siehe auch