blob: 1e1b0d8af64f251c8bf1611016887db88efe2ce5 [file] [log] [blame]
Skyler Grey61f0f852024-06-09 00:02:53 +00001# SPDX-FileCopyrightText: 2024 Clicks Codes
2#
3# SPDX-License-Identifier: GPL-3.0-only
4
Skyler Grey107cb5e2024-06-10 17:15:45 +00005{
6 lib,
7 config,
8 pkgs,
9 ...
10}:
Skyler Grey61f0f852024-06-09 00:02:53 +000011let
12 cfg = config.clicks.services.headscale;
13in
14{
15 options.clicks.services.headscale = {
16 enable = lib.mkEnableOption "The headscale control server for tailscale";
17 url = lib.mkOption {
18 type = lib.types.str;
19 description = "The url users should connect to to register a new device";
20 };
21 addr = lib.mkOption {
22 type = lib.types.str;
23 description = "Where to host headscale";
24 default = "0.0.0.0";
25 };
26 port = lib.mkOption {
27 type = lib.types.int;
28 description = "Port to host headscale on";
29 default = 80;
30 };
31 oidc = {
32 enable = lib.mkEnableOption "Enable OIDC";
33 issuer = lib.mkOption {
34 type = lib.types.str;
35 description = "Issuer URL for your OIDC provider";
36 };
37 allowed_groups = lib.mkOption {
38 type = lib.types.nullOr (lib.types.listOf lib.types.str);
39 description = "List of groups to allow authentication from";
40 };
41 client_id = lib.mkOption {
42 type = lib.types.str;
43 description = "Client ID";
44 default = "headscale";
45 };
46 client_secret_path = lib.mkOption {
47 type = lib.types.str;
48 description = "Client secret file path";
49 };
50 };
51 database_password_path = lib.mkOption {
52 type = lib.types.str;
53 description = "Database password file path";
54 };
55 noise_private_key_path = lib.mkOption {
56 type = lib.types.nullOr lib.types.str;
57 description = "Noise private key file path";
58 default = null;
59 };
60 private_key_path = lib.mkOption {
61 type = lib.types.nullOr lib.types.str;
62 description = "Headscale private key file path";
63 default = null;
64 };
Skyler Grey107cb5e2024-06-10 17:15:45 +000065 acl = lib.mkOption {
66 type = lib.types.nullOr (lib.types.attrsOf lib.types.anything);
67 description = "ACL rules for headscale to enforce";
68 default = null;
69 };
Skyler Grey61f0f852024-06-09 00:02:53 +000070 };
71
72 config = lib.mkIf cfg.enable {
73 clicks = {
74 services.postgres.enable = true;
75 services.postgres.databases.headscale = cfg.database_password_path;
76 services.postgres.secretRequiredGroups = [ "headscale" ];
77 };
78
79 services.headscale = {
80 enable = true;
81
82 address = cfg.addr;
83 port = cfg.port;
84
85 settings.db_type = "postgres";
86 settings.db_port = config.services.postgresql.settings.port;
87 settings.db_user = "headscale";
88 settings.db_password_file = cfg.database_password_path;
89 settings.db_name = "headscale";
90 settings.db_host = lib.clicks.constants.hosts.standard;
91
92 settings.server_url = "https://${cfg.url}";
93
94 settings.ip_prefixes = "100.64.0.0/10";
95
96 settings.noise.private_key_path = lib.mkIf (
97 cfg.noise_private_key_path != null
98 ) cfg.noise_private_key_path;
99 settings.private_key_path = lib.mkIf (cfg.private_key_path != null) cfg.private_key_path;
100
101 settings.dns_config = {
102 nameservers = [
103 "1.1.1.1"
104 "1.0.0.1"
105 ];
Skyler Grey4d386d12024-06-09 16:05:44 +0000106 domains = [ cfg.url ];
Skyler Grey61f0f852024-06-09 00:02:53 +0000107 override_local_dns = true;
108 base_domain = cfg.url;
109 };
110
111 settings.oidc = lib.mkIf cfg.oidc.enable {
112 only_start_if_oidc_is_available = true;
113
114 issuer = cfg.oidc.issuer;
115
116 client_id = cfg.oidc.client_id;
117 client_secret_path = cfg.oidc.client_secret_path;
118
119 allowed_groups = lib.mkIf (cfg.oidc.allowed_groups != null) cfg.oidc.allowed_groups;
120 strip_email_domain = true;
121 };
Skyler Grey107cb5e2024-06-10 17:15:45 +0000122
123 settings.acl_policy_path = lib.mkIf (cfg.acl != null) (
124 pkgs.writers.writeJSON "tailscale-acls.json" cfg.acl
125 );
Skyler Grey61f0f852024-06-09 00:02:53 +0000126 };
127
128 systemd.services.headscale.requires = [ "postgresql.service" ];
129 systemd.services.headscale.after = [ "postgresql.service" ];
130
131 networking.firewall.allowedTCPPorts = lib.mkIf (cfg.addr == "0.0.0.0") [ cfg.port ];
132 };
133}