nftable-migration
  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}