← Zurück zum Blog

Caddy + Tailscale = Pangolin Light – getunnelte Dienste ohne Portfreigabe

Caddy + Tailscale = Pangolin Light – getunnelte Dienste ohne Portfreigabe

Pangolin hat mich fasziniert: Ein Tool, das getunnelte Verbindungen ins Heimnetz ermöglicht, ohne dass man einen einzelnen Port ins Internet freigeben muss. Kein Portforwarding, kein Dynamic DNS – einfach Tunnel aufbauen und Dienste erreichbar machen.

Das Problem: Pangolin braucht einen sogenannten Newt-Container als Tunnel-Endpoint. Das ist ein weiterer Container, den man irgendwo betreiben muss – und das widerstrebt mir, wenn ich doch schon Tailscale/Headscale im Einsatz habe.

Die Idee

Was wäre, wenn man stattdessen einfach einen Caddy-Server ins Tailnet stellt? Caddy kann als Reverse Proxy agieren, und wenn er im Tailnet hängt, kennt er alle Knoten und deren Services. Stellt man diesen Caddy jetzt irgendwo ins Internet – etwa auf einem günstigen VPS – hat man einen einzigen öffentlichen Endpunkt, über den man auf alle tailnet-internen Dienste zugreifen kann.

Kein Newt-Container, kein weiterer Tunnel-Dienst. Nur Caddy, das eh schon läuft.

Internet ──▶ Caddy (VPS) ──▶ Tailnet ──▶ Dienste im Heimnetz
                 │
                 └── Tailscale-Client

Warum nicht einfach Tailscale direkt?

Klar, Tailscale kann das auch. Aber:

  • Jeder Client muss Tailscale installiert haben
  • Man kann nicht einfach einen Browser auf einen internen Dienst werfen
  • Für Gastzugriffe oder mobile Geräte ist das umständlich

Caddy als öffentlicher Endpunkt löst das: Einfach die URL aufrufen, Caddy leitet weiter, fertig. TLS-Zertifikate kümmert sich Caddy automatisch drum.

Umsetzung

1. Caddy im Tailnet starten

Caddy läuft bei mir schlank im Alpine-Container – kein Docker, kein Overhead. Tailscale und Caddy installieren:

apk add tailscale caddy

Dienste starten und beim Boot aktivieren:

rc-update add tailscale default
rc-update add caddy default
rc-service tailscale start
rc-service caddy start

Dann den Tailscale-Client ins Tailnet einbinden:

tailscale up --login-server https://headscale.frischux.de

Caddy bekommt automatisch Zugriff auf alle Knoten im Netz.

2. Caddyfile konfigurieren

# Öffentlicher Endpunkt für internen Dienst
dashboard.frischux.de {
    reverse_proxy homeassistant.fx.de:8123
}

# Weiterer Dienst
photos.frischux.de {
    reverse_proxy immich.fx.de:2283
}

Die .fx.de-Adressen sind die internen Tailnet-Adressen der Knoten (heißt bei euch anders, je nach base_domain in Headscale). Caddy löst das über das interne DNS auf.

*Edit:* Soweit zur Theorie, in der Praxis funktioniert es leider nicht so leicht. Ein managed Netzwerk im Incus ist sehr penibel darauf bedacht, die DNS Hoheit zu behalten. Nach einiger Zeit wird die */etc/resolv.conf* vom DHCP überschrieben und schon ist die Magic-DNS-Auflösung im Caddycontainer wieder weg. Ich habe dafür noch keine zufriedenstellende Lösung (tailscale spricht hier schön vom dns fight), ein Workaround ist, im Caddyfile statt der Namen die tailnet IP Adressen (bei mir 100.64.0.x) anzugeben, nicht ganz so elegant, aber funktional.

*Edit 2:* In einem Debain 13 Container mit systemd-resolved funktioniert die Koexistenz offenbar hervorragend. Bisher mochte ich systemd-resolved nicht (zu verkopft, zu komplex), aberr ich beginne, meine Meinung zu ändern.

3. TLS-Zertifikate

Caddy kümmert sich automatisch um Let's Encrypt – solange die öffentlichen Domains auf den VPS zeigen. Das ist das Schöne an Caddy: Kein Certbot, kein cron job, funktioniert einfach.

Der Vorteil gegenüber Pangolin

Aspekt Pangolin + Newt Caddy + Tailscale
Tunnel-Endpoint Newt-Container Caddy (ohnehin vorhanden)
Zusätzliche Abhängigkeit Ja Nein
Konfiguration Web-UI + Config Caddyfile
TLS Automatisch Automatisch
Flexibilität Begrenzt Caddy kann alles

Das ist im Grunde Pangolin Light: Weniger Features, aber auch weniger Komplexität. Wer keine Fancy Web-UI braucht und ohnehin Caddy im Einsatz hat, spart sich den Newt-Container.

Dazu kommt: Pangolin kommt als komplexes Docker-Paket. Caddy hingegen läuft schlank im Alpine-Container – ohne zusätzlichen Overhead.

Fazit

Eine Idee, die ich gestern Abend hatte und die mich nicht mehr losgelassen hat. Noch nicht im produktiven Einsatz, aber der Grundgedanke ist solide: Caddy als öffentlicher Proxy, der über Tailscale auf interne Dienste zugreift. Kein Portforwarding, kein weiterer Tunnel-Service, einfach Caddy das seine Arbeit macht.

Der große Vorteil: Die Dienstcontainer werden unabhängig vom Host. Man kann Container beliebig auf Server verteilen – es muss nichts konfiguriert werden, wo der Dienst läuft. Hauptsache, der Container hängt im Tailnet, dann findet ihn Caddy automatisch.

Wenn wer Lust hat, das auszuprobieren – meldet euch. Ich halte die Augen offen, ob sich das bewährt.