DIESE ANLEITUNG IST VERALTET!!! Bitte verwendet die neue Anleitung!
In dieser Anleitung zeige ich, wie man Headscale mit Docker und Caddy installiert. Headscale ist eine selbstgehostete Open-Source-Implementierung des Tailscale-Kontrollservers. Tailscale ist ein benutzerfreundliches VPN, das auf WireGuard basiert und eine einfache und sichere Netzwerkverbindung zwischen Geräten ermöglicht, ohne komplexe Konfigurationen. Es nutzt eine Zero-Trust-Architektur, um den Netzwerkverkehr zu sichern und erleichtert den Zugriff auf Ressourcen, unabhängig vom Standort der Benutzer.
1. Grundvoraussetzung
- Docker & Docker Compose v2 (Debian / Ubuntu)
- Ein VPS mit fester IP und diese sollte per A-Record auf eine Domain zeigen (FQDN)
- Port 80, 443, 443udp offen + SSH
2. Vorwort
Bevor wir loslegen, möchte ich kurz mein Setup erklären. Ich nutze es seit 4 Wochen in meinem Homelab und bin damit sehr zufrieden. Es ist wichtig zu wissen, dass Headscale sich noch in der Entwicklung befindet, aber es läuft bei mir sehr stabil.
Mein Ziel war es, bestimmte Dienste öffentlich zugänglich zu machen und gleichzeitig bestmöglich abzusichern. Dafür verwende ich ein Setup mit Traefik und CrowdSec. Für besonders sensible Anwendungen wie Vaultwarden suchte ich jedoch eine zusätzliche Sicherheitsmaßnahme. Ich wollte für diese Anwendungen SSL-Zertifikate haben, die nur für mich oder ausgewählte Personen zugänglich sind. Um das zu realisieren, habe ich Headscale eingerichtet.
Das heißt im Klartext: Mein Vaultwarden läuft komplett lokal bei mir zu Hause, hier ist kein Port geöffnet. Verbunden mit Headscale kann ich mir dann allerdings SSL-Zertifikate besorgen und mein Vaultwarden nur von einer bestimmten IP zugänglich machen.
Der Headscale-Server verbindet alle benötigten Server einfach und schnell per VPN. Über einen integrierten Caddy-Container kann ich ausgewählten Diensten SSL-Zertifikate zuweisen und den Zugriff über IP-Blockregeln steuern.
Um es noch komfortabler zu gestalten, habe ich einen sehr günstigen VPS (1 € pro Monat) mit 1 CPU und 1 GB RAM gemietet, was völlig ausreicht. Ich verwende eine Domain für öffentliche Dienste, die über Traefik läuft, und eine zweite Domain für meine internen Dienste. Diese nenne ich hier public.com und private.com.
Auf diese Weise könnte man generell Dienste nach außen freigeben, ohne Ports am Heimrouter öffnen zu müssen, aber das war nicht mein Hauptziel. Ich hoffe, meine Grundidee ist klar, und nun zur Installation.
3. Docker Setup
Hier kümmern wir uns erstmal darum das die Container sauber laufen.
3.1 Ordner anlegen
Ordner Struktur anlegen.
mkdir -p /opt/containers/ && cd /opt/containers
Git Repro clonen.
git clone https://github.com/2TAP2B/headscale.git
Weitere Ordner erstellen
cd headscale mkdir -p caddy/config caddy/data
3.2 Caddyfile anpassen
nano /opt/containers/headscale/caddy/Caddyfile
Hier bitte in der zweiten Zeile headscale.private.com
entsprechend anpassen.
3.3 Headscale config anpassen
nano /opt/containers/headscale/headscale/config/config.yml
Die Server Url:
server_url: https://headscale.private.com
bitte anpassen.
Die Base Doamin:
base_domain: headscale.private.com
bitte anpassen.
3.4 Container starten
docker compose -f /opt/containers/headscale/docker-compose.yml up -d
4. Einrichtung von Headscale
Öffnet nun:
https://headscale.private.com/web
Hier solltest du nun im WebUI von Headscale ankommen und ein API Fehler taucht auf.
Wir gehen nochmal ins hauptverzeichnis rein.
cd /opt/containers/headscale/
Jetzt erstellen wir uns unseren API-Token.
docker compose exec headscale headscale apikeys create
Kopiere den API Token unter Settings in das Feld Headscale API Key und klicke einmal Test Server.
Jetzt erstellen wir erstmal einen User. Unter dem Punkt User, Add User.
Jetzt kann man sich den Tailscale Client von der offiziell Tailscale Seite downloaden.
Für Linux zB.
curl -fsSL https://tailscale.com/install.sh | sh
Die Client Software ist Open Source. Die Verbindung via Windows oder Linux ist sehr einfach und mit einem Befehl zu realiseren.
Öffnet diesen Link (entsprechend angepasst auf deine Domain)
https://headscale.private.com/windows
Dort steht nun:
Use Tailscale’s login command to add your profile:
tailscale login –login-server https://headscale.private.com
Haut den Befehl in die Konsole bzw. Windows-Powershell und folgt den Anweisungen im Fenster.
Dann müsstet ihr den neuen Client per SSH auf dem Headscale-Server hinzufügen, oder ihr kopiert den “mkey:bblabla” und fügt diesen im Web-UI unter “Device View”, “New Device” ein, wählt einen Benutzer und schon seid ihr mit Headscale verbunden.
Auch der Server auf dem Headscale läuft muss manuell als Client registriert werden, wenn er Teil des Netzwerkes sein soll!
4.1 Der klassische VPN: IP verschleiern
Um jetzt nicht nur einen VPN zu haben, sondern auch ein Proxy der unseren Traffic durch die entsprechende IP Routet zu erstellen, müssen wir einen der Clients zur “Exit Node” machen. Das machen wir ganz einfach über die CLI beim gewünschten Client per
tailscale set --advertise-exit-node
Jetzt sagen wir dem Betriebssystem noch das wir wirklich den Traffic weiterleiten wollen.
echo 'net.ipv4.ip_forward = 1' > /etc/sysctl.d/99-vpn.conf echo 'net.ipv6.conf.all.forwarding = 1' >> /etc/sysctl.d/99-vpn.conf sysctl -p /etc/sysctl.d/99-vpn.conf
Zu guter letzt müssen wir im WebUI noch die angegebene Route freigeben. Wählt dafür das entsprechende Device aus und drückt auf “Pending.”
5. DynDNS Setup (optional)
Wenn ihr wie ich einen ganz normalen Internetanschluss mit einer dynamischen IP habt und den Datenverkehr eures VPN über diese wechselnde IP leiten möchtet, habe ich eine einfache Lösung. Ich habe ein kleines Skript erstellt, das das Problem der wechselnden IP-Adressen behebt.
Das Bashscript sollte auch per git clone schon vorhanden sein.
#!/bin/bash DDNS_DOMAIN="public.com" CADDYFILE="/opt/containers/headscale/caddy/Caddyfile" # Erhalte die aktuelle IP-Adresse von der DDNS-Domain CURRENT_IP=$(dig +short $DDNS_DOMAIN) # Prüfe, ob die aktuelle IP-Adresse gültig ist if [[ -z "$CURRENT_IP" ]]; then echo "Fehler: Konnte die IP-Adresse von $DDNS_DOMAIN nicht ermitteln." exit 1 fi # Ersetze den Platzhalter im Caddyfile durch die aktuelle IP-Adresse sed -i "s/CURRENT_IP_PLACEHOLDER/$CURRENT_IP/" $CADDYFILE # Starte Caddy neu, um die Änderungen zu übernehmen docker compose -f /opt/containers/headscale/docker-compose.yml restart caddy # Füge den Platzhalter zurück, um zukünftige Ersetzungen zu ermöglichen sed -i "s/$CURRENT_IP/CURRENT_IP_PLACEHOLDER/" $CADDYFILE
Ihr müsst nur bei DDNS_DOMAIN=”euredyndnsdomain.de” eintragen.
Dann machen wir es noch ausführbar.
chmod +x /opt/containers/headscale/ip.sh
Und erstellen einen Cronjob, damit es automatisch die IP aktualisiert.
crontab -e
Am Ende der Datei fügst du folgende Zeile ein:
0 4 * * * /opt/containers/headscale/ip.sh
5.1 Troubleshooting
Wenn ihr das Setup mit DynDNS und diesem Skript verwendet, müsst ihr Folgendes beachten: Nach einem Neustart eures Stacks solltet ihr das ip.sh
-Skript erneut ausführen. Andernfalls wird der Reverse Proxy nicht richtig funktionieren.
Quellen:
https://github.com/juanfont/headscale
https://blog.gurucomputing.com.au/Smart%20VPNS%20with%20Headscale/Setting%20up%20Headscale
Hi, vielen Dank für die Anleitung, die mich schon viel weiter bringt. Tatsächlich fand ich das Vorwort am interessantesten 🙂
ich bin mir allerdings nicht ganz sicher, ob ich dabei alles richtig verstanden habe oder die richtigen Schlüsse daraus ziehe.
Mein Szenario wäre/n ein oder mehrere Dienste im Heimnetz, den/die ich nur per VPN freigeben will, ohne offene Ports. Lass es mal ne Nextcloud und ein Paperless oder sowas sein.
Brauche ich Traefik/Crowdsec dann zwingend oder erst, wenn ich sonst mehrere Dienste auf dem 443 bzw. dem Tailscale laufen hätte und die daher in Domains organisiere?
Oder genügt auch dann die SSL-Zuweisung durch Headscale und Traefik/Crowdsec kommt erst dazu, wenn (bzw. ist schon vorhanden, weil) andere Dienste neben dem VPN über den forwardeten 443 aus dem Internet erreichbar sind?
Läuft bei Dir dann Headscale “draußen” und stellt den Eingang zum Tunnel bereit und Traefik läuft auf einem Server innen über Portforwarding oder laufen Traefik und Headscale beide “draußen” und leiten nur auf öffentliche Ports (Traefik) oder eben ein VPN (headscale)?
Und wenn headscale “draußen” läuft, läuft natürlich ein weiterer tailscale-Container “drinnen”, der dann Exit Node ist und auch diese DynDNS-Geschichte macht? Da ist mir auch noch nicht ganz klar, wie ich den Traffic von dem aus dann in ein Docker-Netz route (in dem ja die Dienste laufen, um die es eigentlich geht).
Kannst Du dazu noch etwas im Artikel ergänzen oder zumindest kommentieren?
Ich glaube, das interessiert echt viele Leute, die keine Ports auf ihr Homelab weiterleiten wollen (und ich hoffe, ich bin nicht der einzige, der das nicht sofort versteht).
Vielen lieben Dank im Voraus!
L.B.Q.R.
Wo gibts denn den1€ Server dauerhaft? VG
Sehr cool, ich nutze Tailscail schon lange! Das probiere ich auf jeden Fall aus!
Ein Tipp noch für DynDND : ipv64.net das ist super leicht umzusetzen und Klapp wunderbar!