# Tailscale2Headscale # Headscale + Tailscale – Homelab Migration > ⚠️ **Version publique / anonymisée** > Tous les noms de domaine, IP et clés ont été remplacés par des **placeholders**. > Remplace toutes les valeurs `<...>` par les tiennes avant d’utiliser ce document. --- ## 1. Objectif Mettre en place un **contrôle Tailscale auto‑hébergé** avec **Headscale** pour remplacer le control plane Tailscale SaaS, tout en : - gardant les clients Tailscale classiques (Linux, macOS, Android, Home Assistant, etc.) ; - centralisant la gestion sur un serveur Headscale accessible en HTTPS ; - évitant les bugs classiques (mauvais port, mauvais `--login-server`, clés expirées, etc.). Ce README récapitule : - l’architecture retenue ; - la configuration Headscale (service + Web admin + reverse proxy) ; - la configuration des clients Tailscale ; - les problèmes rencontrés et les solutions pour ne plus tomber dedans. --- ## 2. Architecture globale ### 2.1. Composants - **Serveur Headscale** - Type : LXC / VM Linux (Debian‑like) - Rôle : serveur `headscale` + Web UI (headscale‑admin) - IP locale : `` (ex : `192.168.1.xxx`) - Nom interne : `` (ex : `headscale`) - **Reverse proxy (facultatif mais recommandé)** - Logiciel : `caddy` (ou nginx/traefik) - Port HTTP interne Headscale : `8080` - Port HTTPS public : `443` - Nom de domaine : `` (ex : `headscale.example.com`) - Certificats : Let’s Encrypt ou équivalent - **Clients Tailscale** - Serveur Proxmox VE : `` - Proxmox Backup Server : `` - NAS (OMV ou autre) : `` - Containers / VM : ``, ``, etc. - Home Assistant : Add-on Tailscale - macOS : client Tailscale officiel - Android : app Tailscale officielle Tout ce petit monde parle au **contrôle plane** : `https://`. --- ## 3. Installation de Headscale (serveur) ### 3.1. Pré‑requis Sur la machine Headscale (LXC/VM) : ```bash apt update apt install -y headscale sqlite3 ca-certificates curl ``` Créer le répertoire de configuration : ```bash mkdir -p /etc/headscale ``` ### 3.2. Fichier de configuration `config.yaml` (exemple) > ⚠️ **Important :** Adapter les valeurs `server_url`, `listen_addr`, `grpc_listen_addr`, `db_path`, etc. ```yaml # /etc/headscale/config.yaml server_url: "https://" listen_addr: "0.0.0.0:8080" grpc_listen_addr: "0.0.0.0:50443" ip_prefixes: - "100.64.0.0/10" derp: server: enabled: true region_id: 999 region_code: "homelab" region_name: "Homelab DERP" paths: [] # Base SQLite (simple à gérer pour un homelab) db_type: "sqlite" db_path: "/var/lib/headscale/db.sqlite" tls_cert_path: "" tls_key_path: "" log: level: "info" dns_config: override_local_dns: false magic_dns: false ``` ### 3.3. Service systemd Le paquet Debian fournit généralement un service `headscale.service`. Vérifier : ```bash systemctl enable --now headscale systemctl status headscale --no-pager ``` --- ## 4. Web admin : headscale‑admin ### 4.1. Dossier et configuration Headscale‑admin est en général servi par un serveur web (caddy/nginx), souvent depuis : ```bash /var/www/headscale-admin ``` ou : ```bash /opt/headscale-admin ``` Cette UI se connecte à l’API Headscale (port `8080` dans notre cas). --- ## 5. Reverse proxy (Caddy) > C’est ici qu’on a eu une galère : mélange de ports `80` et `8080`. > Résultat : clients Tailscale qui pointaient sur `:8080` alors que le proxy écoutait en `:80` → 404 ou connexion impossible. ### 5.1. Exemple de config Caddy ```bash # /etc/caddy/Caddyfile { encode gzip # Proxy API Headscale (port interne 8080) reverse_proxy /api/* 127.0.0.1:8080 # UI headscale-admin statique root * /var/www/headscale-admin file_server } ``` ### 5.2. Points critiques - Le **serveur Headscale** écoute sur `:8080`. - Le **reverse proxy** écoute en **:443** (et :80 pour HTTP → redirect vers 443). - Les clients Tailscale **n’ont pas besoin du port** dans `--login-server` s’ils passent par HTTPS standard :`--login-server=https://` - Si tu décides de garder un port non standard (`:8443` par ex.), **il faut le mettre partout** : - dans `server_url` de Headscale ; - dans `--login-server=` côté clients ; - dans la config du reverse proxy. --- ## 6. Gestion des utilisateurs et clés dans Headscale ### 6.1. Créer un user ```bash headscale users create headscale users list ``` Exemple : ```bash headscale users create syleric ``` ### 6.2. Créer une preauth key ```bash headscale preauthkeys create --user --reusable --ephemeral=false ``` Exemple : ```bash headscale users list # ID | Name | Username | Email | Created # 1 | | $user | | ... headscale preauthkeys create --user 1 --reusable --ephemeral=false # -> renvoie une clé du type : 548fd8... (ne pas mettre ici en clair) ``` > 🔁 Si tu doutes d’une clé, tu peux la **regénérer** et supprimer l’ancienne. --- ## 7. Enregistrement des clients Tailscale ### 7.1. Schéma global Pour chaque machine, on fait : ```bash tailscale logout || true tailscale down || true tailscale up \ --login-server=https:// \ --auth-key= \ --accept-dns=false \ --accept-routes=false \ --reset ``` > On utilise `--accept-dns=false` pour éviter que Headscale override le DNS local du homelab, > et `--accept-routes=false` tant qu’on ne met pas en place de routes subnets poussées via Headscale. --- ## 8. Cas particulier : macOS ### 8.1. Utiliser le binaire Tailscale macOS Depuis le Mac : ```bash /Applications/Tailscale.app/Contents/MacOS/Tailscale status /Applications/Tailscale.app/Contents/MacOS/Tailscale logout || true /Applications/Tailscale.app/Contents/MacOS/Tailscale down || true ``` Puis : ```bash sudo /Applications/Tailscale.app/Contents/MacOS/Tailscale up \ --login-server=https:// \ --auth-key= \ --accept-dns=false \ --accept-routes=false \ --reset ``` Si la clé est valide et le `--login-server` accessible, tu dois voir : ```bash /Applications/Tailscale.app/Contents/MacOS/Tailscale status # 100.64.0.X macOS - ``` --- ## 9. Cas particulier : Home Assistant (add-on Tailscale) ### 9.1. Configurer l’add-on Dans la configuration de l’add-on Tailscale (interface Home Assistant) : ```yaml authkey: "" hostname: "homeassistant" login_server: "https://" userspace_networking: true # ou false selon ton besoin accept_dns: false accept_routes: false ``` Ensuite, côté Headscale, tu verras un node : ```bash headscale nodes list # ... # homeassistant | homeassistant | ... | 100.64.0.9, ... ``` Si l’expiration a été mise par erreur, tu peux la corriger : ```bash headscale nodes expire --identifier --expiry 0001-01-01T00:00:00Z ``` > ⚠️ Attention : commande `expire` sert à **fixer** une date d’expiration. > Pour ne pas expirer, il faut utiliser la valeur spéciale `0001-01-01T00:00:00Z`. --- ## 10. Android (smartphone & tablette) ### 10.1. Changer de serveur de login Sur Android, il n’y a **pas (encore)** de bouton clair dans l’UI pour changer de `login-server` vers un Headscale custom. La méthode fiable : 1. **Se déconnecter** de Tailscale sur l’app (logout). 2. **Effacer les données** de l’appli (ou désinstaller/réinstaller) pour repartir de zéro. 3. Lors du premier `tailscale up` par CLI dans Termux (si tu utilises Termux + binaire Linux), lancer : ```bash tailscale up \ --login-server=https:// \ --auth-key= \ --accept-dns=false \ --accept-routes=false \ --reset ``` 4. Côté Headscale, tu verras apparaître le nouveau node avec hostname (souvent `localhost` si tu utilises Termux) ; tu peux ensuite le renommer côté Headscale : ```bash headscale nodes rename --identifier ``` > 💡 Attention : renommer dans Headscale ne change pas le hostname système Android/Termux, mais ça rend la liste Headscale plus lisible. --- ## 11. Vérifications globales ### 11.1. Depuis Headscale ```bash headscale nodes list ``` Tu dois voir quelque chose du genre : ```text ID | Hostname | Name | IP addresses | Connected | Expired 1 | jenkins | jenkins | 100.64.0.1, fd7a:115c:a1e0::1 | online | no 2 | docker-ptr | docker-ptr | 100.64.0.2, fd7a:115c:a1e0::2 | online | no 3 | nas | nas | 100.64.0.5, fd7a:115c:a1e0::5 | online | no ... ``` ### 11.2. Depuis un client (ex : PBS ou PVE) ```bash tailscale status ``` Tu dois voir **tous** les peers (Mac, Android, HA, etc.) avec leurs IPs `100.64.0.X`. --- ## 12. Pièges rencontrés & leçons apprises ### 12.1. Mauvais port dans le reverse proxy - **Symptôme :** - `curl http://127.0.0.1:8080` → 404 ou rien - Tailscale `up` semble bloquer ou ne jamais arriver à joindre le serveur - **Cause :** - Caddy/nginx servait sur le port `80`, mais on essayait d’appeler `:8080` depuis l’extérieur. - **Solution :** - Standardiser : - Headscale API : `:8080` (interne) - Reverse proxy : `:443` (public) - `--login-server=https://` (pas de port explicite si 443) ### 12.2. Mélange Tailscale SaaS / Headscale - **Symptôme :** - Le client Tailscale ouvre une URL `https://login.tailscale.com/...` - **Cause :** - Pas de `--login-server` ou valeur incorrecte → le client repart sur le SaaS officiel. - **Solution :** - Toujours vérifier la commande `tailscale up` : ```bash tailscale up --login-server=https:// ... ``` ### 12.3. Expiration des nodes - **Symptôme :** - Dans `headscale nodes list`, la colonne `Expired` passe à `yes`. - **Cause :** - Mauvaise manipulation de la commande `expire` avec une date proche du présent. - **Solution :** - Pour un node **qui ne doit pas expirer** : ```bash headscale nodes expire --identifier --expiry 0001-01-01T00:00:00Z ``` --- ## 13. Résumé rapide (mode pense‑bête) 1. **Headscale** - Config dans `/etc/headscale/config.yaml` - `listen_addr: 0.0.0.0:8080` - `server_url: "https://"` 2. **Reverse proxy** - Terminate TLS (Let’s Encrypt) - Proxy `/api` → `127.0.0.1:8080` - Servir la Web UI statique (`/var/www/headscale-admin`) 3. **Utilisateur & clés** - `headscale users create ` - `headscale preauthkeys create --user --reusable --ephemeral=false` 4. **Clients** - Commande type : ```bash tailscale logout || true tailscale down || true tailscale up \ --login-server=https:// \ --auth-key= \ --accept-dns=false \ --accept-routes=false \ --reset ``` 5. **Validation** - `headscale nodes list` - `tailscale status` depuis plusieurs machines Avec cette checklist, tu peux **reconstruire tout ton tailnet Headscale** sans re‑te prendre 2 jours de galère, et tu peux publier ce README (après remplacement des placeholders) sur un dépôt public sans exposer ta vraie infra.