2022-02-12 01:53:57 +00:00
|
|
|
{ lib, options, config, ... }:
|
2022-02-13 13:10:21 +00:00
|
|
|
let
|
2023-12-11 15:05:42 +00:00
|
|
|
inherit (builtins) typeOf attrNames;
|
2023-12-11 14:59:40 +00:00
|
|
|
inherit (lib)
|
|
|
|
optionalString concatStringsSep concatMapStringsSep mapAttrsToList optionalAttrs mkIf
|
|
|
|
mkDefault mkMerge mkOverride;
|
2023-05-14 01:12:42 +01:00
|
|
|
inherit (lib.my) isIPv6 mkOpt' mkBoolOpt';
|
2022-02-12 01:53:57 +00:00
|
|
|
|
2022-05-28 22:59:50 +01:00
|
|
|
allowICMP = ''
|
|
|
|
icmp type {
|
|
|
|
destination-unreachable,
|
|
|
|
router-solicitation,
|
|
|
|
router-advertisement,
|
|
|
|
time-exceeded,
|
|
|
|
parameter-problem,
|
|
|
|
echo-request
|
|
|
|
} accept
|
|
|
|
'';
|
|
|
|
allowICMP6 = ''
|
|
|
|
icmpv6 type {
|
|
|
|
destination-unreachable,
|
|
|
|
packet-too-big,
|
|
|
|
time-exceeded,
|
|
|
|
parameter-problem,
|
|
|
|
mld-listener-query,
|
|
|
|
mld-listener-report,
|
|
|
|
mld-listener-reduction,
|
|
|
|
nd-router-solicit,
|
|
|
|
nd-router-advert,
|
|
|
|
nd-neighbor-solicit,
|
|
|
|
nd-neighbor-advert,
|
|
|
|
ind-neighbor-solicit,
|
|
|
|
ind-neighbor-advert,
|
|
|
|
mld2-listener-report,
|
|
|
|
echo-request
|
|
|
|
} accept
|
|
|
|
'';
|
|
|
|
allowUDPTraceroute = ''
|
|
|
|
udp dport 33434-33625 accept
|
|
|
|
'';
|
|
|
|
|
2023-05-14 01:12:42 +01:00
|
|
|
forwardOpts = with lib.types; { config, ... }: {
|
2022-05-28 21:50:26 +01:00
|
|
|
options = {
|
|
|
|
proto = mkOpt' (enum [ "tcp" "udp" ]) "tcp" "Protocol.";
|
2022-05-28 22:59:50 +01:00
|
|
|
port = mkOpt' (either port str) null "Incoming port.";
|
2023-05-14 01:12:42 +01:00
|
|
|
dst = mkOpt' str null "Destination IP.";
|
|
|
|
dstPort = mkOpt' (either port str) config.port "Destination port.";
|
2022-05-28 21:50:26 +01:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2022-02-13 13:10:21 +00:00
|
|
|
cfg = config.my.firewall;
|
2022-03-26 14:20:30 +00:00
|
|
|
iptCfg = config.networking.firewall;
|
2022-02-13 13:10:21 +00:00
|
|
|
in
|
|
|
|
{
|
|
|
|
options.my.firewall = with lib.types; {
|
2022-02-13 17:44:14 +00:00
|
|
|
enable = mkBoolOpt' true "Whether to enable the nftables-based firewall.";
|
2022-02-13 13:10:21 +00:00
|
|
|
trustedInterfaces = options.networking.firewall.trustedInterfaces;
|
|
|
|
tcp = {
|
2022-02-17 17:08:25 +00:00
|
|
|
allowed = mkOpt' (listOf (either port str)) [ ] "TCP ports to open.";
|
2022-02-13 13:10:21 +00:00
|
|
|
};
|
|
|
|
udp = {
|
2022-02-13 17:44:14 +00:00
|
|
|
allowed = mkOpt' (listOf (either port str)) [ ] "UDP ports to open.";
|
2022-05-28 22:59:50 +01:00
|
|
|
allowTraceroute = mkBoolOpt' true "Whethor or not to add a rule to accept UDP traceroute packets.";
|
2022-02-13 13:10:21 +00:00
|
|
|
};
|
2022-02-13 17:44:14 +00:00
|
|
|
extraRules = mkOpt' lines "" "Arbitrary additional nftables rules.";
|
2022-02-12 01:53:57 +00:00
|
|
|
|
2022-02-13 13:10:21 +00:00
|
|
|
nat = with options.networking.nat; {
|
2022-02-13 17:44:14 +00:00
|
|
|
enable = mkBoolOpt' true "Whether to enable IP forwarding and NAT.";
|
2023-12-11 14:59:40 +00:00
|
|
|
inherit externalInterface;
|
|
|
|
forwardPorts = mkOpt' (either (listOf (submodule forwardOpts)) (attrsOf (listOf (submodule forwardOpts)))) [ ] "IPv4 port forwards";
|
2022-02-12 01:53:57 +00:00
|
|
|
};
|
2022-02-13 13:10:21 +00:00
|
|
|
};
|
2022-02-12 01:53:57 +00:00
|
|
|
|
2022-02-13 13:10:21 +00:00
|
|
|
config = mkIf cfg.enable (mkMerge [
|
|
|
|
{
|
|
|
|
networking = {
|
|
|
|
firewall.enable = false;
|
|
|
|
nftables = {
|
|
|
|
enable = true;
|
|
|
|
ruleset =
|
|
|
|
let
|
2022-03-26 14:20:30 +00:00
|
|
|
trusted' = "{ ${concatStringsSep ", " (cfg.trustedInterfaces ++ iptCfg.trustedInterfaces)} }";
|
|
|
|
openTCP = cfg.tcp.allowed ++ iptCfg.allowedTCPPorts;
|
|
|
|
openUDP = cfg.udp.allowed ++ iptCfg.allowedUDPPorts;
|
2022-02-13 13:10:21 +00:00
|
|
|
in
|
|
|
|
''
|
|
|
|
table inet filter {
|
|
|
|
chain wan-tcp {
|
2022-02-17 17:08:25 +00:00
|
|
|
${concatMapStringsSep "\n " (p: "tcp dport ${toString p} accept") openTCP}
|
2022-05-31 21:25:51 +01:00
|
|
|
return
|
2022-02-13 13:10:21 +00:00
|
|
|
}
|
|
|
|
chain wan-udp {
|
2022-02-17 17:08:25 +00:00
|
|
|
${concatMapStringsSep "\n " (p: "udp dport ${toString p} accept") openUDP}
|
2022-05-31 21:25:51 +01:00
|
|
|
return
|
2022-02-13 13:10:21 +00:00
|
|
|
}
|
2022-02-12 01:53:57 +00:00
|
|
|
|
2022-02-13 13:10:21 +00:00
|
|
|
chain wan {
|
2022-05-28 22:59:50 +01:00
|
|
|
${allowICMP}
|
2022-05-28 21:50:26 +01:00
|
|
|
ip protocol igmp accept
|
2022-05-28 22:59:50 +01:00
|
|
|
${allowICMP6}
|
|
|
|
${allowUDPTraceroute}
|
|
|
|
|
|
|
|
tcp flags & (fin|syn|rst|ack) == syn ct state new jump wan-tcp
|
|
|
|
meta l4proto udp ct state new jump wan-udp
|
|
|
|
|
|
|
|
return
|
2022-02-13 13:10:21 +00:00
|
|
|
}
|
2022-02-12 01:53:57 +00:00
|
|
|
|
2022-02-13 13:10:21 +00:00
|
|
|
chain input {
|
|
|
|
type filter hook input priority 0; policy drop;
|
2022-02-12 01:53:57 +00:00
|
|
|
|
2022-02-13 13:10:21 +00:00
|
|
|
ct state established,related accept
|
|
|
|
ct state invalid drop
|
2022-02-12 01:53:57 +00:00
|
|
|
|
2022-02-13 13:10:21 +00:00
|
|
|
iif lo accept
|
|
|
|
${optionalString (cfg.trustedInterfaces != []) "iifname ${trusted'} accept\n"}
|
|
|
|
jump wan
|
2022-02-12 01:53:57 +00:00
|
|
|
}
|
2022-02-13 13:10:21 +00:00
|
|
|
chain forward {
|
|
|
|
type filter hook forward priority 0; policy drop;
|
|
|
|
${optionalString (cfg.trustedInterfaces != []) "\n iifname ${trusted'} accept\n"}
|
|
|
|
ct state related,established accept
|
2022-05-28 22:59:50 +01:00
|
|
|
|
|
|
|
${allowICMP}
|
|
|
|
${allowICMP6}
|
|
|
|
${allowUDPTraceroute}
|
2022-02-13 13:10:21 +00:00
|
|
|
}
|
|
|
|
chain output {
|
|
|
|
type filter hook output priority 0; policy accept;
|
|
|
|
}
|
|
|
|
}
|
2022-02-12 01:53:57 +00:00
|
|
|
|
2022-05-28 21:50:26 +01:00
|
|
|
table inet nat {
|
2022-02-13 13:10:21 +00:00
|
|
|
chain prerouting {
|
2022-06-06 00:18:24 +01:00
|
|
|
type nat hook prerouting priority dstnat;
|
2022-02-13 13:10:21 +00:00
|
|
|
}
|
2023-12-09 16:30:06 +00:00
|
|
|
chain output {
|
|
|
|
type nat hook output priority dstnat;
|
|
|
|
}
|
2022-02-13 13:10:21 +00:00
|
|
|
chain postrouting {
|
2022-06-06 00:18:24 +01:00
|
|
|
type nat hook postrouting priority srcnat;
|
2022-02-12 01:53:57 +00:00
|
|
|
}
|
2023-12-11 15:05:42 +00:00
|
|
|
chain input {
|
|
|
|
type nat hook input priority srcnat;
|
|
|
|
}
|
2022-02-13 13:10:21 +00:00
|
|
|
}
|
2022-02-12 01:53:57 +00:00
|
|
|
|
2022-02-13 13:10:21 +00:00
|
|
|
${cfg.extraRules}
|
|
|
|
'';
|
2022-02-12 01:53:57 +00:00
|
|
|
};
|
2022-02-13 13:10:21 +00:00
|
|
|
};
|
|
|
|
}
|
2023-12-11 14:59:40 +00:00
|
|
|
(mkIf cfg.nat.enable (
|
|
|
|
let
|
|
|
|
iifForward = typeOf cfg.nat.forwardPorts == "list" && cfg.nat.forwardPorts != [ ];
|
|
|
|
dipForward = typeOf cfg.nat.forwardPorts == "set" && cfg.nat.forwardPorts != { };
|
|
|
|
in
|
|
|
|
{
|
2022-02-13 13:10:21 +00:00
|
|
|
assertions = [
|
|
|
|
{
|
2023-12-11 14:59:40 +00:00
|
|
|
assertion = with cfg.nat; iifForward -> (externalInterface != null);
|
|
|
|
message = "my.firewall.nat.forwardPorts as list requires my.firewall.nat.externalInterface";
|
2022-02-13 13:10:21 +00:00
|
|
|
}
|
|
|
|
];
|
2022-02-12 01:53:57 +00:00
|
|
|
|
2022-02-13 13:10:21 +00:00
|
|
|
# Yoinked from nixpkgs/nixos/modules/services/networking/nat.nix
|
|
|
|
boot = {
|
|
|
|
kernel.sysctl = {
|
|
|
|
"net.ipv4.conf.all.forwarding" = mkOverride 99 true;
|
|
|
|
"net.ipv4.conf.default.forwarding" = mkOverride 99 true;
|
|
|
|
} // optionalAttrs config.networking.enableIPv6 {
|
|
|
|
# Do not prevent IPv6 autoconfiguration.
|
|
|
|
# See <http://strugglers.net/~andy/blog/2011/09/04/linux-ipv6-router-advertisements-and-forwarding/>.
|
|
|
|
"net.ipv6.conf.all.accept_ra" = mkOverride 99 2;
|
|
|
|
"net.ipv6.conf.default.accept_ra" = mkOverride 99 2;
|
2022-02-12 01:53:57 +00:00
|
|
|
|
2022-02-13 13:10:21 +00:00
|
|
|
# Forward IPv6 packets.
|
|
|
|
"net.ipv6.conf.all.forwarding" = mkOverride 99 true;
|
|
|
|
"net.ipv6.conf.default.forwarding" = mkOverride 99 true;
|
2022-02-12 01:53:57 +00:00
|
|
|
};
|
2022-02-13 13:10:21 +00:00
|
|
|
};
|
2022-02-12 01:53:57 +00:00
|
|
|
|
2022-02-13 13:10:21 +00:00
|
|
|
my.firewall.extraRules =
|
|
|
|
let
|
2023-12-11 15:05:42 +00:00
|
|
|
inherit (lib.my.nft) natFilterChain dnatChain;
|
2023-12-11 14:59:40 +00:00
|
|
|
ipK = ip: "ip${optionalString (isIPv6 ip) "6"}";
|
|
|
|
|
2022-02-13 13:10:21 +00:00
|
|
|
makeFilter = f:
|
2023-12-11 14:59:40 +00:00
|
|
|
"${ipK f.dst} daddr ${f.dst} ${f.proto} dport ${toString f.dstPort} accept";
|
2022-05-28 21:50:26 +01:00
|
|
|
makeForward = f:
|
2023-12-11 14:59:40 +00:00
|
|
|
"${f.proto} dport ${toString f.port} dnat ${ipK f.dst} to ${f.dst}:${toString f.dstPort}";
|
2023-12-11 15:05:42 +00:00
|
|
|
|
|
|
|
dnatJumps = ''
|
|
|
|
${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))}
|
|
|
|
'';
|
2022-02-13 13:10:21 +00:00
|
|
|
in
|
|
|
|
''
|
|
|
|
table inet filter {
|
2023-12-11 14:59:40 +00:00
|
|
|
${optionalString iifForward ''
|
|
|
|
chain filter-iif-port-forwards {
|
|
|
|
${concatMapStringsSep "\n " makeFilter cfg.nat.forwardPorts}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
''}
|
|
|
|
${optionalString
|
|
|
|
dipForward
|
|
|
|
(concatStringsSep "\n" (mapAttrsToList (ip: fs: ''
|
2023-12-11 15:05:42 +00:00
|
|
|
chain ${natFilterChain ip} {
|
2023-12-11 14:59:40 +00:00
|
|
|
${concatMapStringsSep "\n " makeFilter fs}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
'') cfg.nat.forwardPorts))}
|
|
|
|
|
2022-02-13 13:10:21 +00:00
|
|
|
chain forward {
|
2022-02-17 15:47:24 +00:00
|
|
|
${optionalString
|
2023-12-11 14:59:40 +00:00
|
|
|
iifForward
|
|
|
|
"iifname ${cfg.nat.externalInterface} jump filter-iif-port-forwards"}
|
|
|
|
${optionalString
|
|
|
|
dipForward
|
2024-01-01 16:28:04 +00:00
|
|
|
(concatMapStringsSep "\n " (ip: "jump ${natFilterChain ip}") (attrNames cfg.nat.forwardPorts))}
|
2022-02-13 13:10:21 +00:00
|
|
|
}
|
|
|
|
}
|
2022-02-12 01:53:57 +00:00
|
|
|
|
2022-05-28 21:50:26 +01:00
|
|
|
table inet nat {
|
2023-12-11 14:59:40 +00:00
|
|
|
${optionalString iifForward ''
|
|
|
|
chain iif-port-forward {
|
|
|
|
${concatMapStringsSep "\n " makeForward cfg.nat.forwardPorts}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
''}
|
|
|
|
${optionalString
|
|
|
|
dipForward
|
|
|
|
(concatStringsSep "\n" (mapAttrsToList (ip: fs: ''
|
2023-12-11 15:05:42 +00:00
|
|
|
chain ${dnatChain ip} {
|
2023-12-11 14:59:40 +00:00
|
|
|
${concatMapStringsSep "\n " makeForward fs}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
'') cfg.nat.forwardPorts))}
|
|
|
|
|
2022-02-13 13:10:21 +00:00
|
|
|
chain prerouting {
|
2023-12-11 15:05:42 +00:00
|
|
|
${dnatJumps}
|
|
|
|
}
|
|
|
|
chain output {
|
|
|
|
${dnatJumps}
|
2022-02-12 01:53:57 +00:00
|
|
|
}
|
2022-02-13 13:10:21 +00:00
|
|
|
}
|
|
|
|
'';
|
2023-12-11 14:59:40 +00:00
|
|
|
}))
|
2022-02-13 13:10:21 +00:00
|
|
|
]);
|
2022-02-15 20:50:27 +00:00
|
|
|
|
|
|
|
meta.buildDocsInSandbox = false;
|
2022-02-13 13:10:21 +00:00
|
|
|
}
|