mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-17 15:22:59 +01:00
Merge pull request #203938 from kradalby/headscale-rfc0042
headscale: make module conform with rfc42 and upstream (0.17.1) config.
This commit is contained in:
commit
e0a2c590e3
6 changed files with 445 additions and 389 deletions
|
@ -100,7 +100,7 @@
|
||||||
</listitem>
|
</listitem>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
<literal>minio</literal> removed support for it’s legacy
|
<literal>minio</literal> removed support for its legacy
|
||||||
filesystem backend in
|
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>.
|
<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,
|
This means if your storage was created with the old format,
|
||||||
|
@ -113,9 +113,9 @@
|
||||||
<literal>minio_legacy_fs</literal>. Use it via
|
<literal>minio_legacy_fs</literal>. Use it via
|
||||||
<literal>services.minio.package = minio_legacy_fs;</literal>
|
<literal>services.minio.package = minio_legacy_fs;</literal>
|
||||||
to export your data before switching to the new version. See
|
to export your data before switching to the new version. See
|
||||||
the corresponding <link xlink:href="">issue</link>
|
the corresponding
|
||||||
https://github.com/NixOS/nixpkgs/issues/199318) for more
|
<link xlink:href="https://github.com/NixOS/nixpkgs/issues/199318">issue</link>
|
||||||
details.
|
for more details.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
@ -288,6 +288,29 @@
|
||||||
remote <literal>PostgreSQL</literal> database.
|
remote <literal>PostgreSQL</literal> database.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</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>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
A new <literal>virtualisation.rosetta</literal> module was
|
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. -->
|
<!-- 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)
|
- 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.
|
- `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).
|
- 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.
|
- 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;
|
config,
|
||||||
let
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
with lib; let
|
||||||
cfg = config.services.headscale;
|
cfg = config.services.headscale;
|
||||||
|
|
||||||
dataDir = "/var/lib/headscale";
|
dataDir = "/var/lib/headscale";
|
||||||
runDir = "/run/headscale";
|
runDir = "/run/headscale";
|
||||||
|
|
||||||
settingsFormat = pkgs.formats.yaml { };
|
settingsFormat = pkgs.formats.yaml {};
|
||||||
configFile = settingsFormat.generate "headscale.yaml" cfg.settings;
|
configFile = settingsFormat.generate "headscale.yaml" cfg.settings;
|
||||||
in
|
in {
|
||||||
{
|
|
||||||
options = {
|
options = {
|
||||||
services.headscale = {
|
services.headscale = {
|
||||||
enable = mkEnableOption (lib.mdDoc "headscale, Open Source coordination server for Tailscale");
|
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 {
|
address = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = "127.0.0.1";
|
default = "127.0.0.1";
|
||||||
|
@ -78,337 +72,346 @@ in
|
||||||
example = 443;
|
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 {
|
settings = mkOption {
|
||||||
type = settingsFormat.type;
|
|
||||||
default = { };
|
|
||||||
description = lib.mdDoc ''
|
description = lib.mdDoc ''
|
||||||
Overrides to {file}`config.yaml` as a Nix attribute set.
|
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)
|
Check the [example config](https://github.com/juanfont/headscale/blob/main/config-example.yaml)
|
||||||
for possible options.
|
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 {
|
config = mkIf cfg.enable {
|
||||||
|
|
||||||
services.headscale.settings = {
|
services.headscale.settings = {
|
||||||
server_url = mkDefault cfg.serverUrl;
|
|
||||||
listen_addr = mkDefault "${cfg.address}:${toString cfg.port}";
|
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
|
# Turn off update checks since the origin of our package
|
||||||
# is nixpkgs and not Github.
|
# is nixpkgs and not Github.
|
||||||
disable_check_updates = true;
|
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";
|
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";
|
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
|
# Setup the headscale configuration in a known path in /etc to
|
||||||
|
@ -416,7 +419,7 @@ in
|
||||||
# for communication.
|
# for communication.
|
||||||
environment.etc."headscale/config.yaml".source = configFile;
|
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") {
|
users.users.headscale = mkIf (cfg.user == "headscale") {
|
||||||
description = "headscale user";
|
description = "headscale user";
|
||||||
|
@ -427,70 +430,68 @@ in
|
||||||
|
|
||||||
systemd.services.headscale = {
|
systemd.services.headscale = {
|
||||||
description = "headscale coordination server for Tailscale";
|
description = "headscale coordination server for Tailscale";
|
||||||
after = [ "network-online.target" ];
|
after = ["network-online.target"];
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = ["multi-user.target"];
|
||||||
restartTriggers = [ configFile ];
|
restartTriggers = [configFile];
|
||||||
|
|
||||||
environment.GIN_MODE = "release";
|
environment.GIN_MODE = "release";
|
||||||
|
|
||||||
script = ''
|
script = ''
|
||||||
${optionalString (cfg.database.passwordFile != null) ''
|
${optionalString (cfg.settings.db_password_file != null) ''
|
||||||
export HEADSCALE_DB_PASS="$(head -n1 ${escapeShellArg cfg.database.passwordFile})"
|
export HEADSCALE_DB_PASS="$(head -n1 ${escapeShellArg cfg.settings.db_password_file})"
|
||||||
''}
|
''}
|
||||||
|
|
||||||
${optionalString (cfg.openIdConnect.clientSecretFile != null) ''
|
${optionalString (cfg.settings.oidc.client_secret_file != null) ''
|
||||||
export HEADSCALE_OIDC_CLIENT_SECRET="$(head -n1 ${escapeShellArg cfg.openIdConnect.clientSecretFile})"
|
export HEADSCALE_OIDC_CLIENT_SECRET="$(head -n1 ${escapeShellArg cfg.settings.oidc.client_secret_file})"
|
||||||
''}
|
''}
|
||||||
exec ${cfg.package}/bin/headscale serve
|
exec ${cfg.package}/bin/headscale serve
|
||||||
'';
|
'';
|
||||||
|
|
||||||
serviceConfig =
|
serviceConfig = let
|
||||||
let
|
capabilityBoundingSet = ["CAP_CHOWN"] ++ 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 ];
|
meta.maintainers = with maintainers; [kradalby misterio77];
|
||||||
}
|
}
|
||||||
|
|
|
@ -257,6 +257,7 @@ in {
|
||||||
haste-server = handleTest ./haste-server.nix {};
|
haste-server = handleTest ./haste-server.nix {};
|
||||||
haproxy = handleTest ./haproxy.nix {};
|
haproxy = handleTest ./haproxy.nix {};
|
||||||
hardened = handleTest ./hardened.nix {};
|
hardened = handleTest ./hardened.nix {};
|
||||||
|
headscale = handleTest ./headscale.nix {};
|
||||||
healthchecks = handleTest ./web-apps/healthchecks.nix {};
|
healthchecks = handleTest ./web-apps/healthchecks.nix {};
|
||||||
hbase2 = handleTest ./hbase.nix { package=pkgs.hbase2; };
|
hbase2 = handleTest ./hbase.nix { package=pkgs.hbase2; };
|
||||||
hbase_2_4 = handleTest ./hbase.nix { package=pkgs.hbase_2_4; };
|
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 {
|
buildGoModule rec {
|
||||||
pname = "headscale";
|
pname = "headscale";
|
||||||
version = "0.16.4";
|
version = "0.17.1";
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
src = fetchFromGitHub {
|
||||||
owner = "juanfont";
|
owner = "juanfont";
|
||||||
repo = "headscale";
|
repo = "headscale";
|
||||||
rev = "v${version}";
|
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 = ''
|
postInstall = ''
|
||||||
installShellCompletion --cmd headscale \
|
installShellCompletion --cmd headscale \
|
||||||
|
@ -44,6 +51,6 @@ buildGoModule rec {
|
||||||
Headscale implements this coordination server.
|
Headscale implements this coordination server.
|
||||||
'';
|
'';
|
||||||
license = licenses.bsd3;
|
license = licenses.bsd3;
|
||||||
maintainers = with maintainers; [ nkje jk kradalby ];
|
maintainers = with maintainers; [nkje jk kradalby misterio77 ghuntley];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue