|
1 | | -# TODO add assertions like this: |
2 | | -# perMachine = { instances }: |
3 | | -# # ... |
4 | | -# instanceNames = builtins.attrNames instances; |
5 | | -# # ..... |
6 | | -# assertions = |
7 | | -# [ |
8 | | -# { |
9 | | -# assertion = builtins.length instanceNames == 1; |
10 | | -# message = "The zerotier module currently only supports one instance per machine, but found ${builtins.toString instanceNames} on machine ${config.clan.core.settings.machine.name}"; |
11 | | -# } |
12 | | -# ] |
13 | | -# # TODO: remove this assertion once we start verifying constraints again |
14 | | -# ++ (lib.mapAttrsToList (_instanceName: instance: { |
15 | | -# assertion = builtins.length (lib.attrNames instance.roles.controller.machines) == 1; |
16 | | -# message = "ZeroTier only supports one controller per network"; |
17 | | -# }) instances); |
18 | | - |
19 | 1 | { lib, ... }: |
20 | 2 | { |
21 | 3 | _class = "clan.service"; |
22 | 4 | manifest.name = "wireguard"; |
23 | 5 |
|
24 | | - # Define what roles exist |
| 6 | + # Peer options and configuration |
25 | 7 | roles.peer = { |
| 8 | + |
26 | 9 | interface = { |
27 | 10 |
|
28 | | - # These options can be set via 'roles.client.settings' |
29 | 11 | options.ip = lib.mkOption { |
30 | 12 | type = lib.types.str; |
31 | | - # default = "0.0.0.0"; |
32 | 13 | example = "192.168.8.1"; |
33 | 14 | description = '' |
34 | 15 | IP address of the host. |
35 | 16 | ''; |
36 | 17 | }; |
| 18 | + |
| 19 | + options.extraIPs = lib.mkOption { |
| 20 | + type = lib.types.listOf lib.types.str; |
| 21 | + default = []; |
| 22 | + example = [ "192.168.2.0/24" ]; |
| 23 | + description = '' |
| 24 | + IP address of the host. |
| 25 | + ''; |
| 26 | + }; |
37 | 27 | }; |
38 | 28 |
|
39 | | - # Maps over all instances and produces one result per instance. |
40 | 29 | perInstance = |
41 | 30 | { |
42 | 31 | instanceName, |
43 | 32 | settings, |
44 | | - machine, |
45 | 33 | roles, |
46 | 34 | ... |
47 | 35 | }: |
|
51 | 39 | nixosModule = |
52 | 40 | { config, ... }: |
53 | 41 | { |
54 | | - |
55 | | - networking.wireguard.interfaces = |
56 | | - let |
57 | | - # Get all controller names: |
58 | | - allControllerNames = (lib.attrNames roles.controller.machines); |
59 | | - in |
60 | | - { |
61 | | - |
62 | | - "${instanceName}" = { |
63 | | - |
64 | | - ips = [ "${settings.ip}/24" ]; |
65 | | - |
66 | | - peers = map (name: { |
67 | | - |
68 | | - # Public key of the server (not a file path). |
69 | | - publicKey = ( |
70 | | - builtins.readFile ( |
71 | | - config.clan.core.settings.directory |
72 | | - + "/vars/per-machine/${name}/wireguard-${instanceName}/publickey/value" |
73 | | - ) |
74 | | - ); |
75 | | - |
76 | | - # Don't forward all the traffic via VPN, only particular subnets |
77 | | - allowedIPs = [ "192.168.8.0/24" ]; |
78 | | - |
79 | | - # Server IP and port. |
80 | | - endpoint = roles.controller.machines."${name}".settings.endpoint; |
81 | | - |
82 | | - # Send keepalives every 25 seconds. Important to keep NAT tables |
83 | | - # alive. |
84 | | - persistentKeepalive = 25; |
85 | | - |
86 | | - }) allControllerNames; |
87 | | - }; |
| 42 | + networking.wireguard.interfaces = { |
| 43 | + "${instanceName}" = { |
| 44 | + ips = [ "${settings.ip}/24" ]; |
| 45 | + peers = map (name: { |
| 46 | + # Public key of the server |
| 47 | + publicKey = ( |
| 48 | + builtins.readFile ( |
| 49 | + config.clan.core.settings.directory |
| 50 | + + "/vars/per-machine/${name}/wireguard-${instanceName}/publickey/value" |
| 51 | + ) |
| 52 | + ); |
| 53 | + |
| 54 | + # Don't forward all the traffic via VPN, only particular subnets |
| 55 | + allowedIPs = [ "192.168.8.0/24" ]; |
| 56 | + |
| 57 | + # Server IP and port |
| 58 | + endpoint = roles.controller.machines."${name}".settings.endpoint; |
| 59 | + |
| 60 | + # Send keepalives every 25 seconds to keep NAT tables alive |
| 61 | + persistentKeepalive = 25; |
| 62 | + |
| 63 | + }) (lib.attrNames roles.controller.machines); |
88 | 64 | }; |
| 65 | + }; |
89 | 66 | }; |
90 | 67 | }; |
91 | 68 | }; |
92 | 69 |
|
| 70 | + # Controller options and configuration |
93 | 71 | roles.controller = { |
94 | 72 | interface = { |
95 | | - # These options can be set via 'roles.server.settings' |
96 | | - # options.dynamicIp.enable =with lib; mkOption { type = bool; }; |
97 | | - |
98 | 73 | options.endpoint = lib.mkOption { |
99 | 74 | type = lib.types.str; |
100 | 75 | example = "vpn.pablo.tools:51820"; |
|
143 | 118 | allowedIPs = [ |
144 | 119 | # TODO we might want to add extra ip's here, e.g. for birne? |
145 | 120 | roles.peer.machines."${peer}".settings.ip |
146 | | - ]; |
| 121 | + ] ++ roles.peer.machines."${peer}".settings.extraIPs; |
147 | 122 |
|
148 | 123 | persistentKeepalive = 25; |
149 | 124 | }) (lib.attrNames roles.peer.machines); |
|
153 | 128 | }; |
154 | 129 | }; |
155 | 130 |
|
156 | | - # Maps over all machines and produces one result per machine. |
| 131 | + # Maps over all machines and produces one result per machine, regardless of role |
157 | 132 | perMachine = |
| 133 | + { instances, ... }: |
158 | 134 | { |
159 | | - instances, |
160 | | - machine, |
161 | | - # instanceName, |
162 | | - ... |
163 | | - }: |
164 | | - { |
165 | | - # Analog to 'perSystem' of flake-parts. |
166 | | - # For every machine of this service we will add exactly one nixosModule to a machine |
167 | 135 | nixosModule = |
168 | 136 | { config, pkgs, ... }: |
169 | 137 | { |
170 | 138 |
|
| 139 | + # Generate keys for each instance of the host |
| 140 | + clan.core.vars.generators = lib.mapAttrs' ( |
| 141 | + name: value: |
| 142 | + lib.nameValuePair ("wireguard-" + name) { |
| 143 | + files.publickey.secret = false; |
| 144 | + files.privatekey = { }; |
| 145 | + runtimeInputs = with pkgs; [ wireguard-tools ]; |
| 146 | + script = '' |
| 147 | + wg genkey > $out/privatekey |
| 148 | + wg pubkey < $out/privatekey > $out/publickey |
| 149 | + ''; |
| 150 | + } |
| 151 | + ) instances; |
| 152 | + |
| 153 | + # Set the private key for each instance |
171 | 154 | networking.wireguard.interfaces = builtins.mapAttrs (name: _: { |
172 | 155 | privateKeyFile = "${config.clan.core.vars.generators."wireguard-${name}".files."privatekey".path}"; |
173 | 156 | }) instances; |
174 | | - |
175 | | - clan.core.vars.generators = |
176 | | - |
177 | | - # mapAttrs' (name: value: nameValuePair ("foo_" + name) ("bar-" + value)) |
178 | | - # { x = "a"; y = "b"; } |
179 | | - # => { foo_x = "bar-a"; foo_y = "bar-b"; } |
180 | | - |
181 | | - lib.mapAttrs' ( |
182 | | - name: value: |
183 | | - lib.nameValuePair ("wireguard-" + name) { |
184 | | - files.publickey.secret = false; |
185 | | - files.privatekey = { }; |
186 | | - runtimeInputs = with pkgs; [ wireguard-tools ]; |
187 | | - script = '' |
188 | | - wg genkey > $out/privatekey |
189 | | - wg pubkey < $out/privatekey > $out/publickey |
190 | | - ''; |
191 | | - } |
192 | | - ) instances; |
193 | 157 | }; |
194 | 158 | }; |
195 | 159 | } |
0 commit comments