diff --git a/README.md b/README.md index 3c71fd9..a81504c 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Node options | Module | [Link](./modules/node.nix) | - | - | A module that store Guests (MicroVMs & Containers) | Module | [Link](./modules/guests) | zfs, disko, node options | - | This module implements a common interface to use guest systems with microvms or nixos-containers. Restic hetzner storage box setup | Module | [Link](./modules/restic.nix) | agenix, agenix-rekey | - | This module exposes new options for restic backups that allow a simple setup of hetzner storage boxes. There's [an app](./apps/setup-hetzner-storage-boxes.nix) that you should expose on your flake to automate remote setup. Wireguard overlay networks | Module | [Link](./modules/wireguard.nix) | agenix, agenix-rekey, nftables-firewall, inputs.self.nodes | - | This module automatically creates cross-node wireguard networks including automatic semi-stable ip address assignments +nix-topology for wireguard | Module | [Link](./modules/topology-wireguard.nix) | nix-topology | - | This module automatically adds wireguard networks and interfaces based on the wireguard configuration from our wireguard module #### Home Manager Modules diff --git a/modules/default.nix b/modules/default.nix index 2a5eea3..92eef87 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -8,6 +8,7 @@ ./nginx.nix ./node.nix ./restic.nix + ./topology-wireguard.nix ./wireguard.nix ]; diff --git a/modules/topology-wireguard.nix b/modules/topology-wireguard.nix new file mode 100644 index 0000000..09840a0 --- /dev/null +++ b/modules/topology-wireguard.nix @@ -0,0 +1,80 @@ +{ + config, + lib, + inputs ? {}, + ... +}: let + inherit + (lib) + flip + mapAttrsToList + mkDefault + mkEnableOption + mkIf + mkMerge + filter + ; + + headOrNull = xs: + if xs == [] + then null + else builtins.head xs; + + networkId = wgName: "wireguard-${wgName}"; +in { + options.topology.extractors.wireguard.enable = mkEnableOption "topology wireguard extractor" // {default = true;}; + + config = mkIf (config.topology.extractors.wireguard.enable && config ? wireguard) { + # Create networks (this will be duplicated by each node, + # but it doesn't matter and will be merged anyway) + topology.networks = mkMerge ( + flip mapAttrsToList config.wireguard ( + wgName: _: let + inherit (lib.wireguard inputs wgName) networkCidrs; + in { + ${networkId wgName} = { + name = mkDefault "Wireguard network '${wgName}'"; + icon = "interfaces.wireguard"; + cidrv4 = headOrNull (filter lib.net.ip.isv4 networkCidrs); + cidrv6 = headOrNull (filter lib.net.ip.isv6 networkCidrs); + }; + } + ) + ); + + # Assign network and physical connections to related interfaces + topology.self.interfaces = mkMerge ( + flip mapAttrsToList config.wireguard ( + wgName: wgCfg: let + inherit + (lib.wireguard inputs wgName) + participatingServerNodes + wgCfgOf + ; + + isServer = wgCfg.server.host != null; + filterSelf = filter (x: x != config.node.name); + + # The list of peers that are "physically" connected in the wireguard network, + # meaning they communicate directly with each other. + connectedPeers = + if isServer + then + # Other servers in the same network + filterSelf participatingServerNodes + else [wgCfg.client.via]; + in { + ${wgCfg.linkName} = { + network = networkId wgName; + virtual = true; + renderer.hidePhysicalConnections = true; + physicalConnections = flip map connectedPeers (peer: { + node = inputs.self.nodes.${peer}.config.topology.id; + interface = (wgCfgOf peer).linkName; + }); + }; + } + ) + ); + }; +}