Clicks Headscale

Our module provides much more basic options than the NixOS headscale module, but we believe this saves you some of the hassle of configuring it.

clicks.services.headscale = {
  enable = true;
  domain = "clicks.domains";
};

The domain is both the address you'll use to access your headscale server, as well as the base address that devices are suffixed with (e.g. a device called albatross owned by minion would become albatross.minion.<domain>). It's also added as a search domain, so you could access "albatross" with "albatross.minion".


clicks.services.headscale = {
  addr = "127.0.0.1";
  port = 1024;
};

Our headscale module integrates with our nginx module, and we will enable it for you unless you explicitly disable it. Therefore, we recommend that you don't change the addr to 0.0.0.0: there is no need, headscale will already be available if you route traffic from the domain you have set to your server.

If you do change the addr or port, we will reverse proxy to the new addr and port. If you have more services, this may help you to avoid port conflicts (e.g. by hosting on 127.0.0.255:1024 or another port)


clicks.services.headscale = {
  oidc = {
    enable = true;
    issuer = "https://login.clicks.codes/realms/master";
    allowed_groups = [ "/clicks" ];
    client_id = "headscale";
    client_secret_path = config.clicks.secrets."${lib.clicks.secrets.name ./headscale.sops.a1d1.json}".paths.oidc_client_secret;
  };
};

If you have an OIDC server, we strongly recommend using it. It allows you to automatically create users and provision devices rather than needing to use the command line to do so.

If you're using keycloak and want to use allowed_groups, you'll need to create a group membership mapper with the name groups. If you set "full group path" to enabled in Keycloak, you'll also need to prefix the group with a leading /. (We recommend you do this, as not doing it will mean that other groups could be unintentionally matched, for example admins would match /clicks/admins and /transplace/admins)

The secret specified in your client_secret_path must be readable by the headscale group.


clicks.services.headscale = {
  database_password_path = config.clicks.secrets."${lib.clicks.secrets.name ./headscale.sops.a1d1.json}".paths.database_password;
  noise_private_key_path = config.clicks.secrets."${lib.clicks.secrets.name ./headscale.sops.a1d1.json}".paths.noise_private_key;
  private_key_path = config.clicks.secrets."${lib.clicks.secrets.name ./headscale.sops.a1d1.json}".paths.private_key;
}

Finally, we have a few more secrets: the database_password_path, the noise_private_key_path, and the private_key_path.

The "noise private key" and "private key" are generated by tailscale at launch if you don't specify them.

The database password need not match an existing database; the headscale module automatically provisions the database using the Clicks Postgres module.

All of these secrets must be readable by the headscale group.