{config, pkgs, lib, ...}: with lib; let cfg = config.services.headscale; authServer = cfg.settings.server_url; connectContainer = name: options: { enableTun = true; bindMounts = { "/var/tailauth" = { hostPath = cfg.ensureUsers."${name}".path; }; }; config = {config, pkgs, ...}: { imports = [ ./tailscale.nix ]; networking.nameservers = [ "1.1.1.1" ]; networking.useHostResolvConf = false; networking.firewall = { enable = false; }; services.tailscale = { enable = true; useRoutingFeatures = "client"; authTokenPath = "/var/tailauth"; authUrl = authServer; }; }; }; mkContainerAfterToken = name: options: { requires = ["headscale-preauth-regen-${name}.service"]; after = ["headscale-preauth-regen-${name}.service"]; }; ensureContainerUser = name: options: { }; in { # Extend NixOS containers to automatically # create a headscale user with the container # name, generate its auth, and binds it to # the container imports = [ ./headscale.nix ]; options = { services.headscale.containers = mkOption { type = types.attrsOf (types.submodule ( {config, options, name, ...}: { } )); default = {}; }; }; config = { networking.bridges = { "br0" = { interfaces = []; }; }; networking.interfaces.br0.ipv4.addresses = [{ address = "10.0.0.1"; prefixLength = 24; }]; networking.nat = { enable = true; # Check for hostBridge use vb instead of ve internalInterfaces = (map (n: "vb-${n}") (attrNames cfg.containers)) ++ ["br0"]; externalInterface = "enp0s25"; enableIPv6 = true; }; containers = mapAttrs connectContainer cfg.containers; systemd.services = mapAttrs' (n: v: nameValuePair "container@${n}" (mkContainerAfterToken n v)) cfg.containers; services.headscale.ensureUsers = mapAttrs ensureContainerUser cfg.containers; }; }