← Übersicht  ·  Skripte & Dateien  ·  cloud-host · Netz

Tailscale

Ein privates WireGuard-Mesh über Laptop, Hetzner-Server und Telefon – jedes Gerät bekommt eine feste 100.x-Adresse und einen DNS-Namen, Verbindungen gehen direkt und verschlüsselt. Am Ende ist am Server gar kein Port offen: SSH (Secure Shell), kubectl, Postgres – alles nur noch übers Tailnet. Mit Auth-Keys für cloud-init, Tailscale SSH ohne Schlüsselverwaltung, ACLs, Subnet-Router und Exit-Node.

Stand: 30. August 2026 Tailscale Free (3 Nutzer, 100 Geräte) 0 €

Platzhalter einsetzen – nur im Browser

Voraussetzung: ein eingerichteter Server – einmaliger Aufbau in Server-Basis, wegwerfen & identisch neu in Snapshot & Restore. Was die Bausteine hier kosten: Kosten & Budget.

Das Prinzip

Tailscale baut ein Mesh: jedes Gerät verbindet sich direkt mit jedem anderen (WireGuard). Ein Koordinationsserver vermittelt nur die Schlüssel und hilft beim NAT-Durchstich – deine Daten laufen nicht über Tailscale.

AnmeldenJedes Gerät loggt sich per Google/GitHub/… ein → bekommt eine feste 100.x.y.z + Namen
Koord.Tailscale-Control tauscht die öffentlichen WireGuard-Keys aus. Private Keys verlassen das Gerät nie.
DirektLaptop ↔ Server: verschlüsselter WireGuard-Tunnel, möglichst direkt (sonst über einen DERP-Relay)
FirewallDer Server macht nur ausgehende Verbindungen auf. In der Hetzner-Firewall kann alles zu.
ACLEine zentrale Policy sagt, welches Gerät welchen Port auf welchem anderen erreichen darf
  • Feste Adressen & Namen – löst das „IP (Internet Protocol) ändert sich beim Neuaufbau“-Problem für alles Interne (SSH, kubectl, DB). Nur die öffentliche Domain braucht weiter Dynamic DNS (Domain Name System).
  • Kein offener Port – nichts zum Scannen, kein Brute-Force auf SSH.
  • Free-Plan reicht: 3 Nutzer, 100 Geräte, alle Features (MagicDNS, SSH, ACLs, Subnet-Router, Exit-Node, Funnel).
  • Wer nicht von Tailscale Inc. abhängen will: Headscale (eigener Koordinationsserver).

1 Konto & erstes Gerät (Laptop)

  1. login.tailscale.com → mit Google/GitHub/Microsoft/E-Mail anmelden. Das legt dein Tailnet an.
  2. Windows-Client von tailscale.com/download installieren → anmelden → der Laptop erscheint im Admin-Panel unter Machines.
  3. Admin-Konsole DNSMagicDNS aktivieren (Schalter oben). Notiere den Tailnet-Namen, z. B. tailXXXXXX.ts.net.
Laptop · PowerShell · Test
tailscale status        # zeigt deine Geraete + Adressen
tailscale ip -4         # deine 100.x-Adresse

2 Server ins Tailnet

Auf einem bestehenden Server von Hand – für neue Server über cloud-init (Abschnitt 4).

Server · als root
curl -fsSL https://tailscale.com/install.sh | sh

# interaktiv: druckt eine Login-URL, im Browser bestaetigen
tailscale up --ssh --hostname podman-host

tailscale status
tailscale ip -4
--ssh

Aktiviert Tailscale SSH auf diesem Node – du kannst dich dann ohne eigenen SSH-Key einloggen, die ACL regelt den Zugang. Ohne --ssh läuft SSH weiter klassisch, nur eben über die Tailnet-Adresse.

Schlüssel-Ablauf

Geräte-Keys laufen nach 180 Tagen ab – ein Server fällt dann still aus dem Netz. Für Server: im Admin-Panel beim Gerät Disable key expiry.

Immutables OS (Fedora / RHEL / CentOS Stream CoreOS)

Auf CoreOS (z. B. dem OKD-Single-Node) scheitert install.sh: kein dnf, keine Paketquellen, /usr schreibgeschützt. Der Installer wählt trotzdem den CentOS-Pfad (Error: There are no enabled repositories). Stattdessen die statischen Binärdateien + eine systemd-Unit – kein OS-Layer, kein Container.

Server (CoreOS) · als root / sudo
# neueste Version ermitteln (der Autoindex hat kein stabiles Format -> JSON-Endpoint)
VER=$(curl -fsSL 'https://pkgs.tailscale.com/stable/?mode=json' | grep -oE '"Version":"[0-9.]+"' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')

curl -fsSL -o /tmp/ts.tgz "https://pkgs.tailscale.com/stable/tailscale_${VER}_amd64.tgz"
sudo tar xzf /tmp/ts.tgz -C /tmp
cd /tmp/tailscale_${VER}_amd64

# /usr/local/bin ist auf CoreOS beschreibbar (-> /var/usrlocal/bin)
sudo install -m 0755 tailscale tailscaled /usr/local/bin/
sudo sed 's#/usr/sbin/tailscaled#/usr/local/bin/tailscaled#' systemd/tailscaled.service \
  | sudo tee /etc/systemd/system/tailscaled.service >/dev/null
sudo cp systemd/tailscaled.defaults /etc/default/tailscaled

# sudo hat /usr/local/bin auf minimalem CoreOS nicht im PATH -> ergaenzen
echo 'Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"' \
  | sudo tee /etc/sudoers.d/10-localpath >/dev/null

sudo systemctl daemon-reload
sudo systemctl enable --now tailscaled
sudo /usr/local/bin/tailscale up --ssh --hostname okd-sno   # druckt eine Login-URL
SELinux (Security-Enhanced Linux) & Tailscale SSH

Auf SCOS (CentOS Stream CoreOS)/FCOS (SELinux enforcing) meldet tailscale up: SELinux is enabled; Tailscale SSH may not work. Das --ssh-Feature (Login ganz ohne Key) braucht ein SELinux-Policy-Modul, das die statische Installation nicht mitbringt. Klassisches ssh <user>@name.ts.net (key-basiert, Port 22 übers Tailnet) funktioniert unabhängig davon – und reicht für „kein offener Port“.

Cluster-OS-Update

Ein rpm-ostree-/Cluster-Update (bei OKD: oc adm upgrade) rollt /usr neu aus – die Binärdateien in /usr/local/bin (→ /var/usrlocal) und die Unit in /etc bleiben aber – nur ein tailscaled-Binary-Update macht man dann von Hand (dieselben Zeilen). Reboots übersteht es unverändert.

Alternativen: rpm-ostree install tailscale (Paket layern, Reboot, bei OKD außerhalb des Machine-Config-Operators – auf einem Lab-Single-Node meist ok) oder Tailscale als podman-Container mit --network=host --cap-add=NET_ADMIN,NET_RAW (update-resistent, aber neben OVN-Kubernetes mit Bedacht testen).

3 Auth-Keys & cloud-init

Ein Auth-Key meldet einen neuen Server unbeaufsichtigt an – ideal für den Snapshot/Restore-Workflow.

  1. Admin-Konsole Settings Keys Generate auth key
  2. Reusable (mehrfach nutzbar), Ephemeral (Gerät wird automatisch entfernt, sobald es länger offline ist – perfekt für Wegwerf-Server), Tags: tag:server. Ablauf max. 90 Tage.
  3. Key kopieren (tskey-auth-…) – wird nur einmal gezeigt.
Datei · cloud-init.yaml (Auszug)
runcmd:
  - curl -fsSL https://tailscale.com/install.sh | sh
  - tailscale up --ssh --authkey tskey-auth-XXXX --hostname podman-host --advertise-tags=tag:server
  - # danach Port 22 in der Firewall ueberfluessig (Abschnitt 6)
Ephemeral + tagged Key im Repo

Ein reusable + ephemeral + tagged Key in der cloud-init: jeder cloud.ps1 up meldet den Server automatisch an, jedes down lässt ihn (nach kurzer Zeit) von selbst aus dem Tailnet verschwinden – keine Karteileichen. Der Key ist durch die Tags eingeschränkt. Trotzdem nicht öffentlich committen.

4 MagicDNS

Statt 100.x-Adressen zu merken: jeder Node ist unter <name>.tailXXXXXX.ts.net erreichbar – von jedem Gerät im Tailnet, ohne DNS-Einträge.

Laptop · ab jetzt überall so
ssh root@podman-host.tailXXXXXX.ts.net
kubectl --server https://podman-host.tailXXXXXX.ts.net:6443 get nodes
psql -h podman-host.tailXXXXXX.ts.net -U app
In den Skripten

In cloud.ps1 / snapshot.ps1 das SSH-Ziel auf den MagicDNS-Namen setzen (statt hcloud server ip). Dann ist der cf-dns.ps1-Hook nur noch für die öffentliche Domain nötig – alles Interne ist stabil.

5 Ports zu

Der eigentliche Gewinn: nichts mehr von außen erreichbar.

Laptop · PowerShell
# Firewall des Servers: alle Inbound-Regeln loeschen (leere Regelliste = alles zu)
'[]' | Out-File -Encoding ascii fw-empty.json
hcloud firewall replace-rules podman-host-fw --rules-file fw-empty.json

# neue Server gleich ohne oeffentliche IPv4 anlegen (spart 0,50 EUR/Monat)
# hcloud server create ... --without-ipv4    (Server nutzt IPv6 raus; Tailscale klappt trotzdem)
Reihenfolge

Erst prüfen, dass Tailscale-SSH wirklich geht (ssh root@name.ts.net), dann Port 22 schließen. Sonst sperrst du dich aus – Rettung ginge nur über die Hetzner-Konsole (Rescue/VNC).

Ergebnis

Port-Scan des Servers zeigt nichts. Kein SSH-Brute-Force in den Logs mehr. Öffentliche Dienste (Web) laufen weiter – entweder über einen Cloudflare Tunnel oder über Tailscale Funnel.

6 Tailscale SSH

Kein authorized_keys, kein Key-Rollout: die ACL entscheidet, wer sich als welcher Unix-User einloggen darf. Tailscale terminiert die SSH-Verbindung selbst.

Server · einschalten
tailscale set --ssh            # oder beim ersten "tailscale up --ssh"
Laptop
ssh root@podman-host.tailXXXXXX.ts.net
# oder explizit ueber den Tailscale-Client:
tailscale ssh root@podman-host
  • Zugang wird bei jeder Verbindung gegen die ACL geprüft – Gerät entfernt/Key abgelaufen → sofort raus.
  • Optional Check-Modus in der ACL: erzwingt eine frische Re-Auth im Browser vor dem Login (2FA für SSH).
  • Session-Recording (Enterprise) protokolliert SSH-Sitzungen – für den Privatgebrauch meist nicht nötig.
  • Klassisches SSH bleibt möglich – du brauchst Tailscale-SSH nicht, wenn du deine Keys ohnehin verwaltest. Der Vorteil ist „keine Keys mehr“.

7 ACLs: wer darf wohin

Standardmässig darf jedes Gerät jedes andere auf allen Ports erreichen. Das ist bequem zum Start und gehört danach eingeschränkt.

Admin-Konsole · Access Controls (HuJSON)
{
  "tagOwners": {
    "tag:server": ["autogroup:admin"]
  },
  "acls": [
    // jeder im Tailnet darf Server per SSH/kubectl/Postgres
    { "action": "accept",
      "src": ["autogroup:member"],
      "dst": ["tag:server:22,6443,5432"] },

    // Server duerfen untereinander alles (etcd, Cluster-Traffic)
    { "action": "accept", "src": ["tag:server"], "dst": ["tag:server:*"] }
  ],
  "ssh": [
    { "action": "accept",
      "src": ["autogroup:member"],
      "dst": ["tag:server"],
      "users": ["root", "deploy"] }
  ]
}
Begriffbedeutet
tag:serverRolle für Maschinen ohne eigenen Nutzer – per Auth-Key vergeben, Key-Ablauf egal
autogroup:memberalle echten Nutzer deines Tailnets (nicht die getaggten Maschinen)
autogroup:adminTailnet-Admins – dürfen tag:server vergeben
dst: "…:22,6443"Zielports, * = alle
ssh-Blockseparat von acls: regelt Tailscale-SSH-Logins (welcher Unix-User)

Die ACL wird im Panel getestet (Preview zeigt, wer worauf käme) und versioniert – oder per GitOps aus einem Repo (gitops-acl-action).

8 Subnet-Router & Exit-Node

Subnet-Router – ein ganzes Netz erreichbar machen

Der Server gibt Zugriff auf ein Netz, in dem er selbst steht – z. B. das Docker-internal-Netz oder ein privates Hetzner-Netzwerk. Andere Geräte müssen dafür nichts installieren.

Server
echo 'net.ipv4.ip_forward=1' > /etc/sysctl.d/99-tailscale.conf && sysctl -p /etc/sysctl.d/99-tailscale.conf
tailscale set --advertise-routes=10.0.0.0/24
# dann im Admin-Panel: Machine ... > Edit route settings > Route freigeben

Exit-Node – gesamter Traffic über den Server

Der Server wird zum VPN-Ausgang: das Telefon im öffentlichen WLAN surft über die Server-IP. Traffic zählt dann auf das Server-Inklusivvolumen (20 TB).

Server
tailscale set --advertise-exit-node
# Admin-Panel: Route freigeben. Auf dem Client dann Exit-Node auswaehlen.

9 Betrieb

  • Geräte-Freigabe: Settings Device approval einschalten → neue Geräte müssen von dir bestätigt werden, bevor sie ins Netz dürfen.
  • Key-Ablauf für Server deaktivieren, für persönliche Geräte an lassen.
  • Updates: tailscale update (oder Paketquelle + apt upgrade). tailscale set --auto-update für Server, die durchlaufen.
  • Diagnose: tailscale status, tailscale ping <name> (zeigt direkt vs. über Relay), tailscale netcheck.
  • Tailnet Lock (fortgeschritten): kryptografischer Schutz davor, dass ein kompromittierter Koordinationsserver heimlich Geräte hinzufügt. Für die meisten Setups Überdimensionierung.
  • Abmelden / entfernen: tailscale logout am Gerät bzw. Delete im Panel. Ephemeral-Node verschwinden von selbst.

+ Funnel & Serve

Serve = einen Dienst innerhalb des Tailnets als HTTPS (Hypertext Transfer Protocol Secure) veröffentlichen. Funnel = denselben Dienst ins öffentliche Internet stellen, mit automatischem TLS (Transport Layer Security) auf einer *.ts.net-Adresse – ohne offenen Port, ohne Cloudflare.

Server
# internen Dienst auf :3000 im Tailnet unter https://name.ts.net erreichbar machen
tailscale serve --bg 3000

# denselben Dienst oeffentlich (vorher in der ACL "funnel" fuer den Node erlauben)
tailscale funnel --bg 3000
tailscale funnel status

Funnel-URL ist name.tailXXXXXX.ts.netnicht deine eigene Domain. Für blog.seb4u.com mit eigener URL (Uniform Resource Locator) bleibt der Cloudflare Tunnel die bessere Wahl. Funnel ist gut für schnelle Demos und Webhooks.

+ Headscale (selbst hosten)

Headscale ist eine quelloffene Neuimplementierung des Koordinationsservers. Du betreibst ihn selbst (ein kleiner Container), die normalen Tailscale-Clients verbinden sich per tailscale up --login-server https://headscale.deine-domain.

  • Dafür: keine Abhängigkeit von Tailscale Inc., keine Nutzer-/Gerätelimits, alles auf deiner Infrastruktur.
  • Dagegen: ACL-Editor, MagicDNS-Komfort, Funnel, mobile Apps mit Custom-Login – teils eingeschränkt oder Handarbeit. Und der Koordinationsserver wird selbst zum Single Point of Failure, den du absichern musst.
  • Für den Anfang: Tailscale-Free nehmen, Headscale im Hinterkopf behalten.

+ Tailscale vs Cloudflare Tunnel

TailscaleCloudflare Tunnel + Access
Zweckprivates Netz zwischen deinen Gerätenöffentliche Website hinter Login
Zugriff durch Fremdenein (Funnel als Ausnahme)ja, nach Access-Login
Eigene Domain….ts.net (Funnel)ja (blog.seb4u.com)
SSH / kubectl / DBdirekt, idealumständlich (SSH per cloudflared access)
Offener Portkeinerkeiner

Zusammen: Tailscale fürs Management (SSH, kubectl, DB, Backups) + Cloudflare Tunnel für das, was die Welt sehen soll. Beide brauchen keinen offenen Port.