nixos: Working l2mesh with IPsec
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				CI / Check, build and cache Nix flake (push) Successful in 17m15s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	CI / Check, build and cache Nix flake (push) Successful in 17m15s
				
			This commit is contained in:
		@@ -1,7 +1,8 @@
 | 
			
		||||
{ lib, pkgs, config, vpns, ... }:
 | 
			
		||||
{ lib, config, vpns, ... }:
 | 
			
		||||
let
 | 
			
		||||
  inherit (lib) optionalString mapAttrsToList concatStringsSep filterAttrs mkIf mkMerge;
 | 
			
		||||
  inherit (lib.my) isIPv6;
 | 
			
		||||
  inherit (builtins) any attrValues;
 | 
			
		||||
  inherit (lib) optionalString mapAttrsToList concatStringsSep concatMapStringsSep filterAttrs mkIf mkMerge;
 | 
			
		||||
  inherit (lib.my) isIPv6 mkOpt';
 | 
			
		||||
 | 
			
		||||
  vxlanPort = 4789;
 | 
			
		||||
 | 
			
		||||
@@ -24,38 +25,34 @@ let
 | 
			
		||||
        Local = ownAddr;
 | 
			
		||||
        MacLearning = true;
 | 
			
		||||
        DestinationPort = vxlanPort;
 | 
			
		||||
        PortRange = "${toString vxlanPort}-${toString (vxlanPort + 1)}";
 | 
			
		||||
        Independent = true;
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
    links."20-l2mesh-${name}" = {
 | 
			
		||||
      matchConfig.Name = mesh.interface;
 | 
			
		||||
      # TODO: ipv6? ipsec?
 | 
			
		||||
      linkConfig.MTUBytes = "1450";
 | 
			
		||||
    };
 | 
			
		||||
    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);
 | 
			
		||||
      linkConfig.MTUBytes =
 | 
			
		||||
      let
 | 
			
		||||
        espOverhead =
 | 
			
		||||
          if (!mesh.security.enable) then 0
 | 
			
		||||
          else
 | 
			
		||||
            # SPI + seq + IV + pad / header + ICV
 | 
			
		||||
            4 + 4 + (if mesh.security.encrypt then 8 else 0) + 2 + 16;
 | 
			
		||||
        # UDP + VXLAN + Ethernet + L3 (IPv4/IPv6)
 | 
			
		||||
        overhead = espOverhead + 8 + 8 + 14 + mesh.l3Overhead;
 | 
			
		||||
      in
 | 
			
		||||
      toString (mesh.baseMTU - overhead);
 | 
			
		||||
 | 
			
		||||
      bridgeFDBs = mapAttrsToList (n: peer: {
 | 
			
		||||
        bridgeFDBConfig = {
 | 
			
		||||
          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
 | 
			
		||||
    '';
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  vxlanAllow = vni: "udp dport ${toString vxlanPort} @th,96,24 ${toString vni} accept";
 | 
			
		||||
  mkFirewallConfig = name: mesh: with info mesh;
 | 
			
		||||
  let
 | 
			
		||||
    netProto = if (isIPv6 ownAddr) then "ip6" else "ip";
 | 
			
		||||
@@ -63,8 +60,11 @@ let
 | 
			
		||||
  ''
 | 
			
		||||
    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
 | 
			
		||||
        ${optionalString mesh.security.enable ''
 | 
			
		||||
          udp dport isakmp accept
 | 
			
		||||
          meta l4proto esp accept
 | 
			
		||||
        ''}
 | 
			
		||||
        ${optionalString (!mesh.security.enable) (vxlanAllow mesh.vni)}
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
      chain input {
 | 
			
		||||
@@ -72,12 +72,63 @@ let
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  '';
 | 
			
		||||
 | 
			
		||||
  mkLibreswanConfig = name: mesh: with info mesh; {
 | 
			
		||||
    enable = true;
 | 
			
		||||
    connections = mkMerge (mapAttrsToList
 | 
			
		||||
      (pName: peer: {
 | 
			
		||||
        "l2mesh-${name}-${pName}" = ''
 | 
			
		||||
          keyexchange=ike
 | 
			
		||||
          hostaddrfamily=ipv${if mesh.ipv6 then "6" else "4"}
 | 
			
		||||
          type=transport
 | 
			
		||||
 | 
			
		||||
          left=${ownAddr}
 | 
			
		||||
          leftprotoport=udp/${toString vxlanPort}
 | 
			
		||||
          right=${peer.addr}
 | 
			
		||||
          rightprotoport=udp/${toString vxlanPort}
 | 
			
		||||
          rightupdown=
 | 
			
		||||
 | 
			
		||||
          auto=start
 | 
			
		||||
          authby=secret
 | 
			
		||||
          phase2=esp
 | 
			
		||||
          esp=${if mesh.security.encrypt then "aes_gcm256" else "null-sha256"}
 | 
			
		||||
          ikev2=yes
 | 
			
		||||
          modecfgpull=no
 | 
			
		||||
        '';
 | 
			
		||||
      })
 | 
			
		||||
    otherPeers);
 | 
			
		||||
  };
 | 
			
		||||
  genSecrets = name: mesh: with info mesh; concatMapStringsSep "\n" (p: ''
 | 
			
		||||
    echo "${ownAddr} ${p.addr} : PSK \"$(< "${config.my.vpns.l2.pskFiles.${name}}")\"" >> /run/l2mesh.secrets
 | 
			
		||||
  '') (attrValues otherPeers);
 | 
			
		||||
  anySecurity = any (c: c.security.enable) (attrValues memberMeshes);
 | 
			
		||||
in
 | 
			
		||||
{
 | 
			
		||||
  options = {
 | 
			
		||||
    my.vpns.l2 = with lib.types; {
 | 
			
		||||
      pskFiles = mkOpt' (attrsOf str) { } "PSK files for secured meshes.";
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  config = {
 | 
			
		||||
    systemd.network = mkMerge (mapAttrsToList mkNetConfig memberMeshes);
 | 
			
		||||
    # TODO: finish this...
 | 
			
		||||
    #services.libreswan = mkMerge (mapAttrsToList mkLibreswanConfig (filterAttrs (_: c: c.security.enable) memberMeshes));
 | 
			
		||||
 | 
			
		||||
    environment.etc."ipsec.d/l2mesh.secrets" = mkIf anySecurity {
 | 
			
		||||
      source = "/run/l2mesh.secrets";
 | 
			
		||||
    };
 | 
			
		||||
    systemd.services.ipsec = mkIf anySecurity {
 | 
			
		||||
      preStart = ''
 | 
			
		||||
        oldUmask="$(umask)"
 | 
			
		||||
        umask 006
 | 
			
		||||
 | 
			
		||||
        > /run/l2mesh.secrets
 | 
			
		||||
        ${concatStringsSep "\n" (mapAttrsToList genSecrets memberMeshes)}
 | 
			
		||||
 | 
			
		||||
        umask "$oldUmask"
 | 
			
		||||
      '';
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    services.libreswan = mkMerge (mapAttrsToList mkLibreswanConfig (filterAttrs (_: c: c.security.enable) memberMeshes));
 | 
			
		||||
    my.firewall.extraRules = concatStringsSep "\n" (mapAttrsToList mkFirewallConfig (filterAttrs (_: c: c.firewall) memberMeshes));
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user