nixcfg/systems/x86_64-linux/attic/headscale.nix
Harald Hoyer 67b7c3a9fd feat(headscale): add ACL policy, isolate mx, make mx an exit node
Introduces a headscale ACL policy (file-mode) plus matching client config:

- New systems/x86_64-linux/attic/headscale-policy.hujson:
  * tag:llm restricts a node to talking only to halo:8000
  * all other harald@ nodes have full mesh access to each other
  * harald@ nodes can route internet traffic via approved exit nodes
  * autoApprovers.exitNode = [tag:llm] auto-approves the exit route
    advertised by any tag:llm node (currently mx)

- attic headscale.nix: wire policy.mode = "file" / policy.path to
  the .hujson above.

- mx default.nix: enable useRoutingFeatures = "server" (needed for IP
  forwarding) and add extraSetFlags = ["--advertise-exit-node"] so the
  flag is reapplied on every activation, not just initial login.

Operational steps after deploy:
  headscale nodes tag -i 10 -t tag:llm
2026-05-13 09:06:40 +02:00

50 lines
1.2 KiB
Nix

{ config, ... }:
let
domain = "headscale.hoyer.xyz";
in
{
services = {
headscale = {
enable = true;
address = "0.0.0.0";
port = 8081;
settings = {
server_url = "https://${domain}";
dns = {
base_domain = "hoyer.tail";
nameservers.global = [
"1.1.1.1"
"1.0.0.1"
"2606:4700:4700::1111"
"2606:4700:4700::1001"
];
};
oidc = {
allowed_domains = [ "hoyer.xyz" ];
client_id = "UgQYtXftYvB9ua4cuyZ9NBvaknQfN76pPnf50pDhqghdb87g9tFcuSMiTLVje3R7";
client_secret_path = "/var/lib/headscale/client_secret";
issuer = "https://nc.hoyer.xyz";
};
policy = {
mode = "file";
path = toString ./headscale-policy.hujson;
};
};
};
nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:${toString config.services.headscale.port}";
proxyWebsockets = true;
extraConfig = ''
proxy_redirect http:// https://;
proxy_buffering off;
'';
};
};
};
environment.systemPackages = [ config.services.headscale.package ];
}