system-manager-wakasu
1{ lib }:
2let
3 /**
4 Check if the given name matches the current hostname.
5
6 @param hostname The current hostname to compare against
7 @param n The name to check
8 @return true if n equals hostname, false otherwise
9 */
10 isCurrentHost = hostname: n: n == hostname;
11
12 /**
13 Check if a host has a VPN public key configured.
14
15 @param host The host configuration to check
16 @return true if host has a non-empty VPN public key, false otherwise
17 */
18 hasVPNPublicKey = host: (lib.attrsets.attrByPath [ "net" "vpn" "pubkey" ] "" host) != "";
19
20 /**
21 Check if a host has VPN IP addresses configured.
22
23 @param host The host configuration to check
24 @return true if host has at least one VPN IP address, false otherwise
25 */
26 hasVPNips = host: (builtins.length (lib.attrsets.attrByPath [ "net" "vpn" "ips" ] [ ] host)) > 0;
27
28 /**
29 Check if a host has network IP addresses configured.
30
31 @param host The host configuration to check
32 @return true if host has at least one VPN IP address, false otherwise
33 */
34 hasIps = host: (builtins.length (lib.attrsets.attrByPath [ "net" "ips" ] [ ] host)) > 0;
35
36 /**
37 Return true if the given host has a list of Syncthing folder configured.
38
39 @param host The host configuration to check
40 @return true if host has syncthing folders configured, false otherwise
41 */
42 hasSyncthingFolders =
43 host:
44 builtins.hasAttr "syncthing" host
45 && builtins.hasAttr "folders" host.syncthing
46 && (builtins.length (lib.attrsets.attrValues host.syncthing.folders)) > 0;
47
48 /**
49 Check if a host has SSH host keys configured.
50
51 @param host The host configuration to check
52 @return true if host has SSH host keys, false otherwise
53 */
54 hasSSHHostKeys = host: builtins.hasAttr "ssh" host && builtins.hasAttr "hostKey" host.ssh;
55
56 /**
57 Get the path for the given folder, either using the host specified path or the default one.
58
59 @param name The folder name
60 @param folder The folder configuration
61 @param folders The complete folders configuration
62 @return The path for the folder
63 */
64 syncthingFolderPath =
65 name: folder: folders:
66 lib.attrsets.attrByPath [ "path" ] folders."${name}".path folder;
67
68 /**
69 Filter machines with the given syncthing folder.
70
71 @param hostname The current hostname to exclude from results
72 @param folderName The folder name to filter by
73 @param machines The set of all machines
74 @return Filtered set of machines that have the specified folder and are not the current host
75 */
76 syncthingMachinesWithFolder =
77 hostname: folderName: machines:
78 lib.attrsets.filterAttrs (
79 name: value:
80 hasSyncthingFolders value
81 && !(isCurrentHost hostname name)
82 && (builtins.hasAttr folderName value.syncthing.folders)
83 ) machines;
84
85 /**
86 Generate Syncthing addresses for a machine from its network configuration.
87
88 @param machine The machine configuration
89 @return List of TCP addresses (ips, vpn ips, and names) prefixed with "tcp://"
90 */
91 generateSyncthingAdresses =
92 machine:
93 builtins.map (x: "tcp://${x}") (
94 lib.attrsets.attrByPath [ "net" "ips" ] [ ] machine
95 ++ lib.attrsets.attrByPath [ "net" "vpn" "ips" ] [ ] machine
96 ++ lib.attrsets.attrByPath [ "net" "names" ] [ ] machine
97 );
98
99 /**
100 Get SSH host identifiers for a machine (names, IPs, and VPN IPs).
101
102 @param machine The machine configuration
103 @return List of all network identifiers for the machine
104 */
105 sshHostIdentifier =
106 machine:
107 lib.attrsets.attrByPath [ "net" "names" ] [ ] machine
108 ++ lib.attrsets.attrByPath [ "net" "ips" ] [ ] machine
109 ++ lib.attrsets.attrByPath [ "net" "vpn" "ips" ] [ ] machine;
110
111 /**
112 Generate host configuration mapping IPs to appropriate hostnames.
113
114 @param machine The machine configuration
115 @return Attribute set mapping IP addresses to corresponding hostnames
116 */
117 hostConfig =
118 machine:
119 builtins.listToAttrs (
120 map
121 (x: {
122 name = x;
123 value =
124 if (lib.strings.hasPrefix "10.100" x) then
125 builtins.filter (n: lib.strings.hasSuffix ".vpn" n) machine.net.names
126 else if (lib.strings.hasPrefix "192.168" x) then
127 builtins.filter (n: lib.strings.hasSuffix ".home" n) machine.net.names
128 else
129 [ ];
130 })
131 (
132 lib.attrsets.attrByPath [ "net" "ips" ] [ ] machine
133 ++ lib.attrsets.attrByPath [ "net" "vpn" "ips" ] [ ] machine
134 )
135 );
136
137 /**
138 Generate SSH configuration for a machine.
139
140 @param machine The machine configuration
141 @return Attribute set of SSH host configurations with hostnames, identity settings, etc.
142 */
143 sshConfig =
144 machine:
145 builtins.listToAttrs (
146 map
147 (x: {
148 name = x;
149 value = {
150 hostname =
151 if (lib.strings.hasSuffix ".vpn" x) then
152 builtins.head machine.net.vpn.ips
153 else if (lib.strings.hasSuffix ".home" x) then
154 builtins.head machine.net.ips
155 else
156 x;
157 forwardAgent = true;
158 identityFile = "~/.ssh/kyushu";
159 identityAgent = "empty";
160 };
161 })
162 (
163 builtins.filter (x: (lib.strings.hasSuffix ".home" x) || (lib.strings.hasSuffix ".vpn" x)) (
164 sshHostIdentifier machine
165 )
166 )
167 );
168
169 /**
170 Return a list of wireguard ips from a list of ips.
171
172 Essentially, it will append /32 to each element of the list.
173
174 @param ips List of IP addresses
175 @return List of IP addresses with /32 suffix for wireguard configuration
176 */
177 wg-ips = ips: builtins.map (x: "${x}/32") ips;
178
179 /**
180 Generate Wireguard peer configurations from a set of machines.
181
182 @param machines The set of all machines
183 @return List of wireguard peer configurations with allowedIPs and publicKey
184 */
185 generateWireguardPeers =
186 machines:
187 lib.attrsets.attrValues (
188 lib.attrsets.mapAttrs
189 (_name: value: {
190 allowedIPs = value.net.vpn.ips;
191 publicKey = value.net.vpn.pubkey;
192 })
193 (
194 lib.attrsets.filterAttrs (
195 name: value: name != "kerkouane" && (hasVPNPublicKey value) && (hasVPNips value)
196 ) machines
197 )
198 );
199
200 /**
201 Generate Syncthing folder configurations for the current machine.
202
203 @param hostname The current hostname
204 @param machine The current machine configuration
205 @param machines The set of all machines
206 @param folders The folder definitions
207 @return Attribute set of syncthing folder configurations
208 */
209 generateSyncthingFolders =
210 hostname: machine: machines: folders:
211 lib.attrsets.mapAttrs' (
212 name: value:
213 lib.attrsets.nameValuePair (syncthingFolderPath name value folders) {
214 inherit (folders."${name}") id;
215 label = name;
216 devices = lib.attrsets.mapAttrsToList (n: _v: n) (
217 syncthingMachinesWithFolder hostname name machines
218 );
219 rescanIntervalS = 3600 * 6; # TODO: make it configurable
220 }
221 ) (lib.attrsets.attrByPath [ "syncthing" "folders" ] { } machine);
222
223 /**
224 Generate Syncthing device configurations for all machines except the current one.
225
226 @param hostname The current hostname to exclude
227 @param machines The set of all machines
228 @return Attribute set of syncthing device configurations with IDs and addresses
229 */
230 generateSyncthingDevices =
231 hostname: machines:
232 lib.attrsets.mapAttrs
233 (_name: value: {
234 inherit (value.syncthing) id;
235 addresses = generateSyncthingAdresses value;
236 })
237 (
238 lib.attrsets.filterAttrs (
239 name: value: hasSyncthingFolders value && !(isCurrentHost hostname name)
240 ) machines
241 );
242
243 /**
244 Generate Syncthing GUI address for a machine.
245
246 @param machine The machine configuration
247 @return String in format "IP:8384" for accessing Syncthing GUI
248 */
249 syncthingGuiAddress =
250 machine:
251 (builtins.head (lib.attrsets.attrByPath [ "net" "vpn" "ips" ] [ "127.0.0.1" ] machine)) + ":8384";
252
253 /**
254 Generate SSH known_hosts entries for all machines with SSH host keys.
255
256 @param machines The set of all machines
257 @return String containing SSH known_hosts entries
258 */
259 sshKnownHosts =
260 machines:
261 lib.strings.concatStringsSep "\n" (
262 lib.attrsets.mapAttrsToList (
263 _name: value: "${lib.strings.concatStringsSep "," (sshHostIdentifier value)} ${value.ssh.hostKey}"
264 ) (lib.attrsets.filterAttrs (_name: hasSSHHostKeys) machines)
265 );
266
267 /**
268 Merge host configurations from all machines.
269
270 @param machines The set of all machines
271 @return Merged attribute set of all host configurations
272 */
273 hostConfigs =
274 machines: lib.attrsets.mergeAttrsList (lib.attrsets.mapAttrsToList (_name: hostConfig) machines);
275
276 /**
277 Generate and merge SSH configurations from all machines.
278
279 @param machines The set of all machines
280 @return Merged attribute set of all SSH configurations
281 */
282 sshConfigs =
283 machines:
284 lib.attrsets.mergeAttrsList (
285 lib.attrsets.mapAttrsToList (_name: sshConfig) (
286 lib.attrsets.filterAttrs (_name: _value: true) machines
287 )
288 );
289in
290{
291 inherit
292 syncthingFolderPath
293 hasSyncthingFolders
294 syncthingMachinesWithFolder
295 generateSyncthingAdresses
296 isCurrentHost
297 hasVPNPublicKey
298 hasVPNips
299 hasIps
300 hasSSHHostKeys
301 sshHostIdentifier
302 sshConfig
303 hostConfig
304 wg-ips
305 generateWireguardPeers
306 generateSyncthingFolders
307 generateSyncthingDevices
308 syncthingGuiAddress
309 sshKnownHosts
310 hostConfigs
311 sshConfigs
312 ;
313}