feat(mesh): headscale durci (DERP embarque) et noeuds tailscale
Le controleur headscale du VPS passe en mode durci : flotte DERP de Tailscale retiree (derp.urls vide), DERP embarque sur le VPS (region 900, verify_clients) et STUN en UDP 3478 ; route Traefik headscale.marcpartensky.com -> 127.0.0.1:8090. Cote clients, services/tailscale remplace l'ancien secret tailscale/auth_key (absent) par headscale/preauth_key, coupe l'envoi de logs vers Tailscale Inc (disableUpstreamLogging) et est importe sur tower, le deck et anywhere.
This commit is contained in:
@@ -27,8 +27,10 @@
|
||||
../../services/pangolin
|
||||
../../services/pangolin/gerbil-wg0-ensure.nix
|
||||
../../services/wireguard
|
||||
# ../../services/tailscale # desactive 26/09: exige le secret sops tailscale/auth_key (absent de secrets/anywhere.yml). Reactiver avec le mesh.
|
||||
# Mesh headscale : serveur sur ce VPS (services/headscale) + nœud
|
||||
# tailscale (services/tailscale, clé partagée secrets/headscale.yml).
|
||||
../../services/headscale
|
||||
../../services/tailscale
|
||||
# ./services/chhoto
|
||||
../../services/eternal-terminal
|
||||
../../services/stalwart
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
# Réseau privé ygg-mesh (Yggdrasil via Tor + lien direct LAN), membres :
|
||||
# tower, anywhere, deck. Voir services/ygg-mesh/default.nix.
|
||||
../../services/ygg-mesh
|
||||
# Mesh headscale : nœud tailscale (contrôleur sur le VPS anywhere, cf
|
||||
# services/headscale et services/tailscale).
|
||||
../../services/tailscale
|
||||
./switch-emu.nix
|
||||
];
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@ in {
|
||||
# Réseau privé ygg-mesh (Yggdrasil via Tor + lien direct LAN), membres :
|
||||
# tower, anywhere, deck. Voir services/ygg-mesh/default.nix.
|
||||
../../services/ygg-mesh
|
||||
# Mesh headscale : nœud tailscale (contrôleur sur le VPS anywhere, cf
|
||||
# services/headscale et services/tailscale).
|
||||
../../services/tailscale
|
||||
];
|
||||
|
||||
# Sessions hermes lancees par marc (panes herdr, HERMES_HOME partage, cf
|
||||
|
||||
@@ -1,32 +1,67 @@
|
||||
{ pkgs, config, ... }:
|
||||
{
|
||||
# Contrôleur du mesh headscale de marc (version DURCIE).
|
||||
#
|
||||
# Tourne sur le VPS `anywhere`, seul hôte à IP publique stable, et sert :
|
||||
# - la coordination des nœuds tailscale (tower, deck, anywhere, laptop...) ;
|
||||
# - un DERP embarqué : relais chiffré de secours quand le hole punching
|
||||
# échoue. Le trafic y circule en WireGuard, le relais ne déchiffre rien.
|
||||
#
|
||||
# Durcissement (par rapport au tailscale.com public) :
|
||||
# - derp.urls = [] : la flotte DERP de Tailscale Inc est retirée de la carte
|
||||
# annoncée aux clients ; le seul relais est celui de ce VPS, qui fait
|
||||
# partie du réseau. Aucune machine extérieure ne voit les IP des nœuds.
|
||||
# - logtail.enabled = false : aucun log client n'est envoyé à Tailscale Inc.
|
||||
#
|
||||
# Côté clients : services/tailscale/default.nix, à importer sur chaque machine
|
||||
# NixOS du flake.
|
||||
#
|
||||
# Exposition : headscale.marcpartensky.com -> Traefik local (Pangolin) ->
|
||||
# 127.0.0.1:8090 en HTTP ; la route est dans services/traefik/dynamic.toml.
|
||||
# Le DERP embarqué passe par le MÊME endpoint HTTPS (/derp), donc par
|
||||
# Traefik ; seul le STUN (UDP 3478) est exposé en direct, un reverse proxy
|
||||
# ne portant pas l'UDP.
|
||||
#
|
||||
# Bootstrap / rotation : voir en bas du fichier.
|
||||
services.headscale = {
|
||||
enable = true;
|
||||
address = "127.0.0.1";
|
||||
port = 8090;
|
||||
settings = {
|
||||
# URL publique que les clients Tailscale utiliseront pour rejoindre le tailnet.
|
||||
# headscale.marcpartensky.com doit pointer vers CE VPS et être routé (Pangolin)
|
||||
# vers 127.0.0.1:8090 en HTTP.
|
||||
# URL publique que les clients utilisent pour rejoindre le tailnet.
|
||||
server_url = "https://headscale.marcpartensky.com";
|
||||
# Le reverse proxy (Pangolin/Traefik) tourne en local sur ce VPS : on honore
|
||||
# ses en-têtes X-Forwarded-* pour logguer la vraie IP source des clients.
|
||||
# Traefik tourne en local sur ce VPS : honorer ses X-Forwarded-*.
|
||||
trusted_proxies = [ "127.0.0.1/32" ];
|
||||
|
||||
# DERP : flotte publique Tailscale par défaut pour le fallback derrière NAT
|
||||
# (fonctionne out-of-the-box). Le data plane reste du WireGuard chiffré; le
|
||||
# relais ne voit que des paquets qu'il ne peut pas déchiffrer. Pour une
|
||||
# souveraineté totale plus tard : activer derp.server (embarqué) + exposer
|
||||
# STUN, cf. docs headscale (exige server_url en https et TLS terminé par
|
||||
# headscale lui-même, pas via reverse proxy).
|
||||
derp = {
|
||||
# Aucune carte DERP extérieure : uniquement le DERP embarqué ci-dessous.
|
||||
urls = [ ];
|
||||
paths = [ ];
|
||||
auto_update_enabled = false;
|
||||
server = {
|
||||
enabled = true;
|
||||
region_id = 900; # hors plage des régions officielles (1-30)
|
||||
region_code = "vps";
|
||||
region_name = "VPS RackNerd";
|
||||
# STUN : aide au hole punching, en UDP direct sur l'IP publique.
|
||||
stun_listen_addr = "0.0.0.0:3478";
|
||||
# La région du DERP embarqué est ajoutée à la carte automatiquement,
|
||||
# annoncée sur server_url (port 443, via Traefik).
|
||||
automatically_add_embedded_derp_region = true;
|
||||
# Seuls les nœuds du tailnet peuvent utiliser ce relais.
|
||||
verify_clients = true;
|
||||
# private_key_path : défaut du module
|
||||
# (/var/lib/headscale/derp_server_private.key, généré au premier boot)
|
||||
};
|
||||
};
|
||||
|
||||
# Politique ACL depuis un fichier HuJSON (mode "file").
|
||||
# ACL depuis un fichier HuJSON (mode "file").
|
||||
policy = {
|
||||
mode = "file";
|
||||
path = ./policy.hujson;
|
||||
};
|
||||
|
||||
# Ne jamais envoyer les logs clients vers Tailscale Inc.
|
||||
# Aucun log client vers Tailscale Inc (défaut de 0.28, explicité ici).
|
||||
logtail.enabled = false;
|
||||
|
||||
dns = {
|
||||
@@ -39,4 +74,27 @@
|
||||
};
|
||||
|
||||
environment.systemPackages = [ config.services.headscale.package ];
|
||||
|
||||
# STUN du DERP embarqué : UDP en direct, Traefik ne porte pas l'UDP.
|
||||
networking.firewall.allowedUDPPorts = [ 3478 ];
|
||||
}
|
||||
|
||||
# ================================ BOOTSTRAP ================================
|
||||
#
|
||||
# Déjà en place (23/10/2026) :
|
||||
# - utilisateur headscale `marc` (id 1) ;
|
||||
# - clé de pré-authentification réutilisable (expiration 10 ans, id 2),
|
||||
# chiffrée dans secrets/headscale.yml (clé `preauth_key`), consommée par
|
||||
# services/tailscale sur chaque machine.
|
||||
#
|
||||
# Rotation / nouvelle clé (après révocation par exemple) :
|
||||
# sudo headscale users list # retrouver l'id de marc
|
||||
# sudo headscale preauthkeys create -u <id> --reusable --expiration 0
|
||||
# # remplacer preauth_key dans secrets/headscale.yml (sops), redéployer.
|
||||
#
|
||||
# Révocation d'un nœud : sudo headscale nodes delete -i <id> (ou nodes expire
|
||||
# pour forcer une réauthentification).
|
||||
#
|
||||
# Les nœuds n'expirent pas (Expiration 0001-01-01 dans `nodes list`) ; en
|
||||
# poser une si voulu :
|
||||
# sudo headscale nodes expire -i <id> -e 2036-01-01T00:00:00Z
|
||||
|
||||
@@ -1,6 +1,25 @@
|
||||
{ pkgs, config, lib, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
sops.secrets."tailscale/auth_key" = {
|
||||
# Nœud tailscale du mesh headscale de marc (voir services/headscale pour le
|
||||
# contrôleur, qui tourne sur le VPS `anywhere`). À importer sur chaque machine
|
||||
# NixOS du flake : tower, deck, anywhere, laptop...
|
||||
#
|
||||
# Enrôlement automatique : clé de pré-authentification réutilisable commune,
|
||||
# dans secrets/headscale.yml (clé `preauth_key` ; destinataires anywhere,
|
||||
# tower, mac, deck-host). Rotation : voir le bas du module headscale.
|
||||
#
|
||||
# Durcissement : disableUpstreamLogging coupe l'envoi de logs de tailscaled
|
||||
# vers Tailscale Inc (équivalent de --no-logs-no-support) ; le contrôleur
|
||||
# n'annonce que le DERP du VPS, jamais la flotte tailscale.com.
|
||||
#
|
||||
# Piège du premier enrôlement : tailscaled-autoconnect.service a 90 s pour
|
||||
# joindre le contrôleur. Si l'endpoint public n'est pas encore prêt (cert
|
||||
# Traefik en cours d'émission, route absente), il part en timeout et le nœud
|
||||
# reste non enrôlé. Une fois l'endpoint OK :
|
||||
# sudo systemctl restart tailscaled-autoconnect.service (ou un reboot)
|
||||
sops.secrets."headscale/preauth_key" = {
|
||||
sopsFile = ../../secrets/headscale.yml;
|
||||
key = "preauth_key";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
mode = "0400";
|
||||
@@ -12,12 +31,14 @@
|
||||
services.tailscale = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
useRoutingFeatures = "both"; # "client", "server" ou "both" si ce nœud est aussi exit node / subnet router
|
||||
authKeyFile = config.sops.secrets."tailscale/auth_key".path;
|
||||
# "both" : ce nœud peut aussi annoncer des sous-réseaux / être exit node.
|
||||
useRoutingFeatures = "both";
|
||||
authKeyFile = config.sops.secrets."headscale/preauth_key".path;
|
||||
extraUpFlags = [
|
||||
"--login-server=https://headscale.marcpartensky.com" # contrôleur auto-hébergé (headscale), pas le tailscale officiel
|
||||
"--login-server=https://headscale.marcpartensky.com" # contrôleur auto-hébergé, pas Tailscale Inc
|
||||
"--accept-routes"
|
||||
];
|
||||
disableUpstreamLogging = true;
|
||||
};
|
||||
|
||||
networking.firewall.trustedInterfaces = [ "tailscale0" ];
|
||||
|
||||
@@ -32,6 +32,12 @@
|
||||
[[http.services.int-api.loadBalancer.servers]]
|
||||
url = "http://127.0.0.1:3003"
|
||||
|
||||
# headscale (contrôleur du mesh tailscale, cf services/headscale) : porte aussi
|
||||
# le DERP embarqué des clients (/derp) ; le STUN est à part, en UDP 3478.
|
||||
[http.services.headscale.loadBalancer]
|
||||
[[http.services.headscale.loadBalancer.servers]]
|
||||
url = "http://127.0.0.1:8090"
|
||||
|
||||
# --- ROUTERS ---
|
||||
[http.routers.traefik]
|
||||
rule = "Host(`traefik.vps.marcpartensky.com`)"
|
||||
@@ -82,3 +88,9 @@
|
||||
entryPoints = ["websecure"]
|
||||
service = "int-api"
|
||||
tls = { certResolver = "letsencrypt" }
|
||||
|
||||
[http.routers.headscale]
|
||||
rule = "Host(`headscale.marcpartensky.com`)"
|
||||
entryPoints = ["websecure"]
|
||||
service = "headscale"
|
||||
tls = { certResolver = "letsencrypt" }
|
||||
|
||||
Reference in New Issue
Block a user