nixos/headscale: update module to headscale 0.23.0

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
Kristoffer Dalby 2024-09-18 10:24:19 +01:00 committed by Sandro Jäckel
parent aec399ee4b
commit abb3b0089b
No known key found for this signature in database
GPG Key ID: 3AF5A43A3EECC2E5

View File

@ -1,9 +1,9 @@
{ config {
, lib config,
, pkgs lib,
, ... pkgs,
}: ...
with lib; let }: let
cfg = config.services.headscale; cfg = config.services.headscale;
dataDir = "/var/lib/headscale"; dataDir = "/var/lib/headscale";
@ -20,17 +20,16 @@ with lib; let
settingsFormat = pkgs.formats.yaml {}; settingsFormat = pkgs.formats.yaml {};
configFile = settingsFormat.generate "headscale.yaml" cfg.settings; configFile = settingsFormat.generate "headscale.yaml" cfg.settings;
cliConfigFile = settingsFormat.generate "headscale.yaml" cliConfig; cliConfigFile = settingsFormat.generate "headscale.yaml" cliConfig;
in in {
{
options = { options = {
services.headscale = { 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"; default = "headscale";
type = types.str; type = lib.types.str;
description = '' description = ''
User account under which headscale runs. User account under which headscale runs.
@ -42,9 +41,9 @@ in
''; '';
}; };
group = mkOption { group = lib.mkOption {
default = "headscale"; default = "headscale";
type = types.str; type = lib.types.str;
description = '' description = ''
Group under which headscale runs. Group under which headscale runs.
@ -56,8 +55,8 @@ in
''; '';
}; };
address = mkOption { address = lib.mkOption {
type = types.str; type = lib.types.str;
default = "127.0.0.1"; default = "127.0.0.1";
description = '' description = ''
Listening address of headscale. Listening address of headscale.
@ -65,8 +64,8 @@ in
example = "0.0.0.0"; example = "0.0.0.0";
}; };
port = mkOption { port = lib.mkOption {
type = types.port; type = lib.types.port;
default = 8080; default = 8080;
description = '' description = ''
Listening port of headscale. Listening port of headscale.
@ -74,18 +73,33 @@ in
example = 443; example = 443;
}; };
settings = mkOption { settings = lib.mkOption {
description = '' description = ''
Overrides to {file}`config.yaml` as a Nix attribute set. Overrides to {file}`config.yaml` as a Nix attribute set.
Check the [example config](https://github.com/juanfont/headscale/blob/main/config-example.yaml) Check the [example config](https://github.com/juanfont/headscale/blob/main/config-example.yaml)
for possible options. for possible options.
''; '';
type = types.submodule { type = lib.types.submodule {
freeformType = settingsFormat.type; 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 = { options = {
server_url = mkOption { server_url = lib.mkOption {
type = types.str; type = lib.types.str;
default = "http://127.0.0.1:8080"; default = "http://127.0.0.1:8080";
description = '' description = ''
The url clients will connect to. The url clients will connect to.
@ -93,38 +107,36 @@ in
example = "https://myheadscale.example.com:443"; example = "https://myheadscale.example.com:443";
}; };
noise.private_key_path = mkOption { noise.private_key_path = lib.mkOption {
type = types.path; type = lib.types.path;
default = "${dataDir}/noise_private.key"; default = "${dataDir}/noise_private.key";
description = '' description = ''
Path to noise private key file, generated automatically if it does not exist. Path to noise private key file, generated automatically if it does not exist.
''; '';
}; };
prefixes = prefixes = let
let
prefDesc = '' prefDesc = ''
Each prefix consists of either an IPv4 or IPv6 address, Each prefix consists of either an IPv4 or IPv6 address,
and the associated prefix length, delimited by a slash. and the associated prefix length, delimited by a slash.
It must be within IP ranges supported by the Tailscale 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. client - i.e., subnets of 100.64.0.0/10 and fd7a:115c:a1e0::/48.
''; '';
in in {
{ v4 = lib.mkOption {
v4 = mkOption { type = lib.types.str;
type = types.str;
default = "100.64.0.0/10"; default = "100.64.0.0/10";
description = prefDesc; description = prefDesc;
}; };
v6 = mkOption { v6 = lib.mkOption {
type = types.str; type = lib.types.str;
default = "fd7a:115c:a1e0::/48"; default = "fd7a:115c:a1e0::/48";
description = prefDesc; description = prefDesc;
}; };
allocation = mkOption { allocation = lib.mkOption {
type = types.enum [ "sequential" "random" ]; type = lib.types.enum ["sequential" "random"];
example = "random"; example = "random";
default = "sequential"; default = "sequential";
description = '' description = ''
@ -136,8 +148,8 @@ in
}; };
derp = { derp = {
urls = mkOption { urls = lib.mkOption {
type = types.listOf types.str; type = lib.types.listOf lib.types.str;
default = ["https://controlplane.tailscale.com/derpmap/default"]; default = ["https://controlplane.tailscale.com/derpmap/default"];
description = '' description = ''
List of urls containing DERP maps. List of urls containing DERP maps.
@ -145,8 +157,8 @@ in
''; '';
}; };
paths = mkOption { paths = lib.mkOption {
type = types.listOf types.path; type = lib.types.listOf lib.types.path;
default = []; default = [];
description = '' description = ''
List of file paths containing DERP maps. List of file paths containing DERP maps.
@ -154,8 +166,8 @@ in
''; '';
}; };
auto_update_enable = mkOption { auto_update_enable = lib.mkOption {
type = types.bool; type = lib.types.bool;
default = true; default = true;
description = '' description = ''
Whether to automatically update DERP maps on a set frequency. Whether to automatically update DERP maps on a set frequency.
@ -163,8 +175,8 @@ in
example = false; example = false;
}; };
update_frequency = mkOption { update_frequency = lib.mkOption {
type = types.str; type = lib.types.str;
default = "24h"; default = "24h";
description = '' description = ''
Frequency to update DERP maps. Frequency to update DERP maps.
@ -181,8 +193,8 @@ in
}; };
}; };
ephemeral_node_inactivity_timeout = mkOption { ephemeral_node_inactivity_timeout = lib.mkOption {
type = types.str; type = lib.types.str;
default = "30m"; default = "30m";
description = '' description = ''
Time before an inactive ephemeral node is deleted. Time before an inactive ephemeral node is deleted.
@ -191,8 +203,8 @@ in
}; };
database = { database = {
type = mkOption { type = lib.mkOption {
type = types.enum [ "sqlite" "sqlite3" "postgres" ]; type = lib.types.enum ["sqlite" "sqlite3" "postgres"];
example = "postgres"; example = "postgres";
default = "sqlite"; default = "sqlite";
description = '' description = ''
@ -203,14 +215,14 @@ in
}; };
sqlite = { sqlite = {
path = mkOption { path = lib.mkOption {
type = types.nullOr types.str; type = lib.types.nullOr lib.types.str;
default = "${dataDir}/db.sqlite"; default = "${dataDir}/db.sqlite";
description = "Path to the sqlite3 database file."; description = "Path to the sqlite3 database file.";
}; };
write_ahead_log = mkOption { write_ahead_log = lib.mkOption {
type = types.bool; type = lib.types.bool;
default = true; default = true;
description = '' description = ''
Enable WAL mode for SQLite. This is recommended for production environments. Enable WAL mode for SQLite. This is recommended for production environments.
@ -221,36 +233,36 @@ in
}; };
postgres = { postgres = {
host = mkOption { host = lib.mkOption {
type = types.nullOr types.str; type = lib.types.nullOr lib.types.str;
default = null; default = null;
example = "127.0.0.1"; example = "127.0.0.1";
description = "Database host address."; description = "Database host address.";
}; };
port = mkOption { port = lib.mkOption {
type = types.nullOr types.port; type = lib.types.nullOr lib.types.port;
default = null; default = null;
example = 3306; example = 3306;
description = "Database host port."; description = "Database host port.";
}; };
name = mkOption { name = lib.mkOption {
type = types.nullOr types.str; type = lib.types.nullOr lib.types.str;
default = null; default = null;
example = "headscale"; example = "headscale";
description = "Database name."; description = "Database name.";
}; };
user = mkOption { user = lib.mkOption {
type = types.nullOr types.str; type = lib.types.nullOr lib.types.str;
default = null; default = null;
example = "headscale"; example = "headscale";
description = "Database user."; description = "Database user.";
}; };
password_file = mkOption { password_file = lib.mkOption {
type = types.nullOr types.path; type = lib.types.nullOr lib.types.path;
default = null; default = null;
example = "/run/keys/headscale-dbpassword"; example = "/run/keys/headscale-dbpassword";
description = '' description = ''
@ -262,8 +274,8 @@ in
}; };
log = { log = {
level = mkOption { level = lib.mkOption {
type = types.str; type = lib.types.str;
default = "info"; default = "info";
description = '' description = ''
headscale log level. headscale log level.
@ -271,8 +283,8 @@ in
example = "debug"; example = "debug";
}; };
format = mkOption { format = lib.mkOption {
type = types.str; type = lib.types.str;
default = "text"; default = "text";
description = '' description = ''
headscale log format. headscale log format.
@ -282,8 +294,8 @@ in
}; };
dns = { dns = {
magic_dns = mkOption { magic_dns = lib.mkOption {
type = types.bool; type = lib.types.bool;
default = true; default = true;
description = '' description = ''
Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/). Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/).
@ -292,8 +304,8 @@ in
example = false; example = false;
}; };
base_domain = mkOption { base_domain = lib.mkOption {
type = types.str; type = lib.types.str;
default = ""; default = "";
description = '' description = ''
Defines the base domain to create the hostnames for MagicDNS. Defines the base domain to create the hostnames for MagicDNS.
@ -305,8 +317,8 @@ in
}; };
nameservers = { nameservers = {
global = mkOption { global = lib.mkOption {
type = types.listOf types.str; type = lib.types.listOf lib.types.str;
default = []; default = [];
description = '' description = ''
List of nameservers to pass to Tailscale clients. List of nameservers to pass to Tailscale clients.
@ -314,8 +326,8 @@ in
}; };
}; };
search_domains = mkOption { search_domains = lib.mkOption {
type = types.listOf types.str; type = lib.types.listOf lib.types.str;
default = []; default = [];
description = '' description = ''
Search domains to inject to Tailscale clients. Search domains to inject to Tailscale clients.
@ -325,8 +337,8 @@ in
}; };
oidc = { oidc = {
issuer = mkOption { issuer = lib.mkOption {
type = types.str; type = lib.types.str;
default = ""; default = "";
description = '' description = ''
URL to OpenID issuer. URL to OpenID issuer.
@ -334,32 +346,32 @@ in
example = "https://openid.example.com"; example = "https://openid.example.com";
}; };
client_id = mkOption { client_id = lib.mkOption {
type = types.str; type = lib.types.str;
default = ""; default = "";
description = '' description = ''
OpenID Connect client ID. OpenID Connect client ID.
''; '';
}; };
client_secret_path = mkOption { client_secret_path = lib.mkOption {
type = types.nullOr types.str; type = lib.types.nullOr lib.types.str;
default = null; default = null;
description = '' description = ''
Path to OpenID Connect client secret file. Expands environment variables in format ''${VAR}. Path to OpenID Connect client secret file. Expands environment variables in format ''${VAR}.
''; '';
}; };
scope = mkOption { scope = lib.mkOption {
type = types.listOf types.str; type = lib.types.listOf lib.types.str;
default = ["openid" "profile" "email"]; default = ["openid" "profile" "email"];
description = '' description = ''
Scopes used in the OIDC flow. Scopes used in the OIDC flow.
''; '';
}; };
extra_params = mkOption { extra_params = lib.mkOption {
type = types.attrsOf types.str; type = lib.types.attrsOf lib.types.str;
default = {}; default = {};
description = '' description = ''
Custom query parameters to send with the Authorize Endpoint request. Custom query parameters to send with the Authorize Endpoint request.
@ -369,8 +381,8 @@ in
}; };
}; };
allowed_domains = mkOption { allowed_domains = lib.mkOption {
type = types.listOf types.str; type = lib.types.listOf lib.types.str;
default = []; default = [];
description = '' description = ''
Allowed principal domains. if an authenticated user's domain Allowed principal domains. if an authenticated user's domain
@ -379,8 +391,8 @@ in
example = ["example.com"]; example = ["example.com"];
}; };
allowed_users = mkOption { allowed_users = lib.mkOption {
type = types.listOf types.str; type = lib.types.listOf lib.types.str;
default = []; default = [];
description = '' description = ''
Users allowed to authenticate even if not in allowedDomains. Users allowed to authenticate even if not in allowedDomains.
@ -388,8 +400,8 @@ in
example = ["alice@example.com"]; example = ["alice@example.com"];
}; };
strip_email_domain = mkOption { strip_email_domain = lib.mkOption {
type = types.bool; type = lib.types.bool;
default = true; default = true;
description = '' description = ''
Whether the domain part of the email address should be removed when generating namespaces. Whether the domain part of the email address should be removed when generating namespaces.
@ -397,16 +409,16 @@ in
}; };
}; };
tls_letsencrypt_hostname = mkOption { tls_letsencrypt_hostname = lib.mkOption {
type = types.nullOr types.str; type = lib.types.nullOr lib.types.str;
default = ""; default = "";
description = '' description = ''
Domain name to request a TLS certificate for. Domain name to request a TLS certificate for.
''; '';
}; };
tls_letsencrypt_challenge_type = mkOption { tls_letsencrypt_challenge_type = lib.mkOption {
type = types.enum [ "TLS-ALPN-01" "HTTP-01" ]; type = lib.types.enum ["TLS-ALPN-01" "HTTP-01"];
default = "HTTP-01"; default = "HTTP-01";
description = '' description = ''
Type of ACME challenge to use, currently supported types: Type of ACME challenge to use, currently supported types:
@ -414,8 +426,8 @@ in
''; '';
}; };
tls_letsencrypt_listen = mkOption { tls_letsencrypt_listen = lib.mkOption {
type = types.nullOr types.str; type = lib.types.nullOr lib.types.str;
default = ":http"; default = ":http";
description = '' description = ''
When HTTP-01 challenge is chosen, letsencrypt must set up a When HTTP-01 challenge is chosen, letsencrypt must set up a
@ -424,16 +436,16 @@ in
''; '';
}; };
tls_cert_path = mkOption { tls_cert_path = lib.mkOption {
type = types.nullOr types.path; type = lib.types.nullOr lib.types.path;
default = null; default = null;
description = '' description = ''
Path to already created certificate. Path to already created certificate.
''; '';
}; };
tls_key_path = mkOption { tls_key_path = lib.mkOption {
type = types.nullOr types.path; type = lib.types.nullOr lib.types.path;
default = null; default = null;
description = '' description = ''
Path to key for already created certificate. Path to key for already created certificate.
@ -441,8 +453,8 @@ in
}; };
policy = { policy = {
mode = mkOption { mode = lib.mkOption {
type = types.enum [ "file" "database" ]; type = lib.types.enum ["file" "database"];
default = "file"; default = "file";
description = '' description = ''
The mode can be "file" or "database" that defines The mode can be "file" or "database" that defines
@ -450,8 +462,8 @@ in
''; '';
}; };
path = mkOption { path = lib.mkOption {
type = types.nullOr types.path; type = lib.types.nullOr lib.types.path;
default = null; default = null;
description = '' description = ''
If the mode is set to "file", the path to a If the mode is set to "file", the path to a
@ -465,50 +477,33 @@ in
}; };
}; };
imports = [ imports = with lib; [
(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" "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" "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" "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" "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" "clientId"] ["services" "headscale" "settings" "oidc" "client_id"])
(mkRenamedOptionModule ["services" "headscale" "openIdConnect" "clientSecretFile"] ["services" "headscale" "settings" "oidc" "client_secret_path"]) (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" "openIdConnect" "issuer"] ["services" "headscale" "settings" "oidc" "issuer"])
(mkRenamedOptionModule [ "services" "headscale" "tls" "letsencrypt" "challengeType" ] [ "services" "headscale" "settings" "tls_letsencrypt_challenge_type" ]) (mkRenamedOptionModule ["services" "headscale" "serverUrl"] ["services" "headscale" "settings" "server_url"])
(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" "certFile"] ["services" "headscale" "settings" "tls_cert_path"])
(mkRenamedOptionModule ["services" "headscale" "tls" "keyFile"] ["services" "headscale" "settings" "tls_key_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" "settings" "acl_policy_path"] ["services" "headscale" "settings" "policy" "path"]) (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"] '' (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. 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 { config = lib.mkIf cfg.enable {
services.headscale.settings = mkMerge [ services.headscale.settings = lib.mkMerge [
cliConfig cliConfig
{ {
listen_addr = mkDefault "${cfg.address}:${toString cfg.port}"; listen_addr = lib.mkDefault "${cfg.address}:${toString cfg.port}";
tls_letsencrypt_cache_dir = "${dataDir}/.cache"; tls_letsencrypt_cache_dir = "${dataDir}/.cache";
} }
@ -522,9 +517,9 @@ in
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"; description = "headscale user";
home = dataDir; home = dataDir;
group = cfg.group; group = cfg.group;
@ -538,18 +533,16 @@ in
wantedBy = ["multi-user.target"]; wantedBy = ["multi-user.target"];
script = '' script = ''
${optionalString (cfg.settings.database.postgres.password_file != null) '' ${lib.optionalString (cfg.settings.database.postgres.password_file != null) ''
export HEADSCALE_DATABASE_POSTGRES_PASS="$(head -n1 ${escapeShellArg cfg.settings.database.postgres.password_file})" export HEADSCALE_DATABASE_POSTGRES_PASS="$(head -n1 ${lib.escapeShellArg cfg.settings.database.postgres.password_file})"
''} ''}
exec ${lib.getExe cfg.package} serve --config ${configFile} exec ${lib.getExe cfg.package} serve --config ${configFile}
''; '';
serviceConfig = serviceConfig = let
let capabilityBoundingSet = ["CAP_CHOWN"] ++ lib.optional (cfg.port < 1024) "CAP_NET_BIND_SERVICE";
capabilityBoundingSet = [ "CAP_CHOWN" ] ++ optional (cfg.port < 1024) "CAP_NET_BIND_SERVICE"; in {
in
{
Restart = "always"; Restart = "always";
Type = "simple"; Type = "simple";
User = cfg.user; User = cfg.user;
@ -593,5 +586,5 @@ in
}; };
}; };
meta.maintainers = with maintainers; [ kradalby misterio77 ]; meta.maintainers = with lib.maintainers; [kradalby misterio77];
} }