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.
100.x.y.z + Namen- 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)
- login.tailscale.com → mit Google/GitHub/Microsoft/E-Mail anmelden. Das legt dein Tailnet an.
- Windows-Client von tailscale.com/download installieren → anmelden → der Laptop erscheint im Admin-Panel unter Machines.
- →
MagicDNS aktivieren (Schalter oben). Notiere den Tailnet-Namen, z. B.
tailXXXXXX.ts.net.
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).
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
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.
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 .
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.
# 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
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“.
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.
- 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. - Key kopieren (
tskey-auth-…) – wird nur einmal gezeigt.
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)
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.
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 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 aussen erreichbar.
# 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)
Erst prüfen, dass Tailscale-SSH wirklich geht (ssh root@name.ts.net),
dann Port 22 schliessen. Sonst sperrst du dich aus – Rettung ginge nur
über die Hetzner-Konsole (Rescue/VNC).
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.
tailscale set --ssh # oder beim ersten "tailscale up --ssh"
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.
{
"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"] }
]
}
| Begriff | bedeutet |
|---|---|
tag:server | Rolle für Maschinen ohne eigenen Nutzer – per Auth-Key vergeben, Key-Ablauf egal |
autogroup:member | alle echten Nutzer deines Tailnets (nicht die getaggten Maschinen) |
autogroup:admin | Tailnet-Admins – dürfen tag:server vergeben |
dst: "…:22,6443" | Zielports, * = alle |
ssh-Block | separat 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.
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).
tailscale set --advertise-exit-node
# Admin-Panel: Route freigeben. Auf dem Client dann Exit-Node auswaehlen.
9 Betrieb
- Geräte-Freigabe: 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-updatefü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 logoutam 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.
# 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.net –
nicht 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
| Tailscale | Cloudflare Tunnel + Access | |
|---|---|---|
| Zweck | privates Netz zwischen deinen Geräten | öffentliche Website hinter Login |
| Zugriff durch Fremde | nein (Funnel als Ausnahme) | ja, nach Access-Login |
| Eigene Domain | ….ts.net (Funnel) | ja (blog.seb4u.com) |
| SSH / kubectl / DB | direkt, ideal | umständlich (SSH per cloudflared access) |
| Offener Port | keiner | keiner |
Zusammen: Tailscale fürs Management (SSH, kubectl, DB, Backups) + Cloudflare Tunnel für das, was die Welt sehen soll. Beide brauchen keinen offenen Port.