nixos/headscale: update module to headscale 0.23.0
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
parent
aec399ee4b
commit
abb3b0089b
@ -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";
|
||||||
@ -17,20 +17,19 @@ with lib; let
|
|||||||
unix_socket = "${runDir}/headscale.sock";
|
unix_socket = "${runDir}/headscale.sock";
|
||||||
};
|
};
|
||||||
|
|
||||||
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,69 +107,67 @@ 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 {
|
||||||
{
|
type = lib.types.str;
|
||||||
v4 = mkOption {
|
default = "100.64.0.0/10";
|
||||||
type = types.str;
|
description = prefDesc;
|
||||||
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).
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
v6 = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "fd7a:115c:a1e0::/48";
|
||||||
|
description = prefDesc;
|
||||||
|
};
|
||||||
|
|
||||||
|
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 = {
|
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.
|
||||||
See [How Tailscale works](https://tailscale.com/blog/how-tailscale-works/) for more information on DERP maps.
|
See [How Tailscale works](https://tailscale.com/blog/how-tailscale-works/) for more information on DERP maps.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
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.
|
||||||
See [How Tailscale works](https://tailscale.com/blog/how-tailscale-works/) for more information on DERP maps.
|
See [How Tailscale works](https://tailscale.com/blog/how-tailscale-works/) for more information on DERP maps.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
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,28 +317,28 @@ 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.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
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.
|
||||||
'';
|
'';
|
||||||
example = [ "mydomain.internal" ];
|
example = ["mydomain.internal"];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
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,33 +346,33 @@ 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,27 +381,27 @@ 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
|
||||||
is not in this list authentication request will be rejected.
|
is not in this list authentication request will be rejected.
|
||||||
'';
|
'';
|
||||||
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.
|
||||||
'';
|
'';
|
||||||
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" "autoUpdate"] ["services" "headscale" "settings" "derp" "auto_update_enable"])
|
||||||
(mkRenamedOptionModule [ "services" "headscale" "derp" "urls" ] [ "services" "headscale" "settings" "derp" "urls" ])
|
(mkRenamedOptionModule ["services" "headscale" "derp" "paths"] ["services" "headscale" "settings" "derp" "paths"])
|
||||||
(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" "autoUpdate" ] [ "services" "headscale" "settings" "derp" "auto_update_enable" ])
|
(mkRenamedOptionModule ["services" "headscale" "derp" "urls"] ["services" "headscale" "settings" "derp" "urls"])
|
||||||
(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" "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"])
|
||||||
|
|
||||||
# (mkRenamedOptionModule ["services" "headscale" "settings" "db_type"] ["services" "headscale" "settings" "database" "type"])
|
(mkRemovedOptionModule ["services" "headscale" "openIdConnect" "domainMap"] ''
|
||||||
# (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.
|
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";
|
||||||
}
|
}
|
||||||
@ -519,12 +514,12 @@ in
|
|||||||
# to talk to the server instance.
|
# to talk to the server instance.
|
||||||
etc."headscale/config.yaml".source = cliConfigFile;
|
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";
|
description = "headscale user";
|
||||||
home = dataDir;
|
home = dataDir;
|
||||||
group = cfg.group;
|
group = cfg.group;
|
||||||
@ -533,65 +528,63 @@ in
|
|||||||
|
|
||||||
systemd.services.headscale = {
|
systemd.services.headscale = {
|
||||||
description = "headscale coordination server for Tailscale";
|
description = "headscale coordination server for Tailscale";
|
||||||
wants = [ "network-online.target" ];
|
wants = ["network-online.target"];
|
||||||
after = [ "network-online.target" ];
|
after = ["network-online.target"];
|
||||||
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";
|
||||||
{
|
Type = "simple";
|
||||||
Restart = "always";
|
User = cfg.user;
|
||||||
Type = "simple";
|
Group = cfg.group;
|
||||||
User = cfg.user;
|
|
||||||
Group = cfg.group;
|
|
||||||
|
|
||||||
# Hardening options
|
# Hardening options
|
||||||
RuntimeDirectory = "headscale";
|
RuntimeDirectory = "headscale";
|
||||||
# Allow headscale group access so users can be added and use the CLI.
|
# Allow headscale group access so users can be added and use the CLI.
|
||||||
RuntimeDirectoryMode = "0750";
|
RuntimeDirectoryMode = "0750";
|
||||||
|
|
||||||
StateDirectory = "headscale";
|
StateDirectory = "headscale";
|
||||||
StateDirectoryMode = "0750";
|
StateDirectoryMode = "0750";
|
||||||
|
|
||||||
ProtectSystem = "strict";
|
ProtectSystem = "strict";
|
||||||
ProtectHome = true;
|
ProtectHome = true;
|
||||||
PrivateTmp = true;
|
PrivateTmp = true;
|
||||||
PrivateDevices = true;
|
PrivateDevices = true;
|
||||||
ProtectKernelTunables = true;
|
ProtectKernelTunables = true;
|
||||||
ProtectControlGroups = true;
|
ProtectControlGroups = true;
|
||||||
RestrictSUIDSGID = true;
|
RestrictSUIDSGID = true;
|
||||||
PrivateMounts = true;
|
PrivateMounts = true;
|
||||||
ProtectKernelModules = true;
|
ProtectKernelModules = true;
|
||||||
ProtectKernelLogs = true;
|
ProtectKernelLogs = true;
|
||||||
ProtectHostname = true;
|
ProtectHostname = true;
|
||||||
ProtectClock = true;
|
ProtectClock = true;
|
||||||
ProtectProc = "invisible";
|
ProtectProc = "invisible";
|
||||||
ProcSubset = "pid";
|
ProcSubset = "pid";
|
||||||
RestrictNamespaces = true;
|
RestrictNamespaces = true;
|
||||||
RemoveIPC = true;
|
RemoveIPC = true;
|
||||||
UMask = "0077";
|
UMask = "0077";
|
||||||
|
|
||||||
CapabilityBoundingSet = capabilityBoundingSet;
|
CapabilityBoundingSet = capabilityBoundingSet;
|
||||||
AmbientCapabilities = capabilityBoundingSet;
|
AmbientCapabilities = capabilityBoundingSet;
|
||||||
NoNewPrivileges = true;
|
NoNewPrivileges = true;
|
||||||
LockPersonality = true;
|
LockPersonality = true;
|
||||||
RestrictRealtime = true;
|
RestrictRealtime = true;
|
||||||
SystemCallFilter = [ "@system-service" "~@privileged" "@chown" ];
|
SystemCallFilter = ["@system-service" "~@privileged" "@chown"];
|
||||||
SystemCallArchitectures = "native";
|
SystemCallArchitectures = "native";
|
||||||
RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX";
|
RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
meta.maintainers = with maintainers; [ kradalby misterio77 ];
|
meta.maintainers = with lib.maintainers; [kradalby misterio77];
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user