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

@ -70,7 +70,7 @@
user = "root"; user = "root";
autoRollback = true; autoRollback = true;
magicRollback = true; magicRollback = true;
remoteBuild = true; remoteBuild = false;
nodes = lib.mapAttrs (name: config: { nodes = lib.mapAttrs (name: config: {
hostname = lib.lists.head (lib.strings.splitString "/" config.ip4); hostname = lib.lists.head (lib.strings.splitString "/" config.ip4);
profilesOrder = [ "system" ]; profilesOrder = [ "system" ];

View file

@ -52,4 +52,7 @@ in
]; ];
}; };
}; };
fwtables.allowedMgmtFwdToMesh = true;
fwtables.allowedUDPPorts = [{ port = 51920; public = true; }];
} }

View file

@ -11,6 +11,8 @@ let
# And mappings # And mappings
mapping = import ./../../mapping.nix; mapping = import ./../../mapping.nix;
meshPort = 51820;
buildSecret = zone: id: { buildSecret = zone: id: {
"wg-private-zone-${toString zone}-id-${toString id}" = { "wg-private-zone-${toString zone}-id-${toString id}" = {
file = ./../../secrets/wireguard + ( "/wg-private-zone-" + toString zone + "-id-" + toString id + ".age" ); file = ./../../secrets/wireguard + ( "/wg-private-zone-" + toString zone + "-id-" + toString id + ".age" );
@ -32,13 +34,13 @@ let
"172.19.${toString (peerConfig.zone + 127)}.0/24" "172.19.${toString (peerConfig.zone + 127)}.0/24"
"fc00:f::${toString (peerConfig.zone + 127)}:0/112" "fc00:f::${toString (peerConfig.zone + 127)}:0/112"
]; ];
Endpoint = "${lib.lists.head (lib.strings.splitString "/" peerConfig.ip4)}:51820"; Endpoint = "${lib.lists.head (lib.strings.splitString "/" peerConfig.ip4)}:${toString meshPort}";
PersistentKeepalive = 25; PersistentKeepalive = 25;
}) peerConfigs; }) peerConfigs;
interfaceConfig = { interfaceConfig = {
PrivateKeyFile = config.age.secrets."wg-private-zone-${toString myZone}-id-${toString myId}".path; PrivateKeyFile = config.age.secrets."wg-private-zone-${toString myZone}-id-${toString myId}".path;
ListenPort = 51820; ListenPort = meshPort;
}; };
# Return route for mgmt traffic # Return route for mgmt traffic
@ -76,4 +78,6 @@ in
routes = rtwg4 ++ rtwg6; routes = rtwg4 ++ rtwg6;
}; };
}; };
fwtables.allowedUDPPorts = [{ port = meshPort; public = true; }];
} }

View file

@ -7,115 +7,150 @@ let
# Import mapping # Import mapping
mapping = import ./../../mapping.nix; 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 in
{ {
networking = { options.fwtables = with lib; {
nat.enable = false; allowedTCPPorts = mkOption {
firewall.enable = false; type = types.listOf (types.submodule {
nftables = { options = {
enable = true; port = mkOption {
checkRuleset = true; type = types.port;
flushRuleset = true; description = "The TCP port number";
tables = { };
filter = { public = mkOption {
family = "inet"; type = types.bool;
content = '' default = false;
chain preroute { description = "Whether the port should be accessible from public internet (true) or only from mesh network (false)";
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;
}
'';
}; };
# We'll not nat the outgoing wg packet });
# Instead custom routing is done on all default = [];
# other node to sent it back correclty description = "List of allowed TCP ports with their accessibility settings";
# That allow for custom ACL for the mgmt example = [
# ip addresses { port = 80; public = true; }
nat = { { port = 8080; public = false; }
family = "inet"; ];
content = '' };
chain prerouting {
type nat hook prerouting priority -100; policy accept; allowedUDPPorts = mkOption {
} type = types.listOf (types.submodule {
chain postrouting { options = {
type nat hook postrouting priority 100; policy accept; 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;
}
'';
};
}; };
}; };
}; };

View file

@ -84,4 +84,8 @@ in
echo "PostgreSQL dataDir not empty, skipping initial master to slave replication" echo "PostgreSQL dataDir not empty, skipping initial master to slave replication"
fi fi
''); '');
fwtables.allowedTCPPorts = [
{ port = cfg.settings.port; public = false; }
];
} }

View file

@ -285,4 +285,7 @@ in
]; ];
}; };
}; };
fwtables.allowedTCPPorts = [{ port = 53; public = true; }];
fwtables.allowedUDPPorts = [{ port = 53; public = true; }];
} }

View file

@ -223,4 +223,10 @@ in
}; };
}; };
}; };
fwtables.allowedTCPPorts = [
{ port = 25; public = true; }
{ port = 465; public = true; }
{ port = 587; public = true; }
];
} }

View file

@ -3,6 +3,13 @@
{ {
# Users # Users
# Uid 1000 - 1999 are reserved for specific system users that need uid > 999 # Uid 1000 - 1999 are reserved for specific system users that need uid > 999
nix.settings.trusted-users = [
"asyncnomi"
"gamma"
"jeltz"
"soyouzpanda"
"raito"
];
# Wheeler # Wheeler
users.users.asyncnomi = { users.users.asyncnomi = {