mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-25 08:23:09 +00:00
headscale: Update to 0.17.1, conform module to RFC0042
This commit upgrades headscale to the newest version, 0.17.0 and updates the module with the current breaking config changes. In addition, the module is rewritten to conform with RFC0042 to try to prevent some drift between the module and the upstream. A new maintainer, Misterio77, is added as maintainer. Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> Co-authored-by: Gabriel Fontes <hi@m7.rs> Co-authored-by: Geoffrey Huntley <ghuntley@ghuntley.com>
This commit is contained in:
parent
ef77bcb369
commit
571780384a
@ -100,7 +100,7 @@
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>minio</literal> removed support for it’s legacy
|
||||
<literal>minio</literal> removed support for its legacy
|
||||
filesystem backend in
|
||||
<link xlink:href="https://github.com/minio/minio/releases/tag/RELEASE.2022-10-29T06-21-33Z">RELEASE.2022-10-29T06-21-33Z</link>.
|
||||
This means if your storage was created with the old format,
|
||||
@ -113,9 +113,9 @@
|
||||
<literal>minio_legacy_fs</literal>. Use it via
|
||||
<literal>services.minio.package = minio_legacy_fs;</literal>
|
||||
to export your data before switching to the new version. See
|
||||
the corresponding <link xlink:href="">issue</link>
|
||||
https://github.com/NixOS/nixpkgs/issues/199318) for more
|
||||
details.
|
||||
the corresponding
|
||||
<link xlink:href="https://github.com/NixOS/nixpkgs/issues/199318">issue</link>
|
||||
for more details.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
@ -288,6 +288,29 @@
|
||||
remote <literal>PostgreSQL</literal> database.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
The module <literal>services.headscale</literal> was
|
||||
refactored to be compliant with
|
||||
<link xlink:href="https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md">RFC
|
||||
0042</link>. To be precise, this means that the following
|
||||
things have changed:
|
||||
</para>
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>
|
||||
Most settings has been migrated under
|
||||
<link linkend="opt-services.headscale.settings">services.headscale.settings</link>
|
||||
which is an attribute-set that will be converted into
|
||||
headscale’s YAML config format. This means that the
|
||||
configuration from
|
||||
<link xlink:href="https://github.com/juanfont/headscale/blob/main/config-example.yaml">headscale’s
|
||||
example configuration</link> can be directly written as
|
||||
attribute-set in Nix within this option.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
A new <literal>virtualisation.rosetta</literal> module was
|
||||
|
@ -61,7 +61,7 @@ In addition to numerous new and upgraded packages, this release has the followin
|
||||
|
||||
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
||||
|
||||
- `vim_configurable` has been renamed to `vim-full` to avoid confusion: `vim-full`'s build-time features are configurable, but both `vim` and `vim-full` are *customizable* (in the sense of user configuration, like vimrc).
|
||||
- `vim_configurable` has been renamed to `vim-full` to avoid confusion: `vim-full`'s build-time features are configurable, but both `vim` and `vim-full` are _customizable_ (in the sense of user configuration, like vimrc).
|
||||
|
||||
- The module for the application firewall `opensnitch` got the ability to configure rules. Available as [services.opensnitch.rules](#opt-services.opensnitch.rules)
|
||||
|
||||
@ -80,6 +80,13 @@ In addition to numerous new and upgraded packages, this release has the followin
|
||||
|
||||
- `mastodon` now supports connection to a remote `PostgreSQL` database.
|
||||
|
||||
- The module `services.headscale` was refactored to be compliant with [RFC 0042](https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md). To be precise, this means that the following things have changed:
|
||||
|
||||
- Most settings has been migrated under [services.headscale.settings](#opt-services.headscale.settings) which is an attribute-set that
|
||||
will be converted into headscale's YAML config format. This means that the configuration from
|
||||
[headscale's example configuration](https://github.com/juanfont/headscale/blob/main/config-example.yaml)
|
||||
can be directly written as attribute-set in Nix within this option.
|
||||
|
||||
- A new `virtualisation.rosetta` module was added to allow running `x86_64` binaries through [Rosetta](https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment) inside virtualised NixOS guests on Apple silicon. This feature works by default with the [UTM](https://docs.getutm.app/) virtualisation [package](https://search.nixos.org/packages?channel=unstable&show=utm&from=0&size=1&sort=relevance&type=packages&query=utm).
|
||||
|
||||
- The new option `users.motdFile` allows configuring a Message Of The Day that can be updated dynamically.
|
||||
|
@ -1,15 +1,18 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
with lib;
|
||||
let
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
with lib; let
|
||||
cfg = config.services.headscale;
|
||||
|
||||
dataDir = "/var/lib/headscale";
|
||||
runDir = "/run/headscale";
|
||||
|
||||
settingsFormat = pkgs.formats.yaml { };
|
||||
settingsFormat = pkgs.formats.yaml {};
|
||||
configFile = settingsFormat.generate "headscale.yaml" cfg.settings;
|
||||
in
|
||||
{
|
||||
in {
|
||||
options = {
|
||||
services.headscale = {
|
||||
enable = mkEnableOption (lib.mdDoc "headscale, Open Source coordination server for Tailscale");
|
||||
@ -51,15 +54,6 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
serverUrl = mkOption {
|
||||
type = types.str;
|
||||
default = "http://127.0.0.1:8080";
|
||||
description = lib.mdDoc ''
|
||||
The url clients will connect to.
|
||||
'';
|
||||
example = "https://myheadscale.example.com:443";
|
||||
};
|
||||
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
default = "127.0.0.1";
|
||||
@ -78,337 +72,346 @@ in
|
||||
example = 443;
|
||||
};
|
||||
|
||||
privateKeyFile = mkOption {
|
||||
type = types.path;
|
||||
default = "${dataDir}/private.key";
|
||||
description = lib.mdDoc ''
|
||||
Path to private key file, generated automatically if it does not exist.
|
||||
'';
|
||||
};
|
||||
|
||||
derp = {
|
||||
urls = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ "https://controlplane.tailscale.com/derpmap/default" ];
|
||||
description = lib.mdDoc ''
|
||||
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 = [ ];
|
||||
description = lib.mdDoc ''
|
||||
List of file paths containing DERP maps.
|
||||
See [How Tailscale works](https://tailscale.com/blog/how-tailscale-works/) for more information on DERP maps.
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
autoUpdate = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = lib.mdDoc ''
|
||||
Whether to automatically update DERP maps on a set frequency.
|
||||
'';
|
||||
example = false;
|
||||
};
|
||||
|
||||
updateFrequency = mkOption {
|
||||
type = types.str;
|
||||
default = "24h";
|
||||
description = lib.mdDoc ''
|
||||
Frequency to update DERP maps.
|
||||
'';
|
||||
example = "5m";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
ephemeralNodeInactivityTimeout = mkOption {
|
||||
type = types.str;
|
||||
default = "30m";
|
||||
description = lib.mdDoc ''
|
||||
Time before an inactive ephemeral node is deleted.
|
||||
'';
|
||||
example = "5m";
|
||||
};
|
||||
|
||||
database = {
|
||||
type = mkOption {
|
||||
type = types.enum [ "sqlite3" "postgres" ];
|
||||
example = "postgres";
|
||||
default = "sqlite3";
|
||||
description = lib.mdDoc "Database engine to use.";
|
||||
};
|
||||
|
||||
host = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "127.0.0.1";
|
||||
description = lib.mdDoc "Database host address.";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.nullOr types.port;
|
||||
default = null;
|
||||
example = 3306;
|
||||
description = lib.mdDoc "Database host port.";
|
||||
};
|
||||
|
||||
name = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "headscale";
|
||||
description = lib.mdDoc "Database name.";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "headscale";
|
||||
description = lib.mdDoc "Database user.";
|
||||
};
|
||||
|
||||
passwordFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
example = "/run/keys/headscale-dbpassword";
|
||||
description = lib.mdDoc ''
|
||||
A file containing the password corresponding to
|
||||
{option}`database.user`.
|
||||
'';
|
||||
};
|
||||
|
||||
path = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = "${dataDir}/db.sqlite";
|
||||
description = lib.mdDoc "Path to the sqlite3 database file.";
|
||||
};
|
||||
};
|
||||
|
||||
logLevel = mkOption {
|
||||
type = types.str;
|
||||
default = "info";
|
||||
description = lib.mdDoc ''
|
||||
headscale log level.
|
||||
'';
|
||||
example = "debug";
|
||||
};
|
||||
|
||||
dns = {
|
||||
nameservers = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ "1.1.1.1" ];
|
||||
description = lib.mdDoc ''
|
||||
List of nameservers to pass to Tailscale clients.
|
||||
'';
|
||||
};
|
||||
|
||||
domains = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
description = lib.mdDoc ''
|
||||
Search domains to inject to Tailscale clients.
|
||||
'';
|
||||
example = [ "mydomain.internal" ];
|
||||
};
|
||||
|
||||
magicDns = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = lib.mdDoc ''
|
||||
Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/).
|
||||
Only works if there is at least a nameserver defined.
|
||||
'';
|
||||
example = false;
|
||||
};
|
||||
|
||||
baseDomain = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = lib.mdDoc ''
|
||||
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`).
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
openIdConnect = {
|
||||
issuer = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = lib.mdDoc ''
|
||||
URL to OpenID issuer.
|
||||
'';
|
||||
example = "https://openid.example.com";
|
||||
};
|
||||
|
||||
clientId = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = lib.mdDoc ''
|
||||
OpenID Connect client ID.
|
||||
'';
|
||||
};
|
||||
|
||||
clientSecretFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = lib.mdDoc ''
|
||||
Path to OpenID Connect client secret file.
|
||||
'';
|
||||
};
|
||||
|
||||
domainMap = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
default = { };
|
||||
description = lib.mdDoc ''
|
||||
Domain map is used to map incoming users (by their email) to
|
||||
a namespace. The key can be a string, or regex.
|
||||
'';
|
||||
example = {
|
||||
".*" = "default-namespace";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
tls = {
|
||||
letsencrypt = {
|
||||
hostname = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = "";
|
||||
description = lib.mdDoc ''
|
||||
Domain name to request a TLS certificate for.
|
||||
'';
|
||||
};
|
||||
challengeType = mkOption {
|
||||
type = types.enum [ "TLS-ALPN-01" "HTTP-01" ];
|
||||
default = "HTTP-01";
|
||||
description = lib.mdDoc ''
|
||||
Type of ACME challenge to use, currently supported types:
|
||||
`HTTP-01` or `TLS-ALPN-01`.
|
||||
'';
|
||||
};
|
||||
httpListen = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = ":http";
|
||||
description = lib.mdDoc ''
|
||||
When HTTP-01 challenge is chosen, letsencrypt must set up a
|
||||
verification endpoint, and it will be listening on:
|
||||
`:http = port 80`.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
certFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = lib.mdDoc ''
|
||||
Path to already created certificate.
|
||||
'';
|
||||
};
|
||||
keyFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = lib.mdDoc ''
|
||||
Path to key for already created certificate.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
aclPolicyFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = lib.mdDoc ''
|
||||
Path to a file containing ACL policies.
|
||||
'';
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
type = settingsFormat.type;
|
||||
default = { };
|
||||
description = lib.mdDoc ''
|
||||
Overrides to {file}`config.yaml` as a Nix attribute set.
|
||||
This option is ideal for overriding settings not exposed as Nix options.
|
||||
Check the [example config](https://github.com/juanfont/headscale/blob/main/config-example.yaml)
|
||||
for possible options.
|
||||
'';
|
||||
type = types.submodule {
|
||||
freeformType = settingsFormat.type;
|
||||
|
||||
options = {
|
||||
server_url = mkOption {
|
||||
type = types.str;
|
||||
default = "http://127.0.0.1:8080";
|
||||
description = lib.mdDoc ''
|
||||
The url clients will connect to.
|
||||
'';
|
||||
example = "https://myheadscale.example.com:443";
|
||||
};
|
||||
|
||||
private_key_path = mkOption {
|
||||
type = types.path;
|
||||
default = "${dataDir}/private.key";
|
||||
description = lib.mdDoc ''
|
||||
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";
|
||||
description = lib.mdDoc ''
|
||||
Path to noise private key file, generated automatically if it does not exist.
|
||||
'';
|
||||
};
|
||||
|
||||
derp = {
|
||||
urls = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = ["https://controlplane.tailscale.com/derpmap/default"];
|
||||
description = lib.mdDoc ''
|
||||
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 = [];
|
||||
description = lib.mdDoc ''
|
||||
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;
|
||||
default = true;
|
||||
description = lib.mdDoc ''
|
||||
Whether to automatically update DERP maps on a set frequency.
|
||||
'';
|
||||
example = false;
|
||||
};
|
||||
|
||||
update_frequency = mkOption {
|
||||
type = types.str;
|
||||
default = "24h";
|
||||
description = lib.mdDoc ''
|
||||
Frequency to update DERP maps.
|
||||
'';
|
||||
example = "5m";
|
||||
};
|
||||
};
|
||||
|
||||
ephemeral_node_inactivity_timeout = mkOption {
|
||||
type = types.str;
|
||||
default = "30m";
|
||||
description = lib.mdDoc ''
|
||||
Time before an inactive ephemeral node is deleted.
|
||||
'';
|
||||
example = "5m";
|
||||
};
|
||||
|
||||
db_type = mkOption {
|
||||
type = types.enum ["sqlite3" "postgres"];
|
||||
example = "postgres";
|
||||
default = "sqlite3";
|
||||
description = lib.mdDoc "Database engine to use.";
|
||||
};
|
||||
|
||||
db_host = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "127.0.0.1";
|
||||
description = lib.mdDoc "Database host address.";
|
||||
};
|
||||
|
||||
db_port = mkOption {
|
||||
type = types.nullOr types.port;
|
||||
default = null;
|
||||
example = 3306;
|
||||
description = lib.mdDoc "Database host port.";
|
||||
};
|
||||
|
||||
db_name = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "headscale";
|
||||
description = lib.mdDoc "Database name.";
|
||||
};
|
||||
|
||||
db_user = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "headscale";
|
||||
description = lib.mdDoc "Database user.";
|
||||
};
|
||||
|
||||
db_password_file = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
example = "/run/keys/headscale-dbpassword";
|
||||
description = lib.mdDoc ''
|
||||
A file containing the password corresponding to
|
||||
{option}`database.user`.
|
||||
'';
|
||||
};
|
||||
|
||||
db_path = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = "${dataDir}/db.sqlite";
|
||||
description = lib.mdDoc "Path to the sqlite3 database file.";
|
||||
};
|
||||
|
||||
log.level = mkOption {
|
||||
type = types.str;
|
||||
default = "info";
|
||||
description = lib.mdDoc ''
|
||||
headscale log level.
|
||||
'';
|
||||
example = "debug";
|
||||
};
|
||||
|
||||
log.format = mkOption {
|
||||
type = types.str;
|
||||
default = "text";
|
||||
description = lib.mdDoc ''
|
||||
headscale log format.
|
||||
'';
|
||||
example = "json";
|
||||
};
|
||||
|
||||
dns_config = {
|
||||
nameservers = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = ["1.1.1.1"];
|
||||
description = lib.mdDoc ''
|
||||
List of nameservers to pass to Tailscale clients.
|
||||
'';
|
||||
};
|
||||
|
||||
override_local_dns = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = lib.mdDoc ''
|
||||
Whether to use [Override local DNS](https://tailscale.com/kb/1054/dns/).
|
||||
'';
|
||||
example = true;
|
||||
};
|
||||
|
||||
domains = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = lib.mdDoc ''
|
||||
Search domains to inject to Tailscale clients.
|
||||
'';
|
||||
example = ["mydomain.internal"];
|
||||
};
|
||||
|
||||
magic_dns = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = lib.mdDoc ''
|
||||
Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/).
|
||||
Only works if there is at least a nameserver defined.
|
||||
'';
|
||||
example = false;
|
||||
};
|
||||
|
||||
base_domain = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = lib.mdDoc ''
|
||||
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`).
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
oidc = {
|
||||
issuer = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = lib.mdDoc ''
|
||||
URL to OpenID issuer.
|
||||
'';
|
||||
example = "https://openid.example.com";
|
||||
};
|
||||
|
||||
client_id = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = lib.mdDoc ''
|
||||
OpenID Connect client ID.
|
||||
'';
|
||||
};
|
||||
|
||||
client_secret_file = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = lib.mdDoc ''
|
||||
Path to OpenID Connect client secret file.
|
||||
'';
|
||||
};
|
||||
|
||||
domain_map = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
default = {};
|
||||
description = lib.mdDoc ''
|
||||
Domain map is used to map incomming users (by their email) to
|
||||
a namespace. The key can be a string, or regex.
|
||||
'';
|
||||
example = {
|
||||
".*" = "default-namespace";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
tls_letsencrypt_hostname = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = "";
|
||||
description = lib.mdDoc ''
|
||||
Domain name to request a TLS certificate for.
|
||||
'';
|
||||
};
|
||||
|
||||
tls_letsencrypt_challenge_type = mkOption {
|
||||
type = types.enum ["TLS-ALPN-01" "HTTP-01"];
|
||||
default = "HTTP-01";
|
||||
description = lib.mdDoc ''
|
||||
Type of ACME challenge to use, currently supported types:
|
||||
`HTTP-01` or `TLS-ALPN-01`.
|
||||
'';
|
||||
};
|
||||
|
||||
tls_letsencrypt_listen = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = ":http";
|
||||
description = lib.mdDoc ''
|
||||
When HTTP-01 challenge is chosen, letsencrypt must set up a
|
||||
verification endpoint, and it will be listening on:
|
||||
`:http = port 80`.
|
||||
'';
|
||||
};
|
||||
|
||||
tls_cert_path = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = lib.mdDoc ''
|
||||
Path to already created certificate.
|
||||
'';
|
||||
};
|
||||
|
||||
tls_key_path = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = lib.mdDoc ''
|
||||
Path to key for already created certificate.
|
||||
'';
|
||||
};
|
||||
|
||||
acl_policy_path = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = lib.mdDoc ''
|
||||
Path to a file containg ACL policies.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
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_file"])
|
||||
(mkRenamedOptionModule ["services" "headscale" "openIdConnect" "domainMap"] ["services" "headscale" "settings" "oidc" "domain_map"])
|
||||
(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"])
|
||||
];
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
services.headscale.settings = {
|
||||
server_url = mkDefault cfg.serverUrl;
|
||||
listen_addr = mkDefault "${cfg.address}:${toString cfg.port}";
|
||||
|
||||
private_key_path = mkDefault cfg.privateKeyFile;
|
||||
|
||||
derp = {
|
||||
urls = mkDefault cfg.derp.urls;
|
||||
paths = mkDefault cfg.derp.paths;
|
||||
auto_update_enable = mkDefault cfg.derp.autoUpdate;
|
||||
update_frequency = mkDefault cfg.derp.updateFrequency;
|
||||
};
|
||||
|
||||
# Turn off update checks since the origin of our package
|
||||
# is nixpkgs and not Github.
|
||||
disable_check_updates = true;
|
||||
|
||||
ephemeral_node_inactivity_timeout = mkDefault cfg.ephemeralNodeInactivityTimeout;
|
||||
|
||||
db_type = mkDefault cfg.database.type;
|
||||
db_path = mkDefault cfg.database.path;
|
||||
|
||||
log_level = mkDefault cfg.logLevel;
|
||||
|
||||
dns_config = {
|
||||
nameservers = mkDefault cfg.dns.nameservers;
|
||||
domains = mkDefault cfg.dns.domains;
|
||||
magic_dns = mkDefault cfg.dns.magicDns;
|
||||
base_domain = mkDefault cfg.dns.baseDomain;
|
||||
};
|
||||
|
||||
unix_socket = "${runDir}/headscale.sock";
|
||||
|
||||
# OpenID Connect
|
||||
oidc = {
|
||||
issuer = mkDefault cfg.openIdConnect.issuer;
|
||||
client_id = mkDefault cfg.openIdConnect.clientId;
|
||||
domain_map = mkDefault cfg.openIdConnect.domainMap;
|
||||
};
|
||||
|
||||
tls_letsencrypt_cache_dir = "${dataDir}/.cache";
|
||||
|
||||
} // optionalAttrs (cfg.database.host != null) {
|
||||
db_host = mkDefault cfg.database.host;
|
||||
} // optionalAttrs (cfg.database.port != null) {
|
||||
db_port = mkDefault cfg.database.port;
|
||||
} // optionalAttrs (cfg.database.name != null) {
|
||||
db_name = mkDefault cfg.database.name;
|
||||
} // optionalAttrs (cfg.database.user != null) {
|
||||
db_user = mkDefault cfg.database.user;
|
||||
} // optionalAttrs (cfg.tls.letsencrypt.hostname != null) {
|
||||
tls_letsencrypt_hostname = mkDefault cfg.tls.letsencrypt.hostname;
|
||||
} // optionalAttrs (cfg.tls.letsencrypt.challengeType != null) {
|
||||
tls_letsencrypt_challenge_type = mkDefault cfg.tls.letsencrypt.challengeType;
|
||||
} // optionalAttrs (cfg.tls.letsencrypt.httpListen != null) {
|
||||
tls_letsencrypt_listen = mkDefault cfg.tls.letsencrypt.httpListen;
|
||||
} // optionalAttrs (cfg.tls.certFile != null) {
|
||||
tls_cert_path = mkDefault cfg.tls.certFile;
|
||||
} // optionalAttrs (cfg.tls.keyFile != null) {
|
||||
tls_key_path = mkDefault cfg.tls.keyFile;
|
||||
} // optionalAttrs (cfg.aclPolicyFile != null) {
|
||||
acl_policy_path = mkDefault cfg.aclPolicyFile;
|
||||
};
|
||||
|
||||
# Setup the headscale configuration in a known path in /etc to
|
||||
@ -416,7 +419,7 @@ in
|
||||
# for communication.
|
||||
environment.etc."headscale/config.yaml".source = configFile;
|
||||
|
||||
users.groups.headscale = mkIf (cfg.group == "headscale") { };
|
||||
users.groups.headscale = mkIf (cfg.group == "headscale") {};
|
||||
|
||||
users.users.headscale = mkIf (cfg.user == "headscale") {
|
||||
description = "headscale user";
|
||||
@ -427,70 +430,68 @@ in
|
||||
|
||||
systemd.services.headscale = {
|
||||
description = "headscale coordination server for Tailscale";
|
||||
after = [ "network-online.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
restartTriggers = [ configFile ];
|
||||
after = ["network-online.target"];
|
||||
wantedBy = ["multi-user.target"];
|
||||
restartTriggers = [configFile];
|
||||
|
||||
environment.GIN_MODE = "release";
|
||||
|
||||
script = ''
|
||||
${optionalString (cfg.database.passwordFile != null) ''
|
||||
export HEADSCALE_DB_PASS="$(head -n1 ${escapeShellArg cfg.database.passwordFile})"
|
||||
${optionalString (cfg.settings.db_password_file != null) ''
|
||||
export HEADSCALE_DB_PASS="$(head -n1 ${escapeShellArg cfg.settings.db_password_file})"
|
||||
''}
|
||||
|
||||
${optionalString (cfg.openIdConnect.clientSecretFile != null) ''
|
||||
export HEADSCALE_OIDC_CLIENT_SECRET="$(head -n1 ${escapeShellArg cfg.openIdConnect.clientSecretFile})"
|
||||
${optionalString (cfg.settings.oidc.client_secret_file != null) ''
|
||||
export HEADSCALE_OIDC_CLIENT_SECRET="$(head -n1 ${escapeShellArg cfg.settings.oidc.client_secret_file})"
|
||||
''}
|
||||
exec ${cfg.package}/bin/headscale serve
|
||||
'';
|
||||
|
||||
serviceConfig =
|
||||
let
|
||||
capabilityBoundingSet = [ "CAP_CHOWN" ] ++ optional (cfg.port < 1024) "CAP_NET_BIND_SERVICE";
|
||||
in
|
||||
{
|
||||
Restart = "always";
|
||||
Type = "simple";
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
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";
|
||||
# Hardening options
|
||||
RuntimeDirectory = "headscale";
|
||||
# Allow headscale group access so users can be added and use the CLI.
|
||||
RuntimeDirectoryMode = "0750";
|
||||
|
||||
StateDirectory = "headscale";
|
||||
StateDirectoryMode = "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";
|
||||
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";
|
||||
};
|
||||
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 ];
|
||||
meta.maintainers = with maintainers; [kradalby misterio77];
|
||||
}
|
||||
|
@ -257,6 +257,7 @@ in {
|
||||
haste-server = handleTest ./haste-server.nix {};
|
||||
haproxy = handleTest ./haproxy.nix {};
|
||||
hardened = handleTest ./hardened.nix {};
|
||||
headscale = handleTest ./headscale.nix {};
|
||||
healthchecks = handleTest ./web-apps/healthchecks.nix {};
|
||||
hbase2 = handleTest ./hbase.nix { package=pkgs.hbase2; };
|
||||
hbase_2_4 = handleTest ./hbase.nix { package=pkgs.hbase_2_4; };
|
||||
|
17
nixos/tests/headscale.nix
Normal file
17
nixos/tests/headscale.nix
Normal file
@ -0,0 +1,17 @@
|
||||
import ./make-test-python.nix ({ pkgs, lib, ... }: {
|
||||
name = "headscale";
|
||||
meta.maintainers = with lib.maintainers; [ misterio77 ];
|
||||
|
||||
nodes.machine = { ... }: {
|
||||
services.headscale.enable = true;
|
||||
environment.systemPackages = [ pkgs.headscale ];
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
machine.wait_for_unit("headscale")
|
||||
machine.wait_for_open_port(8080)
|
||||
# Test basic funcionality
|
||||
machine.succeed("headscale namespaces create test")
|
||||
machine.succeed("headscale preauthkeys -n test create")
|
||||
'';
|
||||
})
|
@ -1,21 +1,28 @@
|
||||
{ lib, buildGoModule, fetchFromGitHub, installShellFiles }:
|
||||
|
||||
{
|
||||
lib,
|
||||
buildGoModule,
|
||||
fetchFromGitHub,
|
||||
installShellFiles,
|
||||
}:
|
||||
buildGoModule rec {
|
||||
pname = "headscale";
|
||||
version = "0.16.4";
|
||||
version = "0.17.1";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "juanfont";
|
||||
repo = "headscale";
|
||||
rev = "v${version}";
|
||||
sha256 = "sha256-j5fbWxRMkYlsgL1QDEDlitKB3FOmDTy17FcuztALISw=";
|
||||
sha256 = "sha256-/NJUtmH67VZERCvExcX4W4T9Rcixc5m28ujNcrQduWg=";
|
||||
};
|
||||
|
||||
vendorSha256 = "sha256-RzmnAh81BN4tbzAGzJbb6CMuws8kuPJDw7aPkRRnSS8=";
|
||||
vendorSha256 = "sha256-Y1IK9Tx2sv0v27ZYtSxDP9keHQ7skctDOa+37pNGEC8=";
|
||||
|
||||
ldflags = [ "-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}" ];
|
||||
ldflags = ["-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}"];
|
||||
|
||||
nativeBuildInputs = [ installShellFiles ];
|
||||
nativeBuildInputs = [installShellFiles];
|
||||
checkFlags = ["-short"];
|
||||
|
||||
tags = ["ts2019"];
|
||||
|
||||
postInstall = ''
|
||||
installShellCompletion --cmd headscale \
|
||||
@ -44,6 +51,6 @@ buildGoModule rec {
|
||||
Headscale implements this coordination server.
|
||||
'';
|
||||
license = licenses.bsd3;
|
||||
maintainers = with maintainers; [ nkje jk kradalby ];
|
||||
maintainers = with maintainers; [nkje jk kradalby misterio77 ghuntley];
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user