1757 lines
72 KiB
Diff
1757 lines
72 KiB
Diff
From 4a5c88eac3aaf1fb3d5ab15ee84787b9883c2465 Mon Sep 17 00:00:00 2001
|
|
From: Kristoffer Dalby <kristoffer@tailscale.com>
|
|
Date: Fri, 6 Sep 2024 12:47:36 +0200
|
|
Subject: [PATCH 1/8] nixos/headscale: modernize
|
|
|
|
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
|
|
---
|
|
.../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 <kristoffer@tailscale.com>
|
|
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 <kristoffer@tailscale.com>
|
|
---
|
|
.../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?= <mail@dotlambda.de>
|
|
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?= <mail@dotlambda.de>
|
|
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?= <mail@dotlambda.de>
|
|
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?= <mail@dotlambda.de>
|
|
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 <jennifer.graul@wobcom.de>
|
|
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 <kristoffer@tailscale.com>
|
|
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 <kristoffer@tailscale.com>
|
|
---
|
|
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
|
|
|