diff --git a/flake.lock b/flake.lock index 450484b..f1b4b66 100644 --- a/flake.lock +++ b/flake.lock @@ -167,11 +167,11 @@ }, "nixpkgs-mine": { "locked": { - "lastModified": 1665003636, - "narHash": "sha256-hZw6brIzr0xIQnhhIFqVD+qUjFWc8Nbhv4/PfSUhv8w=", + "lastModified": 1665936538, + "narHash": "sha256-iOG64H3nsdQ0kVLN84bNEnzhxhtiGoR+WeUneEKD7II=", "owner": "devplayer0", "repo": "nixpkgs", - "rev": "5e97ac47c943f04f7c06d16fcc7cdb16419d0dc4", + "rev": "d23938373f803710a8c223c8715eb61299f2e92a", "type": "github" }, "original": { @@ -183,11 +183,11 @@ }, "nixpkgs-mine-stable": { "locked": { - "lastModified": 1662202463, - "narHash": "sha256-Be5CmzLWGiLtrwYn/NO79ZM6x0H0z+tlWkmW23hNW38=", + "lastModified": 1665936575, + "narHash": "sha256-zIJSDh2US8Y2CrVZSgm8RHFEkz1UAXgMR1zYLsbBhK4=", "owner": "devplayer0", "repo": "nixpkgs", - "rev": "8f64505d3c782ba5d04e1703c466626ea9199716", + "rev": "e01cbf49941d23928ffc2e362449b9500af2c4c5", "type": "github" }, "original": { @@ -199,11 +199,11 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1664883812, - "narHash": "sha256-wqBAcVRBxls2nVaNeQaOy9SRg/bvEUiD26TQDprIg8U=", + "lastModified": 1665856284, + "narHash": "sha256-FQusb2DuFo8UF0jhUSPFhDKc40jRbHcdyyw4anN20WA=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "fe76645aaf2fac3baaa2813fd0089930689c53b5", + "rev": "be44bf672c61a284f759898d1e59077b00655c4c", "type": "github" }, "original": { @@ -214,11 +214,11 @@ }, "nixpkgs-unstable": { "locked": { - "lastModified": 1664780719, - "narHash": "sha256-Oxe6la5dSqRfJogjtY4sRzJjDDqvroJIVkcGEOT87MA=", + "lastModified": 1665848363, + "narHash": "sha256-3Jow1YxzPtQnck1bAAvbVxgRH4gNnkIdw871Vm6UtAU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "fd54651f5ffb4a36e8463e0c327a78442b26cbe7", + "rev": "83b198a2083774844962c854f811538323f9f7b1", "type": "github" }, "original": { diff --git a/nixos/default.nix b/nixos/default.nix index 22f1722..017ef71 100644 --- a/nixos/default.nix +++ b/nixos/default.nix @@ -46,7 +46,7 @@ let ]; _module.args = { - inherit (cfg) secretsPath; + inherit (cfg) secretsPath vpns; inherit (config') assignments; pkgs' = allPkgs; }; @@ -116,6 +116,24 @@ let }; }; + l2PeerOpts = with lib.types; { + options = { + addr = mkOpt' str null "Address."; + }; + }; + l2MeshOpts = with lib.types; { name, ... }: { + options = { + interface = mkOpt' str name "Name of VXLAN interface."; + firewall = mkBoolOpt' true "Whether to generate firewall rules."; + vni = mkOpt' ints.unsigned 1 "VXLAN VNI."; + peers = mkOpt' (attrsOf (submodule l2PeerOpts)) { } "Peers."; + security = { + enable = mkBoolOpt' true "Whether to enable IPsec authentication."; + encrypt = mkBoolOpt' false "Whether to enable IPsec encryption."; + }; + }; + }; + systemOpts = with lib.types; { name, ... }@args: let config' = args.config; @@ -161,6 +179,9 @@ in secretsPath = mkOpt' path null "Path to encrypted secret files."; modules = mkOpt' (attrsOf commonOpts.moduleType) { } "NixOS modules to be exported by nixfiles."; systems = mkOpt' (attrsOf (submodule systemOpts)) { } "NixOS systems to be exported by nixfiles."; + vpns = { + l2 = mkOpt' (attrsOf (submodule l2MeshOpts)) { } "Layer 2 meshes."; + }; }; }; diff --git a/nixos/modules/_list.nix b/nixos/modules/_list.nix index fc859d2..2170069 100644 --- a/nixos/modules/_list.nix +++ b/nixos/modules/_list.nix @@ -15,5 +15,6 @@ pdns = ./pdns.nix; nginx-sso = ./nginx-sso.nix; gui = ./gui.nix; + l2mesh = ./l2mesh.nix; }; } diff --git a/nixos/modules/l2mesh.nix b/nixos/modules/l2mesh.nix new file mode 100644 index 0000000..3213349 --- /dev/null +++ b/nixos/modules/l2mesh.nix @@ -0,0 +1,78 @@ +{ lib, pkgs, config, vpns, ... }: +let + inherit (lib) optionalString mapAttrsToList concatStringsSep filterAttrs mkIf mkMerge; + inherit (lib.my) isIPv6; + + vxlanPort = 4789; + + selfName = config.system.name; + memberMeshes = filterAttrs (_: c: c.peers ? "${selfName}") vpns.l2; + + info = mesh: { + ownAddr = mesh.peers."${selfName}".addr; + otherPeers = filterAttrs (n: _: n != selfName) mesh.peers; + }; + + mkNetConfig = name: mesh: with info mesh; { + netdevs."30-l2mesh-${name}" = { + netdevConfig = { + Name = mesh.interface; + Kind = "vxlan"; + }; + vxlanConfig = { + VNI = mesh.vni; + Local = ownAddr; + MacLearning = true; + DestinationPort = vxlanPort; + Independent = true; + }; + }; + networks."90-l2mesh-${name}" = { + matchConfig.Name = mesh.interface; + extraConfig = concatStringsSep "\n" (mapAttrsToList (n: peer: '' + [BridgeFDB] + MACAddress=00:00:00:00:00:00 + Destination=${peer.addr} + '') otherPeers); + }; + }; + + mkLibreswanConfig = name: mesh: with info mesh; { + enable = true; + # TODO: finish this... + connections."l2mesh-${name}" = '' + keyexchange=ike + type=transport + left=${ownAddr} + + auto=start + phase2=esp + ikev2=yes + ''; + }; + + mkFirewallConfig = name: mesh: with info mesh; + let + netProto = if (isIPv6 ownAddr) then "ip6" else "ip"; + in + '' + table inet filter { + chain l2mesh-${name} { + ${optionalString mesh.security.enable "meta l4proto esp accept"} + udp dport ${toString vxlanPort} @th,96,24 ${toString mesh.vni} accept + return + } + chain input { + ${netProto} daddr ${ownAddr} ${netProto} saddr { ${concatStringsSep ", " (mapAttrsToList (_: p: p.addr) otherPeers)} } jump l2mesh-${name} + } + } + ''; +in +{ + config = { + systemd.network = mkMerge (mapAttrsToList mkNetConfig memberMeshes); + # TODO: finish this... + #services.libreswan = mkMerge (mapAttrsToList mkLibreswanConfig (filterAttrs (_: c: c.security.enable) memberMeshes)); + my.firewall.extraRules = concatStringsSep "\n" (mapAttrsToList mkFirewallConfig (filterAttrs (_: c: c.firewall) memberMeshes)); + }; +}