machines/alasia: headscale server
This commit is contained in:
@@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
# };
|
||||
# };
|
||||
}
|
||||
Reference in New Issue
Block a user