Compare commits

...

3 Commits

Author SHA1 Message Date
5686aa1a01 nixos/shill: Replicate port forwards for internal routing
All checks were successful
CI / Check, build and cache Nix flake (push) Successful in 17m23s
2023-12-11 16:53:09 +00:00
20a3873d25 nixos/colony: Replicate port forwards for internal routing 2023-12-11 16:51:43 +00:00
d9d7a714cd nixos/firewall: Add ability to forward per external IP 2023-12-11 14:59:40 +00:00
6 changed files with 123 additions and 59 deletions

View File

@ -114,6 +114,33 @@ rec {
}; };
home.v6 = "2a0e:97c0:4d0::/48"; home.v6 = "2a0e:97c0:4d0::/48";
}; };
firewallForwards = aa: [
{
port = "http";
dst = aa.middleman.internal.ipv4.address;
}
{
port = "https";
dst = aa.middleman.internal.ipv4.address;
}
{
port = 8448;
dst = aa.middleman.internal.ipv4.address;
}
{
port = 2456;
dst = aa.valheim-oci.internal.ipv4.address;
proto = "udp";
}
{
port = 2457;
dst = aa.valheim-oci.internal.ipv4.address;
proto = "udp";
}
];
fstrimConfig = { fstrimConfig = {
enable = true; enable = true;
# backup happens at 05:00 # backup happens at 05:00

View File

@ -1,6 +1,6 @@
{ lib }: { lib }:
let let
inherit (builtins) length match elemAt filter; inherit (builtins) length match elemAt filter replaceStrings;
inherit (lib) inherit (lib)
genAttrs mapAttrsToList filterAttrsRecursive nameValuePair types genAttrs mapAttrsToList filterAttrsRecursive nameValuePair types
mkOption mkOverride mkForce mkIf mergeEqualOption optional mkOption mkOverride mkForce mkIf mergeEqualOption optional
@ -123,6 +123,12 @@ rec {
home-manager = mkOpt' (enum [ "unstable" "stable" "mine" "mine-stable" ]) "unstable" "Branch of home-manager to use."; home-manager = mkOpt' (enum [ "unstable" "stable" "mine" "mine-stable" ]) "unstable" "Branch of home-manager to use.";
}; };
nft = rec {
ipEscape = replaceStrings ["." ":"] ["-" "-"];
natFilterChain = ip: "filter-fwd-${ipEscape ip}";
dnatChain = ip: "fwd-${ipEscape ip}";
};
mkVLAN = name: vid: { mkVLAN = name: vid: {
"25-${name}" = { "25-${name}" = {
netdevConfig = { netdevConfig = {

View File

@ -1,7 +1,7 @@
{ lib, ... }: { lib, ... }:
let let
inherit (lib.my) net; inherit (lib.my) net;
inherit (lib.my.c.colony) domain prefixes; inherit (lib.my.c.colony) domain prefixes firewallForwards;
in in
{ {
imports = [ ./vms ]; imports = [ ./vms ];
@ -351,6 +351,7 @@ in
firewall = { firewall = {
trustedInterfaces = [ "vms" ]; trustedInterfaces = [ "vms" ];
nat.forwardPorts."${allAssignments.estuary.internal.ipv4.address}" = firewallForwards allAssignments;
extraRules = '' extraRules = ''
define cust = { vm-mail, vm-darts } define cust = { vm-mail, vm-darts }
table inet filter { table inet filter {

View File

@ -2,7 +2,7 @@
let let
inherit (builtins) elemAt; inherit (builtins) elemAt;
inherit (lib.my) net mkVLAN; inherit (lib.my) net mkVLAN;
inherit (lib.my.c.colony) pubV4 domain prefixes; inherit (lib.my.c.colony) pubV4 domain prefixes firewallForwards;
in in
{ {
nixos = { nixos = {
@ -356,32 +356,7 @@ in
nat = { nat = {
enable = true; enable = true;
externalInterface = "wan"; externalInterface = "wan";
externalIP = assignments.internal.ipv4.address; forwardPorts."${assignments.internal.ipv4.address}" = firewallForwards allAssignments;
forwardPorts = [
{
port = "http";
dst = allAssignments.middleman.internal.ipv4.address;
}
{
port = "https";
dst = allAssignments.middleman.internal.ipv4.address;
}
{
port = 8448;
dst = allAssignments.middleman.internal.ipv4.address;
}
{
port = 2456;
dst = allAssignments.valheim-oci.internal.ipv4.address;
proto = "udp";
}
{
port = 2457;
dst = allAssignments.valheim-oci.internal.ipv4.address;
proto = "udp";
}
];
}; };
extraRules = extraRules =
let let

View File

@ -1,7 +1,7 @@
{ lib, ... }: { lib, ... }:
let let
inherit (lib.my) net; inherit (lib.my) net nft;
inherit (lib.my.c.colony) domain prefixes; inherit (lib.my.c.colony) domain prefixes firewallForwards;
in in
{ {
imports = [ ./containers ]; imports = [ ./containers ];
@ -151,6 +151,7 @@ in
firewall = { firewall = {
tcp.allowed = [ 19999 ]; tcp.allowed = [ 19999 ];
trustedInterfaces = [ "ctrs" ]; trustedInterfaces = [ "ctrs" ];
nat.forwardPorts."${allAssignments.estuary.internal.ipv4.address}" = firewallForwards allAssignments;
extraRules = '' extraRules = ''
table inet filter { table inet filter {
chain forward { chain forward {
@ -158,6 +159,17 @@ in
iifname vms oifname ctrs accept iifname vms oifname ctrs accept
} }
} }
table inet nat {
# Hack to fix our NAT situation with internal routing
# We need to snat to our public IP, otherwise on the return path from e.g. middleman it will
# try to forward packet directly with its own IP, bypassing our carefully crafted DNAT...
chain ${nft.dnatChain allAssignments.estuary.internal.ipv4.address} {
ct mark set 0x1337
}
chain postrouting {
ct mark 0x1337 snat ip to ${assignments.internal.ipv4.address}
}
}
''; '';
}; };

View File

@ -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 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";
}; };
}; };
@ -137,6 +140,9 @@ in
chain postrouting { chain postrouting {
type nat hook postrouting priority srcnat; type nat hook postrouting priority srcnat;
} }
chain input {
type nat hook input priority srcnat;
}
} }
${cfg.extraRules} ${cfg.extraRules}
@ -144,11 +150,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 +182,75 @@ in
my.firewall.extraRules = my.firewall.extraRules =
let let
inherit (lib.my.nft) natFilterChain dnatChain;
ipK = ip: "ip${optionalString (isIPv6 ip) "6"}";
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 dnatJumps = ''
"${f.proto} dport ${toString f.port} dnat ip${optionalString v6 "6"} to ${f.dst}:${toString f.dstPort}"; ${optionalString
iifForward
"iifname ${cfg.nat.externalInterface} jump iif-port-forward"}
${optionalString
dipForward
(concatMapStringsSep "\n " (ip: "${ipK ip} daddr ${ip} jump ${dnatChain ip}") (attrNames cfg.nat.forwardPorts))}
'';
in in
'' ''
table inet filter { table inet filter {
chain filter-port-forwards { ${optionalString iifForward ''
${concatMapStringsSep "\n " makeFilter cfg.nat.forwardPorts} chain filter-iif-port-forwards {
return ${concatMapStringsSep "\n " makeFilter cfg.nat.forwardPorts}
} return
}
''}
${optionalString
dipForward
(concatStringsSep "\n" (mapAttrsToList (ip: fs: ''
chain ${natFilterChain 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 ${natFilterChain ip}") (attrNames cfg.nat.forwardPorts))}
} }
} }
table inet nat { table inet nat {
chain port-forward { ${optionalString iifForward ''
${concatMapStringsSep "\n " makeForward cfg.nat.forwardPorts} chain iif-port-forward {
return ${concatMapStringsSep "\n " makeForward cfg.nat.forwardPorts}
} return
}
''}
${optionalString
dipForward
(concatStringsSep "\n" (mapAttrsToList (ip: fs: ''
chain ${dnatChain ip} {
${concatMapStringsSep "\n " makeForward fs}
return
}
'') cfg.nat.forwardPorts))}
chain prerouting { chain prerouting {
${optionalString ${dnatJumps}
(cfg.nat.externalInterface != null) }
"${if (cfg.nat.externalIP != null) then "ip daddr ${cfg.nat.externalIP}" else "iifname ${cfg.nat.externalInterface}"} jump port-forward"} chain output {
${dnatJumps}
} }
} }
''; '';
}) }))
]); ]);
meta.buildDocsInSandbox = false; meta.buildDocsInSandbox = false;