diff --git a/flake.lock b/flake.lock index 4e86656..fb48812 100644 --- a/flake.lock +++ b/flake.lock @@ -202,16 +202,16 @@ ] }, "locked": { - "lastModified": 1726989464, - "narHash": "sha256-Vl+WVTJwutXkimwGprnEtXc/s/s8sMuXzqXaspIGlwM=", + "lastModified": 1733050161, + "narHash": "sha256-lYnT+EYE47f5yY3KS/Kd4pJ6CO9fhCqumkYYkQ3TK20=", "owner": "nix-community", "repo": "home-manager", - "rev": "2f23fa308a7c067e52dfcc30a0758f47043ec176", + "rev": "62d536255879be574ebfe9b87c4ac194febf47c5", "type": "github" }, "original": { "owner": "nix-community", - "ref": "release-24.05", + "ref": "release-24.11", "repo": "home-manager", "type": "github" } @@ -430,16 +430,16 @@ }, "nixpkgs-raw": { "locked": { - "lastModified": 1732632634, - "narHash": "sha256-+G7n/ZD635aN0sEXQLynU7pWMd3PKDM7yBIXvYmjABQ=", + "lastModified": 1732981179, + "narHash": "sha256-F7thesZPvAMSwjRu0K8uFshTk3ZZSNAsXTIFvXBT+34=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "6f6076c37180ea3a916f84928cf3a714c5207a30", + "rev": "62c435d93bf046a5396f3016472e8f7c8e2aed65", "type": "github" }, "original": { "id": "nixpkgs", - "ref": "nixos-24.05", + "ref": "nixos-24.11", "type": "indirect" } }, diff --git a/flake.nix b/flake.nix index 41b14d0..b4bad73 100644 --- a/flake.nix +++ b/flake.nix @@ -6,7 +6,7 @@ url = "git+ssh://forgejo@git.everest.tailscale:4222/Toast/nix-secrets"; flake = false; }; - nixpkgs-raw.url = "nixpkgs/nixos-24.05"; + nixpkgs-raw.url = "nixpkgs/nixos-24.11"; nixpkgs-unstable-raw.url = "nixpkgs/nixos-unstable"; agenix = { @@ -18,7 +18,7 @@ }; home-manager = { - url = "github:nix-community/home-manager/release-24.05"; + url = "github:nix-community/home-manager/release-24.11"; inputs.nixpkgs.follows = "nixpkgs-raw"; }; @@ -105,9 +105,7 @@ nixpkgs-patched = nixpkgs-raw.legacyPackages.x86_64-linux.applyPatches { name = "patched-nixpkgs"; src = nixpkgs-raw; - patches = [ - ./nixpkgs-patches/backport_unstable_headscale_changes.patch - ]; + patches = []; }; # https://discourse.nixos.org/t/proper-way-of-applying-patch-to-system-managed-via-flake/21073/26 nixpkgs-unstable = (import "${nixpkgs-unstable-patched}/flake.nix").outputs {self = inputs.self;}; diff --git a/nixpkgs-patches/backport_unstable_headscale_changes.patch b/nixpkgs-patches/backport_unstable_headscale_changes.patch deleted file mode 100644 index c173ef9..0000000 --- a/nixpkgs-patches/backport_unstable_headscale_changes.patch +++ /dev/null @@ -1,1757 +0,0 @@ -From 4a5c88eac3aaf1fb3d5ab15ee84787b9883c2465 Mon Sep 17 00:00:00 2001 -From: Kristoffer Dalby -Date: Fri, 6 Sep 2024 12:47:36 +0200 -Subject: [PATCH 1/8] nixos/headscale: modernize - -Signed-off-by: Kristoffer Dalby ---- - .../modules/services/networking/headscale.nix | 472 ++++++++++-------- - 1 file changed, 270 insertions(+), 202 deletions(-) - -diff --git a/nixos/modules/services/networking/headscale.nix b/nixos/modules/services/networking/headscale.nix -index ea66faeabbf2..645d4fe9c069 100644 ---- a/nixos/modules/services/networking/headscale.nix -+++ b/nixos/modules/services/networking/headscale.nix -@@ -1,8 +1,7 @@ --{ -- config, -- lib, -- pkgs, -- ... -+{ config -+, lib -+, pkgs -+, ... - }: - with lib; let - cfg = config.services.headscale; -@@ -10,9 +9,19 @@ with lib; let - dataDir = "/var/lib/headscale"; - runDir = "/run/headscale"; - -- settingsFormat = pkgs.formats.yaml {}; -+ cliConfig = { -+ # Turn off update checks since the origin of our package -+ # is nixpkgs and not Github. -+ disable_check_updates = true; -+ -+ unix_socket = "${runDir}/headscale.sock"; -+ }; -+ -+ settingsFormat = pkgs.formats.yaml { }; - configFile = settingsFormat.generate "headscale.yaml" cfg.settings; --in { -+ cliConfigFile = settingsFormat.generate "headscale.yaml" cliConfig; -+in -+{ - options = { - services.headscale = { - enable = mkEnableOption "headscale, Open Source coordination server for Tailscale"; -@@ -84,14 +93,6 @@ in { - example = "https://myheadscale.example.com:443"; - }; - -- private_key_path = mkOption { -- type = types.path; -- default = "${dataDir}/private.key"; -- description = '' -- Path to private key file, generated automatically if it does not exist. -- ''; -- }; -- - noise.private_key_path = mkOption { - type = types.path; - default = "${dataDir}/noise_private.key"; -@@ -100,10 +101,44 @@ in { - ''; - }; - -+ prefixes = -+ let -+ prefDesc = '' -+ Each prefix consists of either an IPv4 or IPv6 address, -+ and the associated prefix length, delimited by a slash. -+ It must be within IP ranges supported by the Tailscale -+ client - i.e., subnets of 100.64.0.0/10 and fd7a:115c:a1e0::/48. -+ ''; -+ in -+ { -+ v4 = mkOption { -+ type = types.str; -+ default = "100.64.0.0/10"; -+ description = prefDesc; -+ }; -+ -+ v6 = mkOption { -+ type = types.str; -+ default = "fd7a:115c:a1e0::/48"; -+ description = prefDesc; -+ }; -+ -+ allocation = mkOption { -+ type = types.enum [ "sequential" "random" ]; -+ example = "random"; -+ default = "sequential"; -+ description = '' -+ Strategy used for allocation of IPs to nodes, available options: -+ - sequential (default): assigns the next free IP from the previous given IP. -+ - random: assigns the next free IP from a pseudo-random IP generator (crypto/rand). -+ ''; -+ }; -+ }; -+ - derp = { - urls = mkOption { - type = types.listOf types.str; -- default = ["https://controlplane.tailscale.com/derpmap/default"]; -+ default = [ "https://controlplane.tailscale.com/derpmap/default" ]; - description = '' - List of urls containing DERP maps. - See [How Tailscale works](https://tailscale.com/blog/how-tailscale-works/) for more information on DERP maps. -@@ -112,7 +147,7 @@ in { - - paths = mkOption { - type = types.listOf types.path; -- default = []; -+ default = [ ]; - description = '' - List of file paths containing DERP maps. - See [How Tailscale works](https://tailscale.com/blog/how-tailscale-works/) for more information on DERP maps. -@@ -136,6 +171,14 @@ in { - ''; - example = "5m"; - }; -+ -+ server.private_key_path = lib.mkOption { -+ type = lib.types.path; -+ default = "${dataDir}/derp_server_private.key"; -+ description = '' -+ Path to derp private key file, generated automatically if it does not exist. -+ ''; -+ }; - }; - - ephemeral_node_inactivity_timeout = mkOption { -@@ -147,102 +190,98 @@ in { - example = "5m"; - }; - -- db_type = mkOption { -- type = types.enum ["sqlite3" "postgres"]; -- example = "postgres"; -- default = "sqlite3"; -- description = "Database engine to use."; -- }; -- -- db_host = mkOption { -- type = types.nullOr types.str; -- default = null; -- example = "127.0.0.1"; -- description = "Database host address."; -- }; -- -- db_port = mkOption { -- type = types.nullOr types.port; -- default = null; -- example = 3306; -- description = "Database host port."; -- }; -+ database = { -+ type = mkOption { -+ type = types.enum [ "sqlite" "sqlite3" "postgres" ]; -+ example = "postgres"; -+ default = "sqlite"; -+ description = '' -+ Database engine to use. -+ Please note that using Postgres is highly discouraged as it is only supported for legacy reasons. -+ All new development, testing and optimisations are done with SQLite in mind. -+ ''; -+ }; - -- db_name = mkOption { -- type = types.nullOr types.str; -- default = null; -- example = "headscale"; -- description = "Database name."; -- }; -+ sqlite = { -+ path = mkOption { -+ type = types.nullOr types.str; -+ default = "${dataDir}/db.sqlite"; -+ description = "Path to the sqlite3 database file."; -+ }; - -- db_user = mkOption { -- type = types.nullOr types.str; -- default = null; -- example = "headscale"; -- description = "Database user."; -- }; -+ write_ahead_log = mkOption { -+ type = types.bool; -+ default = true; -+ description = '' -+ Enable WAL mode for SQLite. This is recommended for production environments. -+ https://www.sqlite.org/wal.html -+ ''; -+ example = true; -+ }; -+ }; - -- db_password_file = mkOption { -- type = types.nullOr types.path; -- default = null; -- example = "/run/keys/headscale-dbpassword"; -- description = '' -- A file containing the password corresponding to -- {option}`database.user`. -- ''; -- }; -+ postgres = { -+ host = mkOption { -+ type = types.nullOr types.str; -+ default = null; -+ example = "127.0.0.1"; -+ description = "Database host address."; -+ }; - -- db_path = mkOption { -- type = types.nullOr types.str; -- default = "${dataDir}/db.sqlite"; -- description = "Path to the sqlite3 database file."; -- }; -+ port = mkOption { -+ type = types.nullOr types.port; -+ default = null; -+ example = 3306; -+ description = "Database host port."; -+ }; - -- log.level = mkOption { -- type = types.str; -- default = "info"; -- description = '' -- headscale log level. -- ''; -- example = "debug"; -- }; -+ name = mkOption { -+ type = types.nullOr types.str; -+ default = null; -+ example = "headscale"; -+ description = "Database name."; -+ }; - -- log.format = mkOption { -- type = types.str; -- default = "text"; -- description = '' -- headscale log format. -- ''; -- example = "json"; -- }; -+ user = mkOption { -+ type = types.nullOr types.str; -+ default = null; -+ example = "headscale"; -+ description = "Database user."; -+ }; - -- dns_config = { -- nameservers = mkOption { -- type = types.listOf types.str; -- default = ["1.1.1.1"]; -- description = '' -- List of nameservers to pass to Tailscale clients. -- ''; -+ password_file = mkOption { -+ type = types.nullOr types.path; -+ default = null; -+ example = "/run/keys/headscale-dbpassword"; -+ description = '' -+ A file containing the password corresponding to -+ {option}`database.user`. -+ ''; -+ }; - }; -+ }; - -- override_local_dns = mkOption { -- type = types.bool; -- default = false; -+ log = { -+ level = mkOption { -+ type = types.str; -+ default = "info"; - description = '' -- Whether to use [Override local DNS](https://tailscale.com/kb/1054/dns/). -+ headscale log level. - ''; -- example = true; -+ example = "debug"; - }; - -- domains = mkOption { -- type = types.listOf types.str; -- default = []; -+ format = mkOption { -+ type = types.str; -+ default = "text"; - description = '' -- Search domains to inject to Tailscale clients. -+ headscale log format. - ''; -- example = ["mydomain.internal"]; -+ example = "json"; - }; -+ }; - -+ dns = { - magic_dns = mkOption { - type = types.bool; - default = true; -@@ -264,6 +303,25 @@ in { - `myhost.mynamespace.example.com`). - ''; - }; -+ -+ nameservers = { -+ global = mkOption { -+ type = types.listOf types.str; -+ default = [ ]; -+ description = '' -+ List of nameservers to pass to Tailscale clients. -+ ''; -+ }; -+ }; -+ -+ search_domains = mkOption { -+ type = types.listOf types.str; -+ default = [ ]; -+ description = '' -+ Search domains to inject to Tailscale clients. -+ ''; -+ example = [ "mydomain.internal" ]; -+ }; - }; - - oidc = { -@@ -294,7 +352,7 @@ in { - - scope = mkOption { - type = types.listOf types.str; -- default = ["openid" "profile" "email"]; -+ default = [ "openid" "profile" "email" ]; - description = '' - Scopes used in the OIDC flow. - ''; -@@ -348,7 +406,7 @@ in { - }; - - tls_letsencrypt_challenge_type = mkOption { -- type = types.enum ["TLS-ALPN-01" "HTTP-01"]; -+ type = types.enum [ "TLS-ALPN-01" "HTTP-01" ]; - default = "HTTP-01"; - description = '' - Type of ACME challenge to use, currently supported types: -@@ -382,12 +440,24 @@ in { - ''; - }; - -- acl_policy_path = mkOption { -- type = types.nullOr types.path; -- default = null; -- description = '' -- Path to a file containing ACL policies. -- ''; -+ policy = { -+ mode = mkOption { -+ type = types.enum [ "file" "database" ]; -+ default = "file"; -+ description = '' -+ The mode can be "file" or "database" that defines -+ where the ACL policies are stored and read from. -+ ''; -+ }; -+ -+ path = mkOption { -+ type = types.nullOr types.path; -+ default = null; -+ description = '' -+ If the mode is set to "file", the path to a -+ HuJSON file containing ACL policies. -+ ''; -+ }; - }; - }; - }; -@@ -396,64 +466,63 @@ in { - }; - - imports = [ -- # TODO address + port = listen_addr -- (mkRenamedOptionModule ["services" "headscale" "serverUrl"] ["services" "headscale" "settings" "server_url"]) -- (mkRenamedOptionModule ["services" "headscale" "privateKeyFile"] ["services" "headscale" "settings" "private_key_path"]) -- (mkRenamedOptionModule ["services" "headscale" "derp" "urls"] ["services" "headscale" "settings" "derp" "urls"]) -- (mkRenamedOptionModule ["services" "headscale" "derp" "paths"] ["services" "headscale" "settings" "derp" "paths"]) -- (mkRenamedOptionModule ["services" "headscale" "derp" "autoUpdate"] ["services" "headscale" "settings" "derp" "auto_update_enable"]) -- (mkRenamedOptionModule ["services" "headscale" "derp" "updateFrequency"] ["services" "headscale" "settings" "derp" "update_frequency"]) -- (mkRenamedOptionModule ["services" "headscale" "ephemeralNodeInactivityTimeout"] ["services" "headscale" "settings" "ephemeral_node_inactivity_timeout"]) -- (mkRenamedOptionModule ["services" "headscale" "database" "type"] ["services" "headscale" "settings" "db_type"]) -- (mkRenamedOptionModule ["services" "headscale" "database" "path"] ["services" "headscale" "settings" "db_path"]) -- (mkRenamedOptionModule ["services" "headscale" "database" "host"] ["services" "headscale" "settings" "db_host"]) -- (mkRenamedOptionModule ["services" "headscale" "database" "port"] ["services" "headscale" "settings" "db_port"]) -- (mkRenamedOptionModule ["services" "headscale" "database" "name"] ["services" "headscale" "settings" "db_name"]) -- (mkRenamedOptionModule ["services" "headscale" "database" "user"] ["services" "headscale" "settings" "db_user"]) -- (mkRenamedOptionModule ["services" "headscale" "database" "passwordFile"] ["services" "headscale" "settings" "db_password_file"]) -- (mkRenamedOptionModule ["services" "headscale" "logLevel"] ["services" "headscale" "settings" "log" "level"]) -- (mkRenamedOptionModule ["services" "headscale" "dns" "nameservers"] ["services" "headscale" "settings" "dns_config" "nameservers"]) -- (mkRenamedOptionModule ["services" "headscale" "dns" "domains"] ["services" "headscale" "settings" "dns_config" "domains"]) -- (mkRenamedOptionModule ["services" "headscale" "dns" "magicDns"] ["services" "headscale" "settings" "dns_config" "magic_dns"]) -- (mkRenamedOptionModule ["services" "headscale" "dns" "baseDomain"] ["services" "headscale" "settings" "dns_config" "base_domain"]) -- (mkRenamedOptionModule ["services" "headscale" "openIdConnect" "issuer"] ["services" "headscale" "settings" "oidc" "issuer"]) -- (mkRenamedOptionModule ["services" "headscale" "openIdConnect" "clientId"] ["services" "headscale" "settings" "oidc" "client_id"]) -- (mkRenamedOptionModule ["services" "headscale" "openIdConnect" "clientSecretFile"] ["services" "headscale" "settings" "oidc" "client_secret_path"]) -- (mkRenamedOptionModule ["services" "headscale" "tls" "letsencrypt" "hostname"] ["services" "headscale" "settings" "tls_letsencrypt_hostname"]) -- (mkRenamedOptionModule ["services" "headscale" "tls" "letsencrypt" "challengeType"] ["services" "headscale" "settings" "tls_letsencrypt_challenge_type"]) -- (mkRenamedOptionModule ["services" "headscale" "tls" "letsencrypt" "httpListen"] ["services" "headscale" "settings" "tls_letsencrypt_listen"]) -- (mkRenamedOptionModule ["services" "headscale" "tls" "certFile"] ["services" "headscale" "settings" "tls_cert_path"]) -- (mkRenamedOptionModule ["services" "headscale" "tls" "keyFile"] ["services" "headscale" "settings" "tls_key_path"]) -- (mkRenamedOptionModule ["services" "headscale" "aclPolicyFile"] ["services" "headscale" "settings" "acl_policy_path"]) -- -- (mkRemovedOptionModule ["services" "headscale" "openIdConnect" "domainMap"] '' -+ (mkRenamedOptionModule [ "services" "headscale" "serverUrl" ] [ "services" "headscale" "settings" "server_url" ]) -+ (mkRenamedOptionModule [ "services" "headscale" "derp" "urls" ] [ "services" "headscale" "settings" "derp" "urls" ]) -+ (mkRenamedOptionModule [ "services" "headscale" "derp" "paths" ] [ "services" "headscale" "settings" "derp" "paths" ]) -+ (mkRenamedOptionModule [ "services" "headscale" "derp" "autoUpdate" ] [ "services" "headscale" "settings" "derp" "auto_update_enable" ]) -+ (mkRenamedOptionModule [ "services" "headscale" "derp" "updateFrequency" ] [ "services" "headscale" "settings" "derp" "update_frequency" ]) -+ (mkRenamedOptionModule [ "services" "headscale" "ephemeralNodeInactivityTimeout" ] [ "services" "headscale" "settings" "ephemeral_node_inactivity_timeout" ]) -+ -+ # (mkRenamedOptionModule ["services" "headscale" "settings" "db_type"] ["services" "headscale" "settings" "database" "type"]) -+ # (mkRenamedOptionModule ["services" "headscale" "settings" "db_path"] ["services" "headscale" "settings" "database" "sqlite" "path"]) -+ # (mkRenamedOptionModule ["services" "headscale" "settings" "db_host"] ["services" "headscale" "settings" "database" "postgres" "host"]) -+ # (mkRenamedOptionModule ["services" "headscale" "settings" "db_port"] ["services" "headscale" "settings" "database" "postgres" "port"]) -+ # (mkRenamedOptionModule ["services" "headscale" "settings" "db_name"] ["services" "headscale" "settings" "database" "postgres" "name"]) -+ # (mkRenamedOptionModule ["services" "headscale" "settings" "db_user"] ["services" "headscale" "settings" "database" "postgres" "user"]) -+ # (mkRenamedOptionModule ["services" "headscale" "settings" "db_password_file"] ["services" "headscale" "settings" "database" "postgres" "password_file"]) -+ -+ (mkRenamedOptionModule [ "services" "headscale" "logLevel" ] [ "services" "headscale" "settings" "log" "level" ]) -+ -+ # (mkRenamedOptionModule ["services" "headscale" "settings" "dns_config" "nameservers"] ["services" "headscale" "settings" "dns" "nameservers" "global"]) -+ # (mkRenamedOptionModule ["services" "headscale" "settings" "dns_config" "domains"] ["services" "headscale" "settings" "dns" "search_domains"]) -+ # (mkRenamedOptionModule ["services" "headscale" "settings" "dns_config" "magic_dns"] ["services" "headscale" "settings" "dns" "magic_dns"]) -+ # (mkRenamedOptionModule ["services" "headscale" "settings" "dns_config" "base_domain"] ["services" "headscale" "settings" "dns" "base_domain"]) -+ -+ (mkRenamedOptionModule [ "services" "headscale" "openIdConnect" "issuer" ] [ "services" "headscale" "settings" "oidc" "issuer" ]) -+ (mkRenamedOptionModule [ "services" "headscale" "openIdConnect" "clientId" ] [ "services" "headscale" "settings" "oidc" "client_id" ]) -+ (mkRenamedOptionModule [ "services" "headscale" "openIdConnect" "clientSecretFile" ] [ "services" "headscale" "settings" "oidc" "client_secret_path" ]) -+ (mkRenamedOptionModule [ "services" "headscale" "tls" "letsencrypt" "hostname" ] [ "services" "headscale" "settings" "tls_letsencrypt_hostname" ]) -+ (mkRenamedOptionModule [ "services" "headscale" "tls" "letsencrypt" "challengeType" ] [ "services" "headscale" "settings" "tls_letsencrypt_challenge_type" ]) -+ (mkRenamedOptionModule [ "services" "headscale" "tls" "letsencrypt" "httpListen" ] [ "services" "headscale" "settings" "tls_letsencrypt_listen" ]) -+ (mkRenamedOptionModule [ "services" "headscale" "tls" "certFile" ] [ "services" "headscale" "settings" "tls_cert_path" ]) -+ (mkRenamedOptionModule [ "services" "headscale" "tls" "keyFile" ] [ "services" "headscale" "settings" "tls_key_path" ]) -+ -+ # (mkRenamedOptionModule ["services" "headscale" "settings" "acl_policy_path"] ["services" "headscale" "settings" "policy" "path"]) -+ -+ (mkRemovedOptionModule [ "services" "headscale" "openIdConnect" "domainMap" ] '' - Headscale no longer uses domain_map. If you're using an old version of headscale you can still set this option via services.headscale.settings.oidc.domain_map. - '') - ]; - - config = mkIf cfg.enable { -- services.headscale.settings = { -- listen_addr = mkDefault "${cfg.address}:${toString cfg.port}"; -+ services.headscale.settings = mkMerge [ -+ cliConfig -+ { -+ listen_addr = mkDefault "${cfg.address}:${toString cfg.port}"; - -- # Turn off update checks since the origin of our package -- # is nixpkgs and not Github. -- disable_check_updates = true; -- -- unix_socket = "${runDir}/headscale.sock"; -- -- tls_letsencrypt_cache_dir = "${dataDir}/.cache"; -- }; -+ tls_letsencrypt_cache_dir = "${dataDir}/.cache"; -+ } -+ ]; - - environment = { -- # Setup the headscale configuration in a known path in /etc to -- # allow both the Server and the Client use it to find the socket -- # for communication. -- etc."headscale/config.yaml".source = configFile; -+ # Headscale CLI needs a minimal config to be able to locate the unix socket -+ # to talk to the server instance. -+ etc."headscale/config.yaml".source = cliConfigFile; - - systemPackages = [ cfg.package ]; - }; - -- users.groups.headscale = mkIf (cfg.group == "headscale") {}; -+ users.groups.headscale = mkIf (cfg.group == "headscale") { }; - - users.users.headscale = mkIf (cfg.user == "headscale") { - description = "headscale user"; -@@ -465,65 +534,64 @@ in { - systemd.services.headscale = { - description = "headscale coordination server for Tailscale"; - wants = [ "network-online.target" ]; -- after = ["network-online.target"]; -- wantedBy = ["multi-user.target"]; -- restartTriggers = [configFile]; -- -- environment.GIN_MODE = "release"; -+ after = [ "network-online.target" ]; -+ wantedBy = [ "multi-user.target" ]; - - script = '' -- ${optionalString (cfg.settings.db_password_file != null) '' -- export HEADSCALE_DB_PASS="$(head -n1 ${escapeShellArg cfg.settings.db_password_file})" -+ ${optionalString (cfg.settings.database.postgres.password_file != null) '' -+ export HEADSCALE_DATABASE_POSTGRES_PASS="$(head -n1 ${escapeShellArg cfg.settings.database.postgres.password_file})" - ''} - -- exec ${cfg.package}/bin/headscale serve -+ exec ${lib.getExe cfg.package} serve --config ${configFile} - ''; - -- serviceConfig = let -- capabilityBoundingSet = ["CAP_CHOWN"] ++ optional (cfg.port < 1024) "CAP_NET_BIND_SERVICE"; -- in { -- Restart = "always"; -- Type = "simple"; -- User = cfg.user; -- Group = cfg.group; -- -- # Hardening options -- RuntimeDirectory = "headscale"; -- # Allow headscale group access so users can be added and use the CLI. -- RuntimeDirectoryMode = "0750"; -- -- StateDirectory = "headscale"; -- StateDirectoryMode = "0750"; -- -- ProtectSystem = "strict"; -- ProtectHome = true; -- PrivateTmp = true; -- PrivateDevices = true; -- ProtectKernelTunables = true; -- ProtectControlGroups = true; -- RestrictSUIDSGID = true; -- PrivateMounts = true; -- ProtectKernelModules = true; -- ProtectKernelLogs = true; -- ProtectHostname = true; -- ProtectClock = true; -- ProtectProc = "invisible"; -- ProcSubset = "pid"; -- RestrictNamespaces = true; -- RemoveIPC = true; -- UMask = "0077"; -- -- CapabilityBoundingSet = capabilityBoundingSet; -- AmbientCapabilities = capabilityBoundingSet; -- NoNewPrivileges = true; -- LockPersonality = true; -- RestrictRealtime = true; -- SystemCallFilter = ["@system-service" "~@privileged" "@chown"]; -- SystemCallArchitectures = "native"; -- RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX"; -- }; -+ serviceConfig = -+ let -+ capabilityBoundingSet = [ "CAP_CHOWN" ] ++ optional (cfg.port < 1024) "CAP_NET_BIND_SERVICE"; -+ in -+ { -+ Restart = "always"; -+ Type = "simple"; -+ User = cfg.user; -+ Group = cfg.group; -+ -+ # Hardening options -+ RuntimeDirectory = "headscale"; -+ # Allow headscale group access so users can be added and use the CLI. -+ RuntimeDirectoryMode = "0750"; -+ -+ StateDirectory = "headscale"; -+ StateDirectoryMode = "0750"; -+ -+ ProtectSystem = "strict"; -+ ProtectHome = true; -+ PrivateTmp = true; -+ PrivateDevices = true; -+ ProtectKernelTunables = true; -+ ProtectControlGroups = true; -+ RestrictSUIDSGID = true; -+ PrivateMounts = true; -+ ProtectKernelModules = true; -+ ProtectKernelLogs = true; -+ ProtectHostname = true; -+ ProtectClock = true; -+ ProtectProc = "invisible"; -+ ProcSubset = "pid"; -+ RestrictNamespaces = true; -+ RemoveIPC = true; -+ UMask = "0077"; -+ -+ CapabilityBoundingSet = capabilityBoundingSet; -+ AmbientCapabilities = capabilityBoundingSet; -+ NoNewPrivileges = true; -+ LockPersonality = true; -+ RestrictRealtime = true; -+ SystemCallFilter = [ "@system-service" "~@privileged" "@chown" ]; -+ SystemCallArchitectures = "native"; -+ RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX"; -+ }; - }; - }; - -- meta.maintainers = with maintainers; [kradalby misterio77]; -+ meta.maintainers = with maintainers; [ kradalby misterio77 ]; - } --- -2.46.1 - - -From afff4fba9136ba0dbe8ff635cb3ead89d5240bf8 Mon Sep 17 00:00:00 2001 -From: Kristoffer Dalby -Date: Wed, 18 Sep 2024 10:24:19 +0100 -Subject: [PATCH 2/8] nixos/headscale: update module to headscale 0.23.0 - -Signed-off-by: Kristoffer Dalby ---- - .../modules/services/networking/headscale.nix | 445 +++++++++--------- - 1 file changed, 219 insertions(+), 226 deletions(-) - -diff --git a/nixos/modules/services/networking/headscale.nix b/nixos/modules/services/networking/headscale.nix -index 645d4fe9c069..622a13fe7b61 100644 ---- a/nixos/modules/services/networking/headscale.nix -+++ b/nixos/modules/services/networking/headscale.nix -@@ -1,9 +1,9 @@ --{ config --, lib --, pkgs --, ... --}: --with lib; let -+{ -+ config, -+ lib, -+ pkgs, -+ ... -+}: let - cfg = config.services.headscale; - - dataDir = "/var/lib/headscale"; -@@ -17,20 +17,19 @@ with lib; let - unix_socket = "${runDir}/headscale.sock"; - }; - -- settingsFormat = pkgs.formats.yaml { }; -+ settingsFormat = pkgs.formats.yaml {}; - configFile = settingsFormat.generate "headscale.yaml" cfg.settings; - cliConfigFile = settingsFormat.generate "headscale.yaml" cliConfig; --in --{ -+in { - options = { - services.headscale = { -- enable = mkEnableOption "headscale, Open Source coordination server for Tailscale"; -+ enable = lib.mkEnableOption "headscale, Open Source coordination server for Tailscale"; - -- package = mkPackageOption pkgs "headscale" { }; -+ package = lib.mkPackageOption pkgs "headscale" {}; - -- user = mkOption { -+ user = lib.mkOption { - default = "headscale"; -- type = types.str; -+ type = lib.types.str; - description = '' - User account under which headscale runs. - -@@ -42,9 +41,9 @@ in - ''; - }; - -- group = mkOption { -+ group = lib.mkOption { - default = "headscale"; -- type = types.str; -+ type = lib.types.str; - description = '' - Group under which headscale runs. - -@@ -56,8 +55,8 @@ in - ''; - }; - -- address = mkOption { -- type = types.str; -+ address = lib.mkOption { -+ type = lib.types.str; - default = "127.0.0.1"; - description = '' - Listening address of headscale. -@@ -65,8 +64,8 @@ in - example = "0.0.0.0"; - }; - -- port = mkOption { -- type = types.port; -+ port = lib.mkOption { -+ type = lib.types.port; - default = 8080; - description = '' - Listening port of headscale. -@@ -74,18 +73,33 @@ in - example = 443; - }; - -- settings = mkOption { -+ settings = lib.mkOption { - description = '' - Overrides to {file}`config.yaml` as a Nix attribute set. - Check the [example config](https://github.com/juanfont/headscale/blob/main/config-example.yaml) - for possible options. - ''; -- type = types.submodule { -+ type = lib.types.submodule { - freeformType = settingsFormat.type; - -+ imports = with lib; [ -+ (mkAliasOptionModule ["acl_policy_path"] ["policy" "path"]) -+ (mkAliasOptionModule ["db_host"] ["database" "postgres" "host"]) -+ (mkAliasOptionModule ["db_name"] ["database" "postgres" "name"]) -+ (mkAliasOptionModule ["db_password_file"] ["database" "postgres" "password_file"]) -+ (mkAliasOptionModule ["db_path"] ["database" "sqlite" "path"]) -+ (mkAliasOptionModule ["db_port"] ["database" "postgres" "port"]) -+ (mkAliasOptionModule ["db_type"] ["database" "type"]) -+ (mkAliasOptionModule ["db_user"] ["database" "postgres" "user"]) -+ (mkAliasOptionModule ["dns_config" "base_domain"] ["dns" "base_domain"]) -+ (mkAliasOptionModule ["dns_config" "domains"] ["dns" "search_domains"]) -+ (mkAliasOptionModule ["dns_config" "magic_dns"] ["dns" "magic_dns"]) -+ (mkAliasOptionModule ["dns_config" "nameservers"] ["dns" "nameservers" "global"]) -+ ]; -+ - options = { -- server_url = mkOption { -- type = types.str; -+ server_url = lib.mkOption { -+ type = lib.types.str; - default = "http://127.0.0.1:8080"; - description = '' - The url clients will connect to. -@@ -93,69 +107,67 @@ in - example = "https://myheadscale.example.com:443"; - }; - -- noise.private_key_path = mkOption { -- type = types.path; -+ noise.private_key_path = lib.mkOption { -+ type = lib.types.path; - default = "${dataDir}/noise_private.key"; - description = '' - Path to noise private key file, generated automatically if it does not exist. - ''; - }; - -- prefixes = -- let -- prefDesc = '' -- Each prefix consists of either an IPv4 or IPv6 address, -- and the associated prefix length, delimited by a slash. -- It must be within IP ranges supported by the Tailscale -- client - i.e., subnets of 100.64.0.0/10 and fd7a:115c:a1e0::/48. -- ''; -- in -- { -- v4 = mkOption { -- type = types.str; -- default = "100.64.0.0/10"; -- description = prefDesc; -- }; -+ prefixes = let -+ prefDesc = '' -+ Each prefix consists of either an IPv4 or IPv6 address, -+ and the associated prefix length, delimited by a slash. -+ It must be within IP ranges supported by the Tailscale -+ client - i.e., subnets of 100.64.0.0/10 and fd7a:115c:a1e0::/48. -+ ''; -+ in { -+ v4 = lib.mkOption { -+ type = lib.types.str; -+ default = "100.64.0.0/10"; -+ description = prefDesc; -+ }; - -- v6 = mkOption { -- type = types.str; -- default = "fd7a:115c:a1e0::/48"; -- description = prefDesc; -- }; -+ v6 = lib.mkOption { -+ type = lib.types.str; -+ default = "fd7a:115c:a1e0::/48"; -+ description = prefDesc; -+ }; - -- allocation = mkOption { -- type = types.enum [ "sequential" "random" ]; -- example = "random"; -- default = "sequential"; -- description = '' -- Strategy used for allocation of IPs to nodes, available options: -- - sequential (default): assigns the next free IP from the previous given IP. -- - random: assigns the next free IP from a pseudo-random IP generator (crypto/rand). -- ''; -- }; -+ allocation = lib.mkOption { -+ type = lib.types.enum ["sequential" "random"]; -+ example = "random"; -+ default = "sequential"; -+ description = '' -+ Strategy used for allocation of IPs to nodes, available options: -+ - sequential (default): assigns the next free IP from the previous given IP. -+ - random: assigns the next free IP from a pseudo-random IP generator (crypto/rand). -+ ''; - }; -+ }; - - derp = { -- urls = mkOption { -- type = types.listOf types.str; -- default = [ "https://controlplane.tailscale.com/derpmap/default" ]; -+ urls = lib.mkOption { -+ type = lib.types.listOf lib.types.str; -+ default = ["https://controlplane.tailscale.com/derpmap/default"]; - description = '' - List of urls containing DERP maps. - See [How Tailscale works](https://tailscale.com/blog/how-tailscale-works/) for more information on DERP maps. - ''; - }; - -- paths = mkOption { -- type = types.listOf types.path; -- default = [ ]; -+ paths = lib.mkOption { -+ type = lib.types.listOf lib.types.path; -+ default = []; - description = '' - List of file paths containing DERP maps. - See [How Tailscale works](https://tailscale.com/blog/how-tailscale-works/) for more information on DERP maps. - ''; - }; - -- auto_update_enable = mkOption { -- type = types.bool; -+ auto_update_enable = lib.mkOption { -+ type = lib.types.bool; - default = true; - description = '' - Whether to automatically update DERP maps on a set frequency. -@@ -163,8 +175,8 @@ in - example = false; - }; - -- update_frequency = mkOption { -- type = types.str; -+ update_frequency = lib.mkOption { -+ type = lib.types.str; - default = "24h"; - description = '' - Frequency to update DERP maps. -@@ -181,8 +193,8 @@ in - }; - }; - -- ephemeral_node_inactivity_timeout = mkOption { -- type = types.str; -+ ephemeral_node_inactivity_timeout = lib.mkOption { -+ type = lib.types.str; - default = "30m"; - description = '' - Time before an inactive ephemeral node is deleted. -@@ -191,8 +203,8 @@ in - }; - - database = { -- type = mkOption { -- type = types.enum [ "sqlite" "sqlite3" "postgres" ]; -+ type = lib.mkOption { -+ type = lib.types.enum ["sqlite" "sqlite3" "postgres"]; - example = "postgres"; - default = "sqlite"; - description = '' -@@ -203,14 +215,14 @@ in - }; - - sqlite = { -- path = mkOption { -- type = types.nullOr types.str; -+ path = lib.mkOption { -+ type = lib.types.nullOr lib.types.str; - default = "${dataDir}/db.sqlite"; - description = "Path to the sqlite3 database file."; - }; - -- write_ahead_log = mkOption { -- type = types.bool; -+ write_ahead_log = lib.mkOption { -+ type = lib.types.bool; - default = true; - description = '' - Enable WAL mode for SQLite. This is recommended for production environments. -@@ -221,36 +233,36 @@ in - }; - - postgres = { -- host = mkOption { -- type = types.nullOr types.str; -+ host = lib.mkOption { -+ type = lib.types.nullOr lib.types.str; - default = null; - example = "127.0.0.1"; - description = "Database host address."; - }; - -- port = mkOption { -- type = types.nullOr types.port; -+ port = lib.mkOption { -+ type = lib.types.nullOr lib.types.port; - default = null; - example = 3306; - description = "Database host port."; - }; - -- name = mkOption { -- type = types.nullOr types.str; -+ name = lib.mkOption { -+ type = lib.types.nullOr lib.types.str; - default = null; - example = "headscale"; - description = "Database name."; - }; - -- user = mkOption { -- type = types.nullOr types.str; -+ user = lib.mkOption { -+ type = lib.types.nullOr lib.types.str; - default = null; - example = "headscale"; - description = "Database user."; - }; - -- password_file = mkOption { -- type = types.nullOr types.path; -+ password_file = lib.mkOption { -+ type = lib.types.nullOr lib.types.path; - default = null; - example = "/run/keys/headscale-dbpassword"; - description = '' -@@ -262,8 +274,8 @@ in - }; - - log = { -- level = mkOption { -- type = types.str; -+ level = lib.mkOption { -+ type = lib.types.str; - default = "info"; - description = '' - headscale log level. -@@ -271,8 +283,8 @@ in - example = "debug"; - }; - -- format = mkOption { -- type = types.str; -+ format = lib.mkOption { -+ type = lib.types.str; - default = "text"; - description = '' - headscale log format. -@@ -282,8 +294,8 @@ in - }; - - dns = { -- magic_dns = mkOption { -- type = types.bool; -+ magic_dns = lib.mkOption { -+ type = lib.types.bool; - default = true; - description = '' - Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/). -@@ -292,8 +304,8 @@ in - example = false; - }; - -- base_domain = mkOption { -- type = types.str; -+ base_domain = lib.mkOption { -+ type = lib.types.str; - default = ""; - description = '' - Defines the base domain to create the hostnames for MagicDNS. -@@ -305,28 +317,28 @@ in - }; - - nameservers = { -- global = mkOption { -- type = types.listOf types.str; -- default = [ ]; -+ global = lib.mkOption { -+ type = lib.types.listOf lib.types.str; -+ default = []; - description = '' - List of nameservers to pass to Tailscale clients. - ''; - }; - }; - -- search_domains = mkOption { -- type = types.listOf types.str; -- default = [ ]; -+ search_domains = lib.mkOption { -+ type = lib.types.listOf lib.types.str; -+ default = []; - description = '' - Search domains to inject to Tailscale clients. - ''; -- example = [ "mydomain.internal" ]; -+ example = ["mydomain.internal"]; - }; - }; - - oidc = { -- issuer = mkOption { -- type = types.str; -+ issuer = lib.mkOption { -+ type = lib.types.str; - default = ""; - description = '' - URL to OpenID issuer. -@@ -334,33 +346,33 @@ in - example = "https://openid.example.com"; - }; - -- client_id = mkOption { -- type = types.str; -+ client_id = lib.mkOption { -+ type = lib.types.str; - default = ""; - description = '' - OpenID Connect client ID. - ''; - }; - -- client_secret_path = mkOption { -- type = types.nullOr types.str; -+ client_secret_path = lib.mkOption { -+ type = lib.types.nullOr lib.types.str; - default = null; - description = '' - Path to OpenID Connect client secret file. Expands environment variables in format ''${VAR}. - ''; - }; - -- scope = mkOption { -- type = types.listOf types.str; -- default = [ "openid" "profile" "email" ]; -+ scope = lib.mkOption { -+ type = lib.types.listOf lib.types.str; -+ default = ["openid" "profile" "email"]; - description = '' - Scopes used in the OIDC flow. - ''; - }; - -- extra_params = mkOption { -- type = types.attrsOf types.str; -- default = { }; -+ extra_params = lib.mkOption { -+ type = lib.types.attrsOf lib.types.str; -+ default = {}; - description = '' - Custom query parameters to send with the Authorize Endpoint request. - ''; -@@ -369,27 +381,27 @@ in - }; - }; - -- allowed_domains = mkOption { -- type = types.listOf types.str; -- default = [ ]; -+ allowed_domains = lib.mkOption { -+ type = lib.types.listOf lib.types.str; -+ default = []; - description = '' - Allowed principal domains. if an authenticated user's domain - is not in this list authentication request will be rejected. - ''; -- example = [ "example.com" ]; -+ example = ["example.com"]; - }; - -- allowed_users = mkOption { -- type = types.listOf types.str; -- default = [ ]; -+ allowed_users = lib.mkOption { -+ type = lib.types.listOf lib.types.str; -+ default = []; - description = '' - Users allowed to authenticate even if not in allowedDomains. - ''; -- example = [ "alice@example.com" ]; -+ example = ["alice@example.com"]; - }; - -- strip_email_domain = mkOption { -- type = types.bool; -+ strip_email_domain = lib.mkOption { -+ type = lib.types.bool; - default = true; - description = '' - Whether the domain part of the email address should be removed when generating namespaces. -@@ -397,16 +409,16 @@ in - }; - }; - -- tls_letsencrypt_hostname = mkOption { -- type = types.nullOr types.str; -+ tls_letsencrypt_hostname = lib.mkOption { -+ type = lib.types.nullOr lib.types.str; - default = ""; - description = '' - Domain name to request a TLS certificate for. - ''; - }; - -- tls_letsencrypt_challenge_type = mkOption { -- type = types.enum [ "TLS-ALPN-01" "HTTP-01" ]; -+ tls_letsencrypt_challenge_type = lib.mkOption { -+ type = lib.types.enum ["TLS-ALPN-01" "HTTP-01"]; - default = "HTTP-01"; - description = '' - Type of ACME challenge to use, currently supported types: -@@ -414,8 +426,8 @@ in - ''; - }; - -- tls_letsencrypt_listen = mkOption { -- type = types.nullOr types.str; -+ tls_letsencrypt_listen = lib.mkOption { -+ type = lib.types.nullOr lib.types.str; - default = ":http"; - description = '' - When HTTP-01 challenge is chosen, letsencrypt must set up a -@@ -424,16 +436,16 @@ in - ''; - }; - -- tls_cert_path = mkOption { -- type = types.nullOr types.path; -+ tls_cert_path = lib.mkOption { -+ type = lib.types.nullOr lib.types.path; - default = null; - description = '' - Path to already created certificate. - ''; - }; - -- tls_key_path = mkOption { -- type = types.nullOr types.path; -+ tls_key_path = lib.mkOption { -+ type = lib.types.nullOr lib.types.path; - default = null; - description = '' - Path to key for already created certificate. -@@ -441,8 +453,8 @@ in - }; - - policy = { -- mode = mkOption { -- type = types.enum [ "file" "database" ]; -+ mode = lib.mkOption { -+ type = lib.types.enum ["file" "database"]; - default = "file"; - description = '' - The mode can be "file" or "database" that defines -@@ -450,8 +462,8 @@ in - ''; - }; - -- path = mkOption { -- type = types.nullOr types.path; -+ path = lib.mkOption { -+ type = lib.types.nullOr lib.types.path; - default = null; - description = '' - If the mode is set to "file", the path to a -@@ -465,50 +477,33 @@ in - }; - }; - -- imports = [ -- (mkRenamedOptionModule [ "services" "headscale" "serverUrl" ] [ "services" "headscale" "settings" "server_url" ]) -- (mkRenamedOptionModule [ "services" "headscale" "derp" "urls" ] [ "services" "headscale" "settings" "derp" "urls" ]) -- (mkRenamedOptionModule [ "services" "headscale" "derp" "paths" ] [ "services" "headscale" "settings" "derp" "paths" ]) -- (mkRenamedOptionModule [ "services" "headscale" "derp" "autoUpdate" ] [ "services" "headscale" "settings" "derp" "auto_update_enable" ]) -- (mkRenamedOptionModule [ "services" "headscale" "derp" "updateFrequency" ] [ "services" "headscale" "settings" "derp" "update_frequency" ]) -- (mkRenamedOptionModule [ "services" "headscale" "ephemeralNodeInactivityTimeout" ] [ "services" "headscale" "settings" "ephemeral_node_inactivity_timeout" ]) -- -- # (mkRenamedOptionModule ["services" "headscale" "settings" "db_type"] ["services" "headscale" "settings" "database" "type"]) -- # (mkRenamedOptionModule ["services" "headscale" "settings" "db_path"] ["services" "headscale" "settings" "database" "sqlite" "path"]) -- # (mkRenamedOptionModule ["services" "headscale" "settings" "db_host"] ["services" "headscale" "settings" "database" "postgres" "host"]) -- # (mkRenamedOptionModule ["services" "headscale" "settings" "db_port"] ["services" "headscale" "settings" "database" "postgres" "port"]) -- # (mkRenamedOptionModule ["services" "headscale" "settings" "db_name"] ["services" "headscale" "settings" "database" "postgres" "name"]) -- # (mkRenamedOptionModule ["services" "headscale" "settings" "db_user"] ["services" "headscale" "settings" "database" "postgres" "user"]) -- # (mkRenamedOptionModule ["services" "headscale" "settings" "db_password_file"] ["services" "headscale" "settings" "database" "postgres" "password_file"]) -- -- (mkRenamedOptionModule [ "services" "headscale" "logLevel" ] [ "services" "headscale" "settings" "log" "level" ]) -- -- # (mkRenamedOptionModule ["services" "headscale" "settings" "dns_config" "nameservers"] ["services" "headscale" "settings" "dns" "nameservers" "global"]) -- # (mkRenamedOptionModule ["services" "headscale" "settings" "dns_config" "domains"] ["services" "headscale" "settings" "dns" "search_domains"]) -- # (mkRenamedOptionModule ["services" "headscale" "settings" "dns_config" "magic_dns"] ["services" "headscale" "settings" "dns" "magic_dns"]) -- # (mkRenamedOptionModule ["services" "headscale" "settings" "dns_config" "base_domain"] ["services" "headscale" "settings" "dns" "base_domain"]) -- -- (mkRenamedOptionModule [ "services" "headscale" "openIdConnect" "issuer" ] [ "services" "headscale" "settings" "oidc" "issuer" ]) -- (mkRenamedOptionModule [ "services" "headscale" "openIdConnect" "clientId" ] [ "services" "headscale" "settings" "oidc" "client_id" ]) -- (mkRenamedOptionModule [ "services" "headscale" "openIdConnect" "clientSecretFile" ] [ "services" "headscale" "settings" "oidc" "client_secret_path" ]) -- (mkRenamedOptionModule [ "services" "headscale" "tls" "letsencrypt" "hostname" ] [ "services" "headscale" "settings" "tls_letsencrypt_hostname" ]) -- (mkRenamedOptionModule [ "services" "headscale" "tls" "letsencrypt" "challengeType" ] [ "services" "headscale" "settings" "tls_letsencrypt_challenge_type" ]) -- (mkRenamedOptionModule [ "services" "headscale" "tls" "letsencrypt" "httpListen" ] [ "services" "headscale" "settings" "tls_letsencrypt_listen" ]) -- (mkRenamedOptionModule [ "services" "headscale" "tls" "certFile" ] [ "services" "headscale" "settings" "tls_cert_path" ]) -- (mkRenamedOptionModule [ "services" "headscale" "tls" "keyFile" ] [ "services" "headscale" "settings" "tls_key_path" ]) -- -- # (mkRenamedOptionModule ["services" "headscale" "settings" "acl_policy_path"] ["services" "headscale" "settings" "policy" "path"]) -- -- (mkRemovedOptionModule [ "services" "headscale" "openIdConnect" "domainMap" ] '' -+ imports = with lib; [ -+ (mkRenamedOptionModule ["services" "headscale" "derp" "autoUpdate"] ["services" "headscale" "settings" "derp" "auto_update_enable"]) -+ (mkRenamedOptionModule ["services" "headscale" "derp" "paths"] ["services" "headscale" "settings" "derp" "paths"]) -+ (mkRenamedOptionModule ["services" "headscale" "derp" "updateFrequency"] ["services" "headscale" "settings" "derp" "update_frequency"]) -+ (mkRenamedOptionModule ["services" "headscale" "derp" "urls"] ["services" "headscale" "settings" "derp" "urls"]) -+ (mkRenamedOptionModule ["services" "headscale" "ephemeralNodeInactivityTimeout"] ["services" "headscale" "settings" "ephemeral_node_inactivity_timeout"]) -+ (mkRenamedOptionModule ["services" "headscale" "logLevel"] ["services" "headscale" "settings" "log" "level"]) -+ (mkRenamedOptionModule ["services" "headscale" "openIdConnect" "clientId"] ["services" "headscale" "settings" "oidc" "client_id"]) -+ (mkRenamedOptionModule ["services" "headscale" "openIdConnect" "clientSecretFile"] ["services" "headscale" "settings" "oidc" "client_secret_path"]) -+ (mkRenamedOptionModule ["services" "headscale" "openIdConnect" "issuer"] ["services" "headscale" "settings" "oidc" "issuer"]) -+ (mkRenamedOptionModule ["services" "headscale" "serverUrl"] ["services" "headscale" "settings" "server_url"]) -+ (mkRenamedOptionModule ["services" "headscale" "tls" "certFile"] ["services" "headscale" "settings" "tls_cert_path"]) -+ (mkRenamedOptionModule ["services" "headscale" "tls" "keyFile"] ["services" "headscale" "settings" "tls_key_path"]) -+ (mkRenamedOptionModule ["services" "headscale" "tls" "letsencrypt" "challengeType"] ["services" "headscale" "settings" "tls_letsencrypt_challenge_type"]) -+ (mkRenamedOptionModule ["services" "headscale" "tls" "letsencrypt" "hostname"] ["services" "headscale" "settings" "tls_letsencrypt_hostname"]) -+ (mkRenamedOptionModule ["services" "headscale" "tls" "letsencrypt" "httpListen"] ["services" "headscale" "settings" "tls_letsencrypt_listen"]) -+ -+ (mkRemovedOptionModule ["services" "headscale" "openIdConnect" "domainMap"] '' - Headscale no longer uses domain_map. If you're using an old version of headscale you can still set this option via services.headscale.settings.oidc.domain_map. - '') - ]; - -- config = mkIf cfg.enable { -- services.headscale.settings = mkMerge [ -+ config = lib.mkIf cfg.enable { -+ services.headscale.settings = lib.mkMerge [ - cliConfig - { -- listen_addr = mkDefault "${cfg.address}:${toString cfg.port}"; -+ listen_addr = lib.mkDefault "${cfg.address}:${toString cfg.port}"; - - tls_letsencrypt_cache_dir = "${dataDir}/.cache"; - } -@@ -519,12 +514,12 @@ in - # to talk to the server instance. - etc."headscale/config.yaml".source = cliConfigFile; - -- systemPackages = [ cfg.package ]; -+ systemPackages = [cfg.package]; - }; - -- users.groups.headscale = mkIf (cfg.group == "headscale") { }; -+ users.groups.headscale = lib.mkIf (cfg.group == "headscale") {}; - -- users.users.headscale = mkIf (cfg.user == "headscale") { -+ users.users.headscale = lib.mkIf (cfg.user == "headscale") { - description = "headscale user"; - home = dataDir; - group = cfg.group; -@@ -533,65 +528,63 @@ in - - systemd.services.headscale = { - description = "headscale coordination server for Tailscale"; -- wants = [ "network-online.target" ]; -- after = [ "network-online.target" ]; -- wantedBy = [ "multi-user.target" ]; -+ wants = ["network-online.target"]; -+ after = ["network-online.target"]; -+ wantedBy = ["multi-user.target"]; - - script = '' -- ${optionalString (cfg.settings.database.postgres.password_file != null) '' -- export HEADSCALE_DATABASE_POSTGRES_PASS="$(head -n1 ${escapeShellArg cfg.settings.database.postgres.password_file})" -+ ${lib.optionalString (cfg.settings.database.postgres.password_file != null) '' -+ export HEADSCALE_DATABASE_POSTGRES_PASS="$(head -n1 ${lib.escapeShellArg cfg.settings.database.postgres.password_file})" - ''} - - exec ${lib.getExe cfg.package} serve --config ${configFile} - ''; - -- serviceConfig = -- let -- capabilityBoundingSet = [ "CAP_CHOWN" ] ++ optional (cfg.port < 1024) "CAP_NET_BIND_SERVICE"; -- in -- { -- Restart = "always"; -- Type = "simple"; -- User = cfg.user; -- Group = cfg.group; -- -- # Hardening options -- RuntimeDirectory = "headscale"; -- # Allow headscale group access so users can be added and use the CLI. -- RuntimeDirectoryMode = "0750"; -- -- StateDirectory = "headscale"; -- StateDirectoryMode = "0750"; -- -- ProtectSystem = "strict"; -- ProtectHome = true; -- PrivateTmp = true; -- PrivateDevices = true; -- ProtectKernelTunables = true; -- ProtectControlGroups = true; -- RestrictSUIDSGID = true; -- PrivateMounts = true; -- ProtectKernelModules = true; -- ProtectKernelLogs = true; -- ProtectHostname = true; -- ProtectClock = true; -- ProtectProc = "invisible"; -- ProcSubset = "pid"; -- RestrictNamespaces = true; -- RemoveIPC = true; -- UMask = "0077"; -- -- CapabilityBoundingSet = capabilityBoundingSet; -- AmbientCapabilities = capabilityBoundingSet; -- NoNewPrivileges = true; -- LockPersonality = true; -- RestrictRealtime = true; -- SystemCallFilter = [ "@system-service" "~@privileged" "@chown" ]; -- SystemCallArchitectures = "native"; -- RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX"; -- }; -+ serviceConfig = let -+ capabilityBoundingSet = ["CAP_CHOWN"] ++ lib.optional (cfg.port < 1024) "CAP_NET_BIND_SERVICE"; -+ in { -+ Restart = "always"; -+ Type = "simple"; -+ User = cfg.user; -+ Group = cfg.group; -+ -+ # Hardening options -+ RuntimeDirectory = "headscale"; -+ # Allow headscale group access so users can be added and use the CLI. -+ RuntimeDirectoryMode = "0750"; -+ -+ StateDirectory = "headscale"; -+ StateDirectoryMode = "0750"; -+ -+ ProtectSystem = "strict"; -+ ProtectHome = true; -+ PrivateTmp = true; -+ PrivateDevices = true; -+ ProtectKernelTunables = true; -+ ProtectControlGroups = true; -+ RestrictSUIDSGID = true; -+ PrivateMounts = true; -+ ProtectKernelModules = true; -+ ProtectKernelLogs = true; -+ ProtectHostname = true; -+ ProtectClock = true; -+ ProtectProc = "invisible"; -+ ProcSubset = "pid"; -+ RestrictNamespaces = true; -+ RemoveIPC = true; -+ UMask = "0077"; -+ -+ CapabilityBoundingSet = capabilityBoundingSet; -+ AmbientCapabilities = capabilityBoundingSet; -+ NoNewPrivileges = true; -+ LockPersonality = true; -+ RestrictRealtime = true; -+ SystemCallFilter = ["@system-service" "~@privileged" "@chown"]; -+ SystemCallArchitectures = "native"; -+ RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX"; -+ }; - }; - }; - -- meta.maintainers = with maintainers; [ kradalby misterio77 ]; -+ meta.maintainers = with lib.maintainers; [kradalby misterio77]; - } --- -2.46.1 - - -From 7b2c10bda8a50ea531a444526a3e89e2851bd681 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Robert=20Sch=C3=BCtz?= -Date: Fri, 11 Oct 2024 13:23:36 -0700 -Subject: [PATCH 3/8] nixos/headscale: assert that server_url does not contain - base_domain - ---- - nixos/modules/services/networking/headscale.nix | 9 +++++++++ - 1 file changed, 9 insertions(+) - -diff --git a/nixos/modules/services/networking/headscale.nix b/nixos/modules/services/networking/headscale.nix -index 622a13fe7b61..c2e616d30e87 100644 ---- a/nixos/modules/services/networking/headscale.nix -+++ b/nixos/modules/services/networking/headscale.nix -@@ -500,6 +500,15 @@ in { - ]; - - config = lib.mkIf cfg.enable { -+ assertions = [ -+ { -+ # This is stricter than it needs to be but is exactly what upstream does: -+ # https://github.com/kradalby/headscale/blob/adc084f20f843d7963c999764fa83939668d2d2c/hscontrol/types/config.go#L799 -+ assertion = with cfg.settings; dns.use_username_in_magic_dns or false || dns.base_domain == "" || !lib.hasInfix dns.base_domain server_url; -+ message = "server_url cannot contain the base_domain, this will cause the headscale server and embedded DERP to become unreachable from the Tailscale node."; -+ } -+ ]; -+ - services.headscale.settings = lib.mkMerge [ - cliConfig - { --- -2.46.1 - - -From 0c1215620750c8ca403827076f3a3d02ce419889 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Robert=20Sch=C3=BCtz?= -Date: Fri, 11 Oct 2024 13:58:20 -0700 -Subject: [PATCH 4/8] nixos/headscale: don't set deprecated options in config - -We cannot use `mkRenamedOptionModule` or `mkRemovedOptionModule` inside -a freeform option. Thus we have to manually assert these deprecated -options aren't used rather than aliasing them to their replacement. ---- - .../modules/services/networking/headscale.nix | 31 ++++++++++--------- - 1 file changed, 16 insertions(+), 15 deletions(-) - -diff --git a/nixos/modules/services/networking/headscale.nix b/nixos/modules/services/networking/headscale.nix -index c2e616d30e87..fd2fd8dbede9 100644 ---- a/nixos/modules/services/networking/headscale.nix -+++ b/nixos/modules/services/networking/headscale.nix -@@ -20,6 +20,11 @@ - settingsFormat = pkgs.formats.yaml {}; - configFile = settingsFormat.generate "headscale.yaml" cfg.settings; - cliConfigFile = settingsFormat.generate "headscale.yaml" cliConfig; -+ -+ assertRemovedOption = option: message: { -+ assertion = !lib.hasAttrByPath option cfg; -+ message = "The option `services.headscale.${lib.options.showOption option}` was removed. " + message; -+ }; - in { - options = { - services.headscale = { -@@ -82,21 +87,6 @@ in { - type = lib.types.submodule { - freeformType = settingsFormat.type; - -- imports = with lib; [ -- (mkAliasOptionModule ["acl_policy_path"] ["policy" "path"]) -- (mkAliasOptionModule ["db_host"] ["database" "postgres" "host"]) -- (mkAliasOptionModule ["db_name"] ["database" "postgres" "name"]) -- (mkAliasOptionModule ["db_password_file"] ["database" "postgres" "password_file"]) -- (mkAliasOptionModule ["db_path"] ["database" "sqlite" "path"]) -- (mkAliasOptionModule ["db_port"] ["database" "postgres" "port"]) -- (mkAliasOptionModule ["db_type"] ["database" "type"]) -- (mkAliasOptionModule ["db_user"] ["database" "postgres" "user"]) -- (mkAliasOptionModule ["dns_config" "base_domain"] ["dns" "base_domain"]) -- (mkAliasOptionModule ["dns_config" "domains"] ["dns" "search_domains"]) -- (mkAliasOptionModule ["dns_config" "magic_dns"] ["dns" "magic_dns"]) -- (mkAliasOptionModule ["dns_config" "nameservers"] ["dns" "nameservers" "global"]) -- ]; -- - options = { - server_url = lib.mkOption { - type = lib.types.str; -@@ -507,6 +497,17 @@ in { - assertion = with cfg.settings; dns.use_username_in_magic_dns or false || dns.base_domain == "" || !lib.hasInfix dns.base_domain server_url; - message = "server_url cannot contain the base_domain, this will cause the headscale server and embedded DERP to become unreachable from the Tailscale node."; - } -+ (assertRemovedOption ["settings" "acl_policy_path"] "Use `policy.path` instead.") -+ (assertRemovedOption ["settings" "db_host"] "Use `database.postgres.host` instead.") -+ (assertRemovedOption ["settings" "db_name"] "Use `database.postgres.name` instead.") -+ (assertRemovedOption ["settings" "db_password_file"] "Use `database.postgres.password_file` instead.") -+ (assertRemovedOption ["settings" "db_path"] "Use `database.sqlite.path` instead.") -+ (assertRemovedOption ["settings" "db_port"] "Use `database.postgres.port` instead.") -+ (assertRemovedOption ["settings" "db_type"] "Use `database.type` instead.") -+ (assertRemovedOption ["settings" "db_user"] "Use `database.postgres.user` instead.") -+ (assertRemovedOption ["settings" "dns_config"] "Use `dns` instead.") -+ (assertRemovedOption ["settings" "dns_config" "domains"] "Use `dns.search_domains` instead.") -+ (assertRemovedOption ["settings" "dns_config" "nameservers"] "Use `dns.nameservers.global` instead.") - ]; - - services.headscale.settings = lib.mkMerge [ --- -2.46.1 - - -From 31de80dcfee97b024298128836c8ab49ca7ae798 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Robert=20Sch=C3=BCtz?= -Date: Fri, 11 Oct 2024 20:17:15 -0700 -Subject: [PATCH 5/8] nixos/headscale: update option descriptions - ---- - nixos/modules/services/networking/headscale.nix | 11 ++++++----- - 1 file changed, 6 insertions(+), 5 deletions(-) - -diff --git a/nixos/modules/services/networking/headscale.nix b/nixos/modules/services/networking/headscale.nix -index fd2fd8dbede9..aac6d331a027 100644 ---- a/nixos/modules/services/networking/headscale.nix -+++ b/nixos/modules/services/networking/headscale.nix -@@ -289,7 +289,6 @@ in { - default = true; - description = '' - Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/). -- Only works if there is at least a nameserver defined. - ''; - example = false; - }; -@@ -299,11 +298,13 @@ in { - default = ""; - description = '' - Defines the base domain to create the hostnames for MagicDNS. -- {option}`baseDomain` must be a FQDNs, without the trailing dot. -- The FQDN of the hosts will be -- `hostname.namespace.base_domain` (e.g. -- `myhost.mynamespace.example.com`). -+ This domain must be different from the {option}`server_url` -+ domain. -+ {option}`base_domain` must be a FQDN, without the trailing dot. -+ The FQDN of the hosts will be `hostname.base_domain` (e.g. -+ `myhost.tailnet.example.com`). - ''; -+ example = "tailnet.example.com"; - }; - - nameservers = { --- -2.46.1 - - -From 083dec59811d7bfcaed1a34fc33d33eabc40fcc9 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Robert=20Sch=C3=BCtz?= -Date: Sat, 12 Oct 2024 18:28:17 -0700 -Subject: [PATCH 6/8] nixos/headscale: assert that dns.base_domain is set when - using MagicDNS - ---- - nixos/modules/services/networking/headscale.nix | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/nixos/modules/services/networking/headscale.nix b/nixos/modules/services/networking/headscale.nix -index aac6d331a027..9261ec03c532 100644 ---- a/nixos/modules/services/networking/headscale.nix -+++ b/nixos/modules/services/networking/headscale.nix -@@ -498,6 +498,10 @@ in { - assertion = with cfg.settings; dns.use_username_in_magic_dns or false || dns.base_domain == "" || !lib.hasInfix dns.base_domain server_url; - message = "server_url cannot contain the base_domain, this will cause the headscale server and embedded DERP to become unreachable from the Tailscale node."; - } -+ { -+ assertion = with cfg.settings; dns.magic_dns -> dns.base_domain != ""; -+ message = "dns.base_domain must be set when using MagicDNS"; -+ } - (assertRemovedOption ["settings" "acl_policy_path"] "Use `policy.path` instead.") - (assertRemovedOption ["settings" "db_host"] "Use `database.postgres.host` instead.") - (assertRemovedOption ["settings" "db_name"] "Use `database.postgres.name` instead.") --- -2.46.1 - - -From 720b90273528c02a0fb865062fe44a45eb04f82e Mon Sep 17 00:00:00 2001 -From: Jennifer Graul -Date: Wed, 13 Sep 2023 13:32:47 +0200 -Subject: [PATCH 7/8] headscale: fix reacting to SIGTERM - -The current version of headscale does not react to SIGTERMs and so it -can only be terminated by a SIGKILL at the moment. This commit provides -a patch to fix this. ---- - pkgs/servers/headscale/default.nix | 4 ++++ - pkgs/servers/headscale/sigterm-fix.patch | 12 ++++++++++++ - 2 files changed, 16 insertions(+) - create mode 100644 pkgs/servers/headscale/sigterm-fix.patch - -diff --git a/pkgs/servers/headscale/default.nix b/pkgs/servers/headscale/default.nix -index fca56f5fc848..eb9ad79b1888 100644 ---- a/pkgs/servers/headscale/default.nix -+++ b/pkgs/servers/headscale/default.nix -@@ -21,6 +21,10 @@ buildGoModule rec { - patches = [ - # backport of https://github.com/juanfont/headscale/pull/1697 - ./trim-oidc-secret-path.patch -+ -+ # fix for headscale not reacting to SIGTERM -+ # see https://github.com/juanfont/headscale/pull/1480 and https://github.com/juanfont/headscale/issues/1461 -+ ./sigterm-fix.patch - ]; - - ldflags = ["-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}"]; -diff --git a/pkgs/servers/headscale/sigterm-fix.patch b/pkgs/servers/headscale/sigterm-fix.patch -new file mode 100644 -index 000000000000..22a97d98caac ---- /dev/null -+++ b/pkgs/servers/headscale/sigterm-fix.patch -@@ -0,0 +1,12 @@ -+diff --git a/hscontrol/app.go b/hscontrol/app.go -+index b8dceba..4bcf019 100644 -+--- a/hscontrol/app.go -++++ b/hscontrol/app.go -+@@ -821,6 +821,7 @@ func (h *Headscale) Serve() error { -+ -+ // And we're done: -+ cancel() -++ return -+ } -+ } -+ } --- -2.46.1 - - -From 1a1fa839c7c8fafdea120b8b9af28d8839f8cafb Mon Sep 17 00:00:00 2001 -From: Kristoffer Dalby -Date: Fri, 6 Sep 2024 13:32:52 +0200 -Subject: [PATCH 8/8] headscale: 0.22.3 -> 0.23.0 - -Signed-off-by: Kristoffer Dalby ---- - pkgs/servers/headscale/default.nix | 20 +++++-------------- - pkgs/servers/headscale/sigterm-fix.patch | 12 ----------- - .../headscale/trim-oidc-secret-path.patch | 13 ------------ - pkgs/top-level/all-packages.nix | 4 +++- - 4 files changed, 8 insertions(+), 41 deletions(-) - delete mode 100644 pkgs/servers/headscale/sigterm-fix.patch - delete mode 100644 pkgs/servers/headscale/trim-oidc-secret-path.patch - -diff --git a/pkgs/servers/headscale/default.nix b/pkgs/servers/headscale/default.nix -index eb9ad79b1888..9f127946217e 100644 ---- a/pkgs/servers/headscale/default.nix -+++ b/pkgs/servers/headscale/default.nix -@@ -7,33 +7,22 @@ - }: - buildGoModule rec { - pname = "headscale"; -- version = "0.22.3"; -+ version = "0.23.0"; - - src = fetchFromGitHub { - owner = "juanfont"; - repo = "headscale"; - rev = "v${version}"; -- hash = "sha256-nqmTqe3F3Oh8rnJH0clwACD/0RpqmfOMXNubr3C8rEc="; -+ hash = "sha256-5tlnVNpn+hJayxHjTpbOO3kRInOYOFz0pe9pwjXZlBE="; - }; - -- vendorHash = "sha256-IOkbbFtE6+tNKnglE/8ZuNxhPSnloqM2sLgTvagMmnc="; -- -- patches = [ -- # backport of https://github.com/juanfont/headscale/pull/1697 -- ./trim-oidc-secret-path.patch -- -- # fix for headscale not reacting to SIGTERM -- # see https://github.com/juanfont/headscale/pull/1480 and https://github.com/juanfont/headscale/issues/1461 -- ./sigterm-fix.patch -- ]; -+ vendorHash = "sha256-+8dOxPG/Q+wuHgRwwWqdphHOuop0W9dVyClyQuh7aRc="; - - ldflags = ["-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}"]; - - nativeBuildInputs = [installShellFiles]; - checkFlags = ["-short"]; - -- tags = ["ts2019"]; -- - postInstall = '' - installShellCompletion --cmd headscale \ - --bash <($out/bin/headscale completion bash) \ -@@ -41,7 +30,7 @@ buildGoModule rec { - --zsh <($out/bin/headscale completion zsh) - ''; - -- passthru.tests = { inherit (nixosTests) headscale; }; -+ passthru.tests = {inherit (nixosTests) headscale;}; - - meta = with lib; { - homepage = "https://github.com/juanfont/headscale"; -@@ -63,6 +52,7 @@ buildGoModule rec { - Headscale implements this coordination server. - ''; - license = licenses.bsd3; -+ mainProgram = "headscale"; - maintainers = with maintainers; [nkje jk kradalby misterio77 ghuntley]; - }; - } -diff --git a/pkgs/servers/headscale/sigterm-fix.patch b/pkgs/servers/headscale/sigterm-fix.patch -deleted file mode 100644 -index 22a97d98caac..000000000000 ---- a/pkgs/servers/headscale/sigterm-fix.patch -+++ /dev/null -@@ -1,12 +0,0 @@ --diff --git a/hscontrol/app.go b/hscontrol/app.go --index b8dceba..4bcf019 100644 ----- a/hscontrol/app.go --+++ b/hscontrol/app.go --@@ -821,6 +821,7 @@ func (h *Headscale) Serve() error { -- -- // And we're done: -- cancel() --+ return -- } -- } -- } -diff --git a/pkgs/servers/headscale/trim-oidc-secret-path.patch b/pkgs/servers/headscale/trim-oidc-secret-path.patch -deleted file mode 100644 -index 4275988aa7db..000000000000 ---- a/pkgs/servers/headscale/trim-oidc-secret-path.patch -+++ /dev/null -@@ -1,13 +0,0 @@ --diff --git a/hscontrol/config.go b/hscontrol/config.go --index 0e83a1c..71fbfb0 100644 ----- a/hscontrol/config.go --+++ b/hscontrol/config.go --@@ -573,7 +573,7 @@ func GetHeadscaleConfig() (*Config, error) { -- if err != nil { -- return nil, err -- } --- oidcClientSecret = string(secretBytes) --+ oidcClientSecret = strings.TrimSpace(string(secretBytes)) -- } -- -- return &Config{ -diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix -index 39aac77560e1..c1811da9b6a4 100644 ---- a/pkgs/top-level/all-packages.nix -+++ b/pkgs/top-level/all-packages.nix -@@ -8912,7 +8912,9 @@ with pkgs; - - heimdall-gui = heimdall.override { enableGUI = true; }; - -- headscale = callPackage ../servers/headscale { }; -+ headscale = callPackage ../servers/headscale { -+ buildGoModule = buildGo123Module; -+ }; - - health = callPackage ../applications/misc/health { }; - --- -2.46.1 - diff --git a/roles/common/programs/eza.nix b/roles/common/programs/eza.nix index f9e980d..e15b9db 100644 --- a/roles/common/programs/eza.nix +++ b/roles/common/programs/eza.nix @@ -9,10 +9,7 @@ enable = true; enableBashIntegration = true; git = true; - icons = - if config.system.nixos.release == "24.05" - then true - else "auto"; + icons = "auto"; }; xdg.configFile."eza/theme.yml".source = "${flakeSelf.inputs.eza-themes}/themes/catppuccin.yml"; }; diff --git a/roles/server/samba.nix b/roles/server/samba.nix index f4fd086..85f3c9f 100755 --- a/roles/server/samba.nix +++ b/roles/server/samba.nix @@ -3,11 +3,11 @@ samba = { enable = true; openFirewall = true; - extraConfig = '' - map to guest = bad user - guest account = transmission - ''; - shares = { + settings = { + "global" = { + "map to guest" = "bad user"; + "guest account" = "transmission"; + }; "Transmission downloads" = { path = "${config.services.transmission.settings.download-dir}"; "read only" = true;