Tailscale unterstützt standardmäßig nur die Anmeldung über bestehende SSO-Anbieter wie Google oder GitHub. Einen eigenen Tailscale-Account kann man nicht anlegen.
Wenn du aber lieber deine eigene Lösung nutzen willst, kannst du Zitadel als Identity Provider einrichten. Dazu wird OIDC (OpenID Connect) verwendet.
Das Problem: Zitadel liefert von Haus aus keinen sogenannten Webfinger mit, den Tailscale aber für die Registrierung benötigt.
Zum Glück können wir diesen Webfinger leicht selbst erstellen und über unseren Caddy Reverse Proxy bereitstellen. Das ist eine Erweiterung unser Netbird mit Zitadel als Identity Provider installation.

Für dieses Setup nutzen wir:
- Caddy als Reverse Proxy
- Docker Compose für die Container-Verwaltung
- Zitadel als Identity Provider
Was ist Webfinger?
Webfinger ist ein Standard, mit dem Metadaten zu einer Identität im Internet abrufbar gemacht werden – maschinenlesbar in Form einer kleinen JSON-Datei.
Man kann sich das wie eine digitale Visitenkarte vorstellen, die einem Dienst (z. B. Tailscale) zeigt: „Für diesen Benutzer ist der Login bei diesem Identity Provider zuständig.“
Ein Beispiel für den benötigten Webfinger:
{
"subject": "acct:[email protected]",
"links": [
{
"rel": "http://openid.net/specs/connect/1.0/issuer",
"href": "https://nb.linuxbase.io"
}
]
}
Wichtig: Passe [email protected]
und https://nb.linuxbase.io
an deine eigene Domain und Subdomain an.
Der Webfinger ist eine einfache JSON Datei, die quasi die Adresse angibt, bei der sich Authentifiziert werden muss.
Caddy: Webfinger anzeigen
Um den Webfinger nun zu integrieren, müssen wir das Caddyfile, also die Config Datei des Caddy Reverse Proxys bearbeiten und natürlich die Datei selbst anlegen.
Schritt 1: Webfinger-Datei anlegen
Lege in deinem Netbird-Ordner einen Unterordner webfinger
an und erstelle dort die Datei webfinger
(ohne .json
Endung):
cd netbird
mkdir webfinger
vi webfinger
Füge den JSON-Inhalt (siehe oben) ein, speichere die Datei mit ESC
→ :wq
.
Schritt 2: Caddyfile anpassen
Jetzt öffnen wir die Konfigurationsdatei von Caddy:
Dann öffnen wir die Caddyfile Datei:
cd ..
vi Caddyfile
Diese Datei sollte ungefähr so ausehen:
{
debug
servers :80,:443 {
protocols h1 h2c h2 h3
}
}
(security_headers) {
header * {
# enable HSTS
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#strict-transport-security-hsts
# NOTE: Read carefully how this header works before using it.
# If the HSTS header is misconfigured or if there is a problem with
# the SSL/TLS certificate being used, legitimate users might be unable
# to access the website. For example, if the HSTS header is set to a
# very long duration and the SSL/TLS certificate expires or is revoked,
# legitimate users might be unable to access the website until
# the HSTS header duration has expired.
# The recommended value for the max-age is 2 year (63072000 seconds).
# But we are using 1 hour (3600 seconds) for testing purposes
# and ensure that the website is working properly before setting
# to two years.
Strict-Transport-Security "max-age=3600; includeSubDomains; preload"
# disable clients from sniffing the media type
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-content-type-options
X-Content-Type-Options "nosniff"
# clickjacking protection
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-frame-options
X-Frame-Options "SAMEORIGIN"
# xss protection
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-xss-protection
X-XSS-Protection "1; mode=block"
# Remove -Server header, which is an information leak
# Remove Caddy from Headers
-Server
# keep referrer data off of HTTP connections
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#referrer-policy
Referrer-Policy strict-origin-when-cross-origin
}
}
:80, nb.linxubase.io:443 {
import security_headers
# relay
reverse_proxy /relay* relay:80
# Signal
reverse_proxy /signalexchange.SignalExchange/* h2c://signal:10000
# Management
reverse_proxy /api/* management:80
reverse_proxy /management.ManagementService/* h2c://management:80
# Zitadel
reverse_proxy /zitadel.admin.v1.AdminService/* h2c://zitadel:8080
reverse_proxy /admin/v1/* h2c://zitadel:8080
reverse_proxy /zitadel.auth.v1.AuthService/* h2c://zitadel:8080
reverse_proxy /auth/v1/* h2c://zitadel:8080
reverse_proxy /zitadel.management.v1.ManagementService/* h2c://zitadel:8080
reverse_proxy /management/v1/* h2c://zitadel:8080
reverse_proxy /zitadel.system.v1.SystemService/* h2c://zitadel:8080
reverse_proxy /system/v1/* h2c://zitadel:8080
reverse_proxy /assets/v1/* h2c://zitadel:8080
reverse_proxy /ui/* h2c://zitadel:8080
reverse_proxy /oidc/v1/* h2c://zitadel:8080
reverse_proxy /saml/v2/* h2c://zitadel:8080
reverse_proxy /oauth/v2/* h2c://zitadel:8080
reverse_proxy /.well-known/openid-configuration h2c://zitadel:8080
reverse_proxy /openapi/* h2c://zitadel:8080
reverse_proxy /debug/* h2c://zitadel:8080
reverse_proxy /device/* h2c://zitadel:8080
reverse_proxy /device h2c://zitadel:8080
reverse_proxy /zitadel.user.v2.UserService/* h2c://zitadel:8080
# Dashboard
reverse_proxy /* dashboard:80
}
Füge unter folgendem Block:
:80, nb.linxuxbase.io:443 {
import security_headers
diesen Abschnitt hinzu:
handle_path /.well-known/webfinger {
root * /etc/caddy/webfinger
try_files /webfinger
file_server
}
Das ganze sieht dann so aus:
{
debug
servers :80,:443 {
protocols h1 h2c h2 h3
}
}
(security_headers) {
header * {
# enable HSTS
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#strict-transport-security-hsts
# NOTE: Read carefully how this header works before using it.
# If the HSTS header is misconfigured or if there is a problem with
# the SSL/TLS certificate being used, legitimate users might be unable
# to access the website. For example, if the HSTS header is set to a
# very long duration and the SSL/TLS certificate expires or is revoked,
# legitimate users might be unable to access the website until
# the HSTS header duration has expired.
# The recommended value for the max-age is 2 year (63072000 seconds).
# But we are using 1 hour (3600 seconds) for testing purposes
# and ensure that the website is working properly before setting
# to two years.
Strict-Transport-Security "max-age=3600; includeSubDomains; preload"
# disable clients from sniffing the media type
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-content-type-options
X-Content-Type-Options "nosniff"
# clickjacking protection
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-frame-options
X-Frame-Options "SAMEORIGIN"
# xss protection
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-xss-protection
X-XSS-Protection "1; mode=block"
# Remove -Server header, which is an information leak
# Remove Caddy from Headers
-Server
# keep referrer data off of HTTP connections
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#referrer-policy
Referrer-Policy strict-origin-when-cross-origin
}
}
:80, nb.linxuxbase.io:443 {
import security_headers
handle_path /.well-known/webfinger {
root * /etc/caddy/webfinger
try_files /webfinger
file_server
}
# relay
reverse_proxy /relay* relay:80
# Signal
reverse_proxy /signalexchange.SignalExchange/* h2c://signal:10000
# Management
reverse_proxy /api/* management:80
reverse_proxy /management.ManagementService/* h2c://management:80
# Zitadel
reverse_proxy /zitadel.admin.v1.AdminService/* h2c://zitadel:8080
reverse_proxy /admin/v1/* h2c://zitadel:8080
reverse_proxy /zitadel.auth.v1.AuthService/* h2c://zitadel:8080
reverse_proxy /auth/v1/* h2c://zitadel:8080
reverse_proxy /zitadel.management.v1.ManagementService/* h2c://zitadel:8080
reverse_proxy /management/v1/* h2c://zitadel:8080
reverse_proxy /zitadel.system.v1.SystemService/* h2c://zitadel:8080
reverse_proxy /system/v1/* h2c://zitadel:8080
reverse_proxy /assets/v1/* h2c://zitadel:8080
reverse_proxy /ui/* h2c://zitadel:8080
reverse_proxy /oidc/v1/* h2c://zitadel:8080
reverse_proxy /saml/v2/* h2c://zitadel:8080
reverse_proxy /oauth/v2/* h2c://zitadel:8080
reverse_proxy /.well-known/openid-configuration h2c://zitadel:8080
reverse_proxy /openapi/* h2c://zitadel:8080
reverse_proxy /debug/* h2c://zitadel:8080
reverse_proxy /device/* h2c://zitadel:8080
reverse_proxy /device h2c://zitadel:8080
reverse_proxy /zitadel.user.v2.UserService/* h2c://zitadel:8080
# Dashboard
reverse_proxy /* dashboard:80
}
Damit wird die Webfinger-Datei unter https://deinedomain.de/.well-known/webfinger
bereitgestellt.
Das Argument try_files /webfinger
ist wichtig, damit nur die Datei ausgeliefert wird. Andere Pfade unter .well-known/
(z. B. für OIDC) werden weiterhin von Zitadel genutzt.
Schritt 3: Caddy neu starten
Damit die Änderungen aktiv werden, musst du den Caddy-Container neu starten.
Liste zuerst die laufenden Container:
docker ps
root@app-a0001:~/netbird# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1fa0ae6125d8 ghcr.io/zitadel/zitadel:v2.64.1 "/app/zitadel start-…" 2 days ago Up 2 days netbird-zitadel-1
1af48ac03c8b netbirdio/relay:latest "/go/bin/netbird-rel…" 2 days ago Up 2 days netbird-relay-1
e18d05216c4b caddy "caddy run --config …" 2 days ago Up 2 days 0.0.0.0:80->80/tcp, [::]:80->80/tcp, 0.0.0.0:443->443/tcp, [::]:443->443/tcp, 0.0.0.0:443->443/udp, [::]:443->443/udp, 2019/tcp netbird-caddy-1
a03d44931c0f netbirdio/management:latest "/go/bin/netbird-mgm…" 2 days ago Up 2 days netbird-management-1
f3c0d42b0903 netbirdio/dashboard:latest "/usr/bin/supervisor…" 2 days ago Up 2 days 80/tcp, 443/tcp netbird-dashboard-1
00a087562f13 coturn/coturn "docker-entrypoint.s…" 2 days ago Up 2 days netbird-coturn-1
4f4209763133 postgres:16-alpine "docker-entrypoint.s…" 2 days ago Up 2 days (healthy) 5432/tcp netbird-zdb-1
584ec1ffced4 netbirdio/signal:latest "/go/bin/netbird-sig…" 2 days ago Up 2 days netbird-signal-1
Suche die Container-ID von Caddy (z. B. e18d05216c4b
) und starte ihn neu:
docker compose restart e18d05216c4b
Hinweis: Der Befehl muss im Verzeichnis mit deiner docker-compose.yml
ausgeführt werden.
Nach wenigen Sekunden sollte die Datei erreichbar sein:
https://deinedomain.de/.well-known/webfinger
Schritt 4: Tailscale mit Zitadel verbinden
Nun können wir Tailscale mit Zitadel verwenden.
Gehe dazu auf die Tailscale-Seite → „Get Started“ → „Sign up with OIDC“.
Trage hier deinen Benutzer in der Form ein:
In meinem Fall ist es der Admin User:
Damit authentifiziert sich Tailscale über deinen Zitadel-Server.
Zusammenfassung
- Webfinger-Datei erstellt
- Caddy konfiguriert
- Caddy-Container neu gestartet
- Tailscale mit Zitadel als OIDC Provider verbunden
Jetzt kannst du Tailscale mit deiner eigenen Zitadel-Installation nutzen, ganz ohne Google- oder GitHub-Account.

Hi, ich bin gelernter Fachinformatiker für Systemintegration und arbeite als Linux-Systemadministrator. Zuhause fühle ich mich vor allem bei Proxmox, Ubuntu und Debian – die drei begleiten mich sowohl im Job als auch privat auf meinen Servern. Wenn ich nicht gerade an der Shell hänge, tüftle ich gern an neuen Projekten und meinem Smart Home (Hometinker.io), teste spannende Open-Source-Tools oder optimiere bestehende Setups. Auf linuxbase.io möchte ich meine Erfahrungen teilen, damit andere nicht jede Stolperfalle selbst mitnehmen müssen.