185 lines
4.3 KiB
Nix
185 lines
4.3 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
options,
|
|
...
|
|
}:
|
|
let
|
|
inherit (lib) mkIf;
|
|
metricsEnable = false;
|
|
mkAcl = src: dst: {
|
|
action = "accept";
|
|
inherit src dst;
|
|
};
|
|
mkSshAcl = src: dst: users: {
|
|
action = "accept";
|
|
inherit src dst users;
|
|
};
|
|
in
|
|
mkIf config.services.headscale.enable {
|
|
environment.systemPackages = [ config.services.headscale.package ];
|
|
|
|
networking.firewall.allowedTCPPorts = [
|
|
3478 # DERP
|
|
];
|
|
|
|
services = {
|
|
headscale = {
|
|
address = "127.0.0.1";
|
|
port = 8521;
|
|
|
|
settings = {
|
|
server_url = "https://hs.ccnlc.eu";
|
|
tls_cert_path = null;
|
|
tls_key_path = null;
|
|
ip_prefixes = [
|
|
"100.64.0.0/10"
|
|
"fd7a:115c:a1e0::/48"
|
|
];
|
|
ephemeral_node_inactivity_timeout = "30m";
|
|
node_update_check_interval = "10s";
|
|
metrics_listen_addr = mkIf metricsEnable "127.0.0.1:8086";
|
|
# logging
|
|
log = {
|
|
format = "text";
|
|
level = "info";
|
|
};
|
|
|
|
logtail.enabled = false;
|
|
|
|
dns_config = {
|
|
override_local_dns = true;
|
|
magic_dns = true;
|
|
nameservers = [
|
|
"100.64.0.4"
|
|
];
|
|
extra_records =
|
|
let
|
|
mkRecords = map (sub: {
|
|
name = "${sub}.ccnlc.eu";
|
|
type = "A";
|
|
value = "100.64.0.4";
|
|
});
|
|
in
|
|
[
|
|
{
|
|
name = "ccnlc.eu";
|
|
type = "A";
|
|
value = "100.64.0.4";
|
|
}
|
|
]
|
|
# Tailscale doesn't seem to support wildcard A/AAAA records
|
|
# - https://github.com/juanfont/headscale/issues/2159#issuecomment-2393406444
|
|
++ mkRecords [
|
|
"immich"
|
|
"adguard"
|
|
"nextcloud"
|
|
"kitchenowl"
|
|
"navidrome"
|
|
"subsonic"
|
|
"nextcloud"
|
|
"paperless"
|
|
"truenas"
|
|
"fritz"
|
|
];
|
|
};
|
|
|
|
derp = {
|
|
# Reference: https://github.com/juanfont/headscale/issues/1326#issuecomment-1505487881
|
|
server = {
|
|
enabled = true;
|
|
stun_listen_addr = "0.0.0.0:3478";
|
|
|
|
# Region code and name are displayed in the Tailscale UI to identify a DERP region
|
|
region_code = "headscale";
|
|
region_name = "Headscale Embedded DERP";
|
|
region_id = 999;
|
|
};
|
|
|
|
urls = [ ];
|
|
paths = [ ];
|
|
|
|
auto_update_enabled = false;
|
|
update_frequency = "6h";
|
|
};
|
|
};
|
|
};
|
|
|
|
nginx.virtualHosts."hs.ccnlc.eu" = {
|
|
forceSSL = true;
|
|
enableACME = true;
|
|
#quic = true;
|
|
http3 = true;
|
|
|
|
locations = {
|
|
"/" = {
|
|
proxyPass = "http://localhost:${toString config.services.headscale.port}";
|
|
proxyWebsockets = true;
|
|
};
|
|
|
|
"/metrics" = mkIf metricsEnable {
|
|
proxyPass = "http://${toString config.services.headscale.settings.metrics_listen_addr}/metrics";
|
|
};
|
|
};
|
|
|
|
extraConfig = ''
|
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
|
'';
|
|
};
|
|
};
|
|
|
|
systemd.services = {
|
|
tailscaled.after = [ "headscale.service" ];
|
|
headscale = {
|
|
environment = {
|
|
# required to use ssh
|
|
HEADSCALE_EXPERIMENTAL_FEATURE_SSH = "1";
|
|
};
|
|
};
|
|
};
|
|
|
|
services.headscale.settings.acl_policy_path = pkgs.writeTextFile {
|
|
name = "headscale-acl.hujson";
|
|
text = builtins.toJSON {
|
|
acls = [
|
|
(mkAcl [ "tag:client" ] [
|
|
"tag:client:*"
|
|
"tag:server:*"
|
|
]) # client -> {client, server}
|
|
(mkAcl [
|
|
"tag:client"
|
|
"tag:server"
|
|
] [ "tag:backup:${toString options.modules.server.rsync-daemon.port.default}" ])
|
|
];
|
|
|
|
ssh = [
|
|
(mkSshAcl [ "tag:client" ] [
|
|
"tag:server"
|
|
"tag:client"
|
|
] [ "ny" ]) # client -> {client, server}
|
|
];
|
|
|
|
tags = [
|
|
"tag:client"
|
|
"tag:server"
|
|
"tag:backup"
|
|
];
|
|
|
|
tagOwners =
|
|
let
|
|
users = [ "ny" ];
|
|
tags = map (name: "tag:${name}") [
|
|
"server"
|
|
"client"
|
|
"backup"
|
|
];
|
|
in
|
|
lib.genAttrs tags (_: users);
|
|
|
|
autoApprovers = {
|
|
exitNode = [ "*" ];
|
|
};
|
|
};
|
|
};
|
|
}
|