nixos/firewall: Add ability to forward per external IP
This commit is contained in:
		@@ -356,8 +356,7 @@ in
 | 
				
			|||||||
                nat = {
 | 
					                nat = {
 | 
				
			||||||
                  enable = true;
 | 
					                  enable = true;
 | 
				
			||||||
                  externalInterface = "wan";
 | 
					                  externalInterface = "wan";
 | 
				
			||||||
                  externalIP = assignments.internal.ipv4.address;
 | 
					                  forwardPorts."${assignments.internal.ipv4.address}" = [
 | 
				
			||||||
                  forwardPorts = [
 | 
					 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                      port = "http";
 | 
					                      port = "http";
 | 
				
			||||||
                      dst = allAssignments.middleman.internal.ipv4.address;
 | 
					                      dst = allAssignments.middleman.internal.ipv4.address;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,9 @@
 | 
				
			|||||||
{ lib, options, config, ... }:
 | 
					{ lib, options, config, ... }:
 | 
				
			||||||
let
 | 
					let
 | 
				
			||||||
  inherit (lib) optionalString concatStringsSep concatMapStringsSep optionalAttrs mkIf mkDefault mkMerge mkOverride;
 | 
					  inherit (builtins) typeOf replaceStrings attrNames;
 | 
				
			||||||
 | 
					  inherit (lib)
 | 
				
			||||||
 | 
					    optionalString concatStringsSep concatMapStringsSep mapAttrsToList optionalAttrs mkIf
 | 
				
			||||||
 | 
					    mkDefault mkMerge mkOverride;
 | 
				
			||||||
  inherit (lib.my) isIPv6 mkOpt' mkBoolOpt';
 | 
					  inherit (lib.my) isIPv6 mkOpt' mkBoolOpt';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  allowICMP = ''
 | 
					  allowICMP = ''
 | 
				
			||||||
@@ -63,8 +66,8 @@ in
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    nat = with options.networking.nat; {
 | 
					    nat = with options.networking.nat; {
 | 
				
			||||||
      enable = mkBoolOpt' true "Whether to enable IP forwarding and NAT.";
 | 
					      enable = mkBoolOpt' true "Whether to enable IP forwarding and NAT.";
 | 
				
			||||||
      inherit externalInterface externalIP;
 | 
					      inherit externalInterface;
 | 
				
			||||||
      forwardPorts = mkOpt' (listOf (submodule forwardOpts)) [ ] "List of port forwards.";
 | 
					      forwardPorts = mkOpt' (either (listOf (submodule forwardOpts)) (attrsOf (listOf (submodule forwardOpts)))) [ ] "IPv4 port forwards";
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -144,11 +147,16 @@ in
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    (mkIf cfg.nat.enable {
 | 
					    (mkIf cfg.nat.enable (
 | 
				
			||||||
 | 
					    let
 | 
				
			||||||
 | 
					      iifForward = typeOf cfg.nat.forwardPorts == "list" && cfg.nat.forwardPorts != [ ];
 | 
				
			||||||
 | 
					      dipForward = typeOf cfg.nat.forwardPorts == "set" && cfg.nat.forwardPorts != { };
 | 
				
			||||||
 | 
					    in
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
      assertions = [
 | 
					      assertions = [
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          assertion = with cfg.nat; (forwardPorts != [ ]) -> (externalInterface != null);
 | 
					          assertion = with cfg.nat; iifForward -> (externalInterface != null);
 | 
				
			||||||
          message = "my.firewall.nat.forwardPorts requires my.firewall.nat.external{Interface,IP}";
 | 
					          message = "my.firewall.nat.forwardPorts as list requires my.firewall.nat.externalInterface";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      ];
 | 
					      ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -171,43 +179,68 @@ in
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      my.firewall.extraRules =
 | 
					      my.firewall.extraRules =
 | 
				
			||||||
        let
 | 
					        let
 | 
				
			||||||
 | 
					          ipK = ip: "ip${optionalString (isIPv6 ip) "6"}";
 | 
				
			||||||
 | 
					          ipEscaped = replaceStrings ["." ":"] ["-" "-"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          makeFilter = f:
 | 
					          makeFilter = f:
 | 
				
			||||||
          let
 | 
					            "${ipK f.dst} daddr ${f.dst} ${f.proto} dport ${toString f.dstPort} accept";
 | 
				
			||||||
            v6 = isIPv6 f.dst;
 | 
					 | 
				
			||||||
          in
 | 
					 | 
				
			||||||
            "ip${optionalString v6 "6"} daddr ${f.dst} ${f.proto} dport ${toString f.dstPort} accept";
 | 
					 | 
				
			||||||
          makeForward = f:
 | 
					          makeForward = f:
 | 
				
			||||||
            let
 | 
					            "${f.proto} dport ${toString f.port} dnat ${ipK f.dst} to ${f.dst}:${toString f.dstPort}";
 | 
				
			||||||
              v6 = isIPv6 f.dst;
 | 
					 | 
				
			||||||
            in
 | 
					 | 
				
			||||||
              "${f.proto} dport ${toString f.port} dnat ip${optionalString v6 "6"} to ${f.dst}:${toString f.dstPort}";
 | 
					 | 
				
			||||||
        in
 | 
					        in
 | 
				
			||||||
        ''
 | 
					        ''
 | 
				
			||||||
          table inet filter {
 | 
					          table inet filter {
 | 
				
			||||||
            chain filter-port-forwards {
 | 
					            ${optionalString iifForward ''
 | 
				
			||||||
 | 
					              chain filter-iif-port-forwards {
 | 
				
			||||||
                ${concatMapStringsSep "\n    " makeFilter cfg.nat.forwardPorts}
 | 
					                ${concatMapStringsSep "\n    " makeFilter cfg.nat.forwardPorts}
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
 | 
					            ''}
 | 
				
			||||||
 | 
					            ${optionalString
 | 
				
			||||||
 | 
					              dipForward
 | 
				
			||||||
 | 
					              (concatStringsSep "\n" (mapAttrsToList (ip: fs: ''
 | 
				
			||||||
 | 
					                chain filter-fwd-${ipEscaped ip} {
 | 
				
			||||||
 | 
					                  ${concatMapStringsSep "\n    " makeFilter fs}
 | 
				
			||||||
 | 
					                  return
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              '') cfg.nat.forwardPorts))}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            chain forward {
 | 
					            chain forward {
 | 
				
			||||||
              ${optionalString
 | 
					              ${optionalString
 | 
				
			||||||
                (cfg.nat.externalInterface != null)
 | 
					                iifForward
 | 
				
			||||||
                "iifname ${cfg.nat.externalInterface} jump filter-port-forwards"}
 | 
					                "iifname ${cfg.nat.externalInterface} jump filter-iif-port-forwards"}
 | 
				
			||||||
 | 
					              ${optionalString
 | 
				
			||||||
 | 
					                dipForward
 | 
				
			||||||
 | 
					                (concatMapStringsSep "\n    " (ip: "${ipK ip} daddr ${ip} jump filter-fwd-${ipEscaped ip}") (attrNames cfg.nat.forwardPorts))}
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          table inet nat {
 | 
					          table inet nat {
 | 
				
			||||||
            chain port-forward {
 | 
					            ${optionalString iifForward ''
 | 
				
			||||||
 | 
					              chain iif-port-forward {
 | 
				
			||||||
                ${concatMapStringsSep "\n    " makeForward cfg.nat.forwardPorts}
 | 
					                ${concatMapStringsSep "\n    " makeForward cfg.nat.forwardPorts}
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
 | 
					            ''}
 | 
				
			||||||
 | 
					            ${optionalString
 | 
				
			||||||
 | 
					              dipForward
 | 
				
			||||||
 | 
					              (concatStringsSep "\n" (mapAttrsToList (ip: fs: ''
 | 
				
			||||||
 | 
					                chain fwd-${ipEscaped ip} {
 | 
				
			||||||
 | 
					                  ${concatMapStringsSep "\n    " makeForward fs}
 | 
				
			||||||
 | 
					                  return
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              '') cfg.nat.forwardPorts))}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            chain prerouting {
 | 
					            chain prerouting {
 | 
				
			||||||
              ${optionalString
 | 
					              ${optionalString
 | 
				
			||||||
                (cfg.nat.externalInterface != null)
 | 
					                iifForward
 | 
				
			||||||
                "${if (cfg.nat.externalIP != null) then "ip daddr ${cfg.nat.externalIP}" else "iifname ${cfg.nat.externalInterface}"} jump port-forward"}
 | 
					                "iifname ${cfg.nat.externalInterface} jump iif-port-forward"}
 | 
				
			||||||
 | 
					              ${optionalString
 | 
				
			||||||
 | 
					                dipForward
 | 
				
			||||||
 | 
					                (concatMapStringsSep "\n    " (ip: "${ipK ip} daddr ${ip} jump fwd-${ipEscaped ip}") (attrNames cfg.nat.forwardPorts))}
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        '';
 | 
					        '';
 | 
				
			||||||
    })
 | 
					    }))
 | 
				
			||||||
  ]);
 | 
					  ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  meta.buildDocsInSandbox = false;
 | 
					  meta.buildDocsInSandbox = false;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user