machines/alasia: headscale server

This commit is contained in:
2026-05-15 18:00:31 +07:00
parent 47c4f3143e
commit 8e085211f7
34 changed files with 2409 additions and 1 deletions
+182
View File
@@ -0,0 +1,182 @@
{ ... }:
{
_class = "clan.service";
manifest.name = "headscale";
manifest.description = "An open source, self-hosted implementation of the Tailscale control server";
manifest.readme = "An open source, self-hosted implementation of the Tailscale control server";
manifest.categories = [ "System" ];
roles.server = {
description = "A server role";
interface =
{ lib, config, ... }:
{
options = {
public_url = lib.mkOption {
type = with lib.types; nullOr str;
default = config.services.headscale.settings.server_url;
description = "Public URL for accessing the instance";
};
base_domain = lib.mkOption {
type = with lib.types; str;
default = "";
description = "Defines the base domain to create the hostnames for MagicDNS in Headscale. `base_domain` must be a FQDN, without the trailing dot. The FQDN of the hosts will be `hostname.base_domain (e.g. myhost.tailnet.example.com)";
};
advertise_routes = lib.mkOption {
type = with lib.types; listOf str;
default = [ ];
description = "Expose physical subnet routes to your entire Tailscale network.";
example = [ "192.168.1.0/24" ];
};
nameservers = lib.mkOption {
type = with lib.types; listOf str;
default = [
"1.1.1.1"
"8.8.8.8"
];
description = "List of nameservers to pass to Tailscale clients";
example = [ "10.0.10.1" ];
};
};
};
perInstance =
{ settings, ... }:
{
nixosModule =
{
config,
pkgs,
lib,
...
}:
let
preAuthKeyFile = "/var/lib/headscale/preauth.key";
routes = lib.concatStringsSep "," settings.advertise_routes;
in
{
systemd.services.headscale-auto-enroll =
let
serverUser = "hcserver";
in
{
description = "Enroll this machine into headscale automatically";
after = [
"headscale.service"
"tailscaled.service"
];
requires = [
"headscale.service"
"tailscaled.service"
];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
User = "root";
};
path = [ pkgs.jq ];
script = ''
set -euo pipefail
if ${pkgs.tailscale}/bin/tailscale status &>/dev/null; then
echo "Already enrolled, skipping."
exit 0
fi
for i in $(seq 1 30); do
${pkgs.headscale}/bin/headscale users list &>/dev/null && break
sleep 1
done
${pkgs.headscale}/bin/headscale users create ${serverUser} 2>/dev/null || true
USER_ID=$(${pkgs.headscale}/bin/headscale users list --name ${serverUser} -o json | jq '.[0].id')
KEY=$(${pkgs.headscale}/bin/headscale preauthkeys create \
--user $USER_ID \
--reusable \
--expiration 30m \
--output json | ${pkgs.jq}/bin/jq -r '.key')
echo "$KEY" > ${preAuthKeyFile}
chmod 600 ${preAuthKeyFile}
${pkgs.tailscale}/bin/tailscale up \
--login-server=https://${settings.public_url} \
--authkey="$KEY" \
--accept-routes \
--advertise-routes=${routes}
'';
};
systemd.services.headscale-approve-routes = {
description = "Auto approve routes";
after = [
"headscale.service"
"tailscaled.service"
"headscale-auto-enroll.service"
];
requires = [
"headscale.service"
"tailscaled.service"
"headscale-auto-enroll.service"
];
path = [ pkgs.jq ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
User = "root";
};
script = ''
set -euo pipefail
NODE_ID=$(${pkgs.tailscale}/bin/tailscale status --json | jq '.Self.ID' | tr -d '"')
${pkgs.headscale}/bin/headscale node approve-routes --identifier $NODE_ID --routes ${routes}
'';
};
systemd.services.tailscaled-autoconnect.after = [
"tailscaled.service"
"headscale-auto-enroll.service"
];
services.tailscale = {
enable = true;
useRoutingFeatures = "server";
openFirewall = true;
};
networking.firewall.allowedTCPPorts = [
config.services.headscale.port
];
services.headscale = {
enable = true;
address = "0.0.0.0";
settings.server_url = "https://${settings.public_url}";
settings.dns = {
base_domain = settings.base_domain;
override_local_dns = true;
nameservers.global = settings.nameservers;
};
};
};
};
};
}
+19
View File
@@ -0,0 +1,19 @@
{ self, inputs, ... }:
let
module = ./default.nix;
in
{
clan.modules = {
headscale = module;
};
# perSystem =
# { ... }:
# {
# clan.nixosTests.service-headplane = {
# imports = [ ./tests/vm/default.nix ];
# _module.args = { inherit self inputs; };
#
# clan.modules."@clan/headplane" = module;
# };
# };
}