use proper options for the firewall & default to local build

This commit is contained in:
asyncnomi 2025-08-03 04:03:22 +02:00
parent 22f93f23ed
commit f5241119eb
8 changed files with 171 additions and 109 deletions

View file

@ -7,115 +7,150 @@ let
# Import mapping
mapping = import ./../../mapping.nix;
generatePortRules = protocol: ports: let
publicPorts = lib.filter (p: p.public) ports;
privatePorts = lib.filter (p: !p.public) ports;
publicRules = map (p: "${protocol} dport ${toString p.port} accept") publicPorts;
privateRules = map (p: "iifname mesh ${protocol} dport ${toString p.port} accept") privatePorts;
in
publicRules ++ privateRules;
cfg = config.fwtables;
in
{
networking = {
nat.enable = false;
firewall.enable = false;
nftables = {
enable = true;
checkRuleset = true;
flushRuleset = true;
tables = {
filter = {
family = "inet";
content = ''
chain preroute {
type filter hook prerouting priority -150; policy accept;
}
chain input {
type filter hook input priority 0; policy drop;
# Authorized already setup connection
ct state related,established accept
# Reject sus stuff
ct state invalid counter drop
tcp flags & (fin|syn|rst|ack) != syn ct state new counter drop
# Loopback
iif lo accept
# ICMP
icmp type { echo-request } limit rate 4/second accept
icmpv6 type { echo-request } limit rate 4/second accept
ip protocol icmp accept
icmpv6 type { nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept
# SSH
tcp dport 22 accept
# Mesh
udp dport 51820 accept
${if lib.elem myName mapping.bastion.hosts then ''
# Mgmt
udp dport 51920 accept
'' else ""}
${if myName == mapping.dns.master then ''
# DNS Master
iifname mesh tcp dport 53 accept
iifname mesh udp dport 53 accept
'' else ""}
${if lib.elem myName mapping.dns.secondary then ''
# DNS Secondary
tcp dport 53 accept
udp dport 53 accept
'' else ""}
${if lib.elem myName mapping.mail.hosts then ''
# Mail server (without IMAP)
# With support for both SSL & STARTTLS
tcp dport 25 accept
tcp dport 465 accept
tcp dport 587 accept
'' else ""}
${if lib.elem myName mapping.db.hosts then ''
# DNS Secondary
iifname mesh tcp dport 5432 accept
'' else ""}
# Log anything else
ip protocol tcp counter log prefix "tcp.in.dropped: "
ip protocol udp counter log prefix "udp.in.dropped: "
}
chain forward {
type filter hook forward priority 0; policy drop;
ct state related,established accept
ct state invalid counter drop
${if lib.elem myName mapping.bastion.hosts then ''
iifname mgmt oifname mesh accept
'' else ""}
}
chain output {
type filter hook output priority 0; policy accept;
}
chain postroute {
type filter hook postrouting priority 50; policy accept;
}
'';
options.fwtables = with lib; {
allowedTCPPorts = mkOption {
type = types.listOf (types.submodule {
options = {
port = mkOption {
type = types.port;
description = "The TCP port number";
};
public = mkOption {
type = types.bool;
default = false;
description = "Whether the port should be accessible from public internet (true) or only from mesh network (false)";
};
};
# We'll not nat the outgoing wg packet
# Instead custom routing is done on all
# other node to sent it back correclty
# That allow for custom ACL for the mgmt
# ip addresses
nat = {
family = "inet";
content = ''
chain prerouting {
type nat hook prerouting priority -100; policy accept;
}
chain postrouting {
type nat hook postrouting priority 100; policy accept;
}
'';
});
default = [];
description = "List of allowed TCP ports with their accessibility settings";
example = [
{ port = 80; public = true; }
{ port = 8080; public = false; }
];
};
allowedUDPPorts = mkOption {
type = types.listOf (types.submodule {
options = {
port = mkOption {
type = types.port;
description = "The UDP port number";
};
public = mkOption {
type = types.bool;
default = false;
description = "Whether the port should be accessible from public internet (true) or only from mesh network (false)";
};
};
});
default = [];
description = "List of allowed UDP ports with their accessibility settings";
example = [
{ port = 53; public = true; }
{ port = 1234; public = false; }
];
};
allowedMgmtFwdToMesh = mkOption {
type = types.bool;
default = false;
description = "Allow traffic to jump from mgmt if to mesh if";
};
};
config = {
networking = {
nat.enable = false;
firewall.enable = false;
nftables = {
enable = true;
checkRuleset = true;
flushRuleset = true;
tables = {
filter = {
family = "inet";
content = ''
chain preroute {
type filter hook prerouting priority -150; policy accept;
}
chain input {
type filter hook input priority 0; policy drop;
# Authorized already setup connection
ct state related,established accept
# Reject sus stuff
ct state invalid counter drop
tcp flags & (fin|syn|rst|ack) != syn ct state new counter drop
# Loopback
iif lo accept
# ICMP
icmp type { echo-request } limit rate 4/second accept
icmpv6 type { echo-request } limit rate 4/second accept
ip protocol icmp accept
icmpv6 type { nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept
# Temporary SSH access for convenience during early stages
tcp dport 22 accept
${lib.concatStringsSep "\n" (generatePortRules "tcp" cfg.allowedTCPPorts)}
${lib.concatStringsSep "\n" (generatePortRules "udp" cfg.allowedUDPPorts)}
# Log anything else
ip protocol tcp counter log prefix "tcp.in.dropped: "
ip protocol udp counter log prefix "udp.in.dropped: "
}
chain forward {
type filter hook forward priority 0; policy drop;
${lib.optionalString cfg.allowedMgmtFwdToMesh ''
ct state related,established accept
ct state invalid counter drop
iifname mgmt oifname mesh accept
''}
}
chain output {
type filter hook output priority 0; policy accept;
}
chain postroute {
type filter hook postrouting priority 50; policy accept;
}
'';
};
# We'll not nat the outgoing wg packet
# Instead custom routing is done on all
# other node to sent it back correclty
# That allow for custom ACL for the mgmt
# ip addresses
nat = {
family = "inet";
content = ''
chain prerouting {
type nat hook prerouting priority -100; policy accept;
}
chain postrouting {
type nat hook postrouting priority 100; policy accept;
}
'';
};
};
};
};