feat: add zoned nftables firewall

This commit is contained in:
oddlama 2023-04-21 01:27:43 +02:00
parent 703056a530
commit deab5b335e
No known key found for this signature in database
GPG key ID: 14EFE510775FE39A
10 changed files with 229 additions and 23 deletions

View file

@ -19,6 +19,20 @@
../../../modules/wireguard.nix
];
# IP address math library
# https://gist.github.com/duairc/5c9bb3c922e5d501a1edb9e7b3b845ba
# Plus some extensions by us
lib = let
libWithNet = (import "${inputs.lib-net}/net.nix" {inherit lib;}).lib;
in
lib.recursiveUpdate libWithNet {
net.cidr = rec {
hostCidr = n: x: "${libWithNet.net.cidr.host n x}/${libWithNet.net.cidr.length x}";
ip = x: lib.head (lib.splitString "/" x);
canonicalize = x: libWithNet.net.cidr.make (libWithNet.net.cidr.length x) (ip x);
};
};
# Setup secret rekeying parameters
rekey = {
inherit

View file

@ -1,4 +1,5 @@
{
config,
lib,
pkgs,
nodeName,
@ -8,6 +9,7 @@
inherit
(lib)
concatStringsSep
head
mapAttrsToList
mkDefault
mkForce
@ -17,10 +19,59 @@ in {
hostName = mkDefault nodeName;
useDHCP = mkForce false;
useNetworkd = true;
wireguard.enable = true;
dhcpcd.enable = false;
nftables.enable = true;
firewall.enable = true;
nftables = {
firewall.enable = true;
stopRuleset = mkDefault ''
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state invalid drop
ct state {established, related} accept
iifname lo accept
meta l4proto ipv6-icmp accept
meta l4proto icmp accept
tcp dport ${toString (head config.services.openssh.ports)} accept
}
chain forward {
type filter hook forward priority filter; policy drop;
}
chain output {
type filter hook output priority filter; policy accept;
}
}
'';
};
nftables.firewall = {
zones = lib.mkForce {
local.localZone = true;
};
rules = lib.mkForce {
icmp = {
early = true;
after = ["ct"];
from = "all";
to = ["local"];
extraLines = [
"ip6 nexthdr icmpv6 icmpv6 type { echo-request, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept"
"ip protocol icmp icmp type { echo-request, router-advertisement } accept"
#"ip6 saddr fe80::/10 ip6 daddr fe80::/10 udp dport 546 accept"
];
};
ssh = {
early = true;
after = ["ct"];
from = "all";
to = ["local"];
allowedTCPPorts = config.services.openssh.ports;
};
};
};
};
# Rename known network interfaces

View file

@ -1,10 +1,7 @@
{lib, ...}: {
networking = {
firewall = {
allowedTCPPorts = [5355];
allowedUDPPorts = [5353 5355];
};
networkmanager.dns = "systemd-resolved";
networking.firewall = {
allowedTCPPorts = [5355];
allowedUDPPorts = [5353 5355];
};
services.resolved = {
@ -16,7 +13,7 @@
"8.8.8.8"
"2001:4860:4860::8844"
];
llmnr = "true";
llmnr = "true"; # Microsoft's version of mDNS
extraConfig = ''
Domains=~.
MulticastDNS=true

View file

@ -3,7 +3,7 @@
system = "x86_64-linux";
microVmHost = true;
physicalConnections = {
"10-lan1" = "LAN 1";
"10-lan2" = "LAN 2";
"10-lan" = "LAN";
"10-wan" = "WAN";
};
}

View file

@ -1,27 +1,140 @@
{
config,
lib,
nodeSecrets,
...
}: {
networking.hostId = "49ce3b71";
}: let
inherit (config.lib.net) cidr;
net.lan.ipv4cidr = "192.168.100.1/24";
net.lan.ipv6cidr = "fd01::1/64";
in {
networking.hostId = nodeSecrets.networking.hostId;
systemd.network.networks = {
"10-lan1" = {
DHCP = "yes";
matchConfig.MACAddress = nodeSecrets.networking.interfaces.lan1.mac;
"10-lan" = {
address = [net.lan.ipv4cidr net.lan.ipv6cidr];
matchConfig.MACAddress = nodeSecrets.networking.interfaces.lan.mac;
networkConfig.IPv6PrivacyExtensions = "kernel";
dhcpV4Config.RouteMetric = 10;
dhcpV6Config.RouteMetric = 10;
};
"10-lan2" = {
"10-wan" = {
DHCP = "yes";
matchConfig.MACAddress = nodeSecrets.networking.interfaces.lan2.mac;
#address = [
# "192.168.178.2/24"
# "fd00::1/64"
#];
#gateway = [
#];
matchConfig.MACAddress = nodeSecrets.networking.interfaces.wan.mac;
networkConfig.IPv6PrivacyExtensions = "kernel";
dhcpV4Config.RouteMetric = 20;
dhcpV6Config.RouteMetric = 20;
};
};
networking.nftables.firewall = {
zones = lib.mkForce {
lan = {
interfaces = ["lan"];
ipv4Addresses = [(cidr.canonicalize net.lan.ipv4cidr)];
ipv6Addresses = [(cidr.canonicalize net.lan.ipv6cidr)];
};
wan = {
interfaces = ["wan"];
# TODO ipv4Addresses = [ net.wan.netv4 ];
# TODO ipv6Addresses = [ net.wan.netv6 ];
ipv4Addresses = ["192.168.1.0/22"];
ipv6Addresses = ["fd00::/64"];
};
};
rules = lib.mkForce {
masquerade-wan = {
from = ["lan"];
to = ["wan"];
masquerade = true;
};
outbound = {
from = ["lan"];
to = ["lan" "wan"];
late = true; # Only accept after any rejects have been processed
verdict = "accept";
};
wan-to-local = {
from = ["wan"];
to = ["local"];
};
lan-to-local = {
from = ["lan"];
to = ["local"];
inherit
(config.networking.firewall)
allowedTCPPorts
allowedUDPPorts
;
};
};
};
services.kea = {
dhcp4 = {
enable = true;
settings = {
lease-database = {
name = "/var/lib/kea/dhcp4.leases";
persist = true;
type = "memfile";
};
valid-lifetime = 4000;
renew-timer = 1000;
rebind-timer = 2000;
interfaces-config = {
interfaces = ["lan"];
service-sockets-max-retries = -1;
};
option-data = [
{
name = "domain-name-servers";
data = "1.1.1.1, 8.8.8.8";
}
];
subnet4 = [
{
interface = "lan";
subnet = cidr.canonicalize net.lan.ipv4cidr;
pools = [
{pool = "192.168.100.20 - 192.168.100.250";}
];
option-data = [
{
name = "routers";
data = cidr.ip net.lan.ipv4cidr;
}
];
#reservations = [
# {
# duid = "aa:bb:cc:dd:ee:ff";
# ip-address = cidr.ip net.lan.ipv4cidr;
# }
#];
}
];
};
};
#dhcp6 = {
# enable = true;
#};
};
systemd.services.kea-dhcp4-server.after = [
"sys-subsystem-net-devices-lan.device"
];
#extra.wireguard.vms = {
# server = {
# enable = true;

Binary file not shown.

View file

@ -1,4 +1,14 @@
{nodeSecrets, ...}: {
{
lib,
config,
nodeSecrets,
...
}: let
inherit (config.lib.net) cidr;
net.iot.ipv4cidr = "10.90.0.1/24";
net.iot.ipv6cidr = "fd90::1/64";
in {
networking.hostId = nodeSecrets.networking.hostId;
systemd.network.networks = {
@ -10,9 +20,27 @@
dhcpV6Config.RouteMetric = 10;
};
"10-wlan1" = {
DHCP = "no";
address = [net.iot.ipv4cidr net.iot.ipv6cidr];
matchConfig.MACAddress = nodeSecrets.networking.interfaces.wlan1.mac;
address = ["10.90.0.1/24"];
};
};
networking.nftables.firewall = {
zones = lib.mkForce {
lan.interfaces = ["lan1"];
};
rules = lib.mkForce {
int-to-local = {
from = ["lan"];
to = ["local"];
inherit
(config.networking.firewall)
allowedTCPPorts
allowedUDPPorts
;
};
};
};
}

View file

@ -24,6 +24,7 @@
mkIf
mkOption
mkEnableOption
net
optionalAttrs
optionals
splitString

View file

@ -26,7 +26,6 @@ in
secrets = self.secrets.content;
nodeSecrets = self.secrets.content.nodes.${nodeName};
nixos-hardware = nixos-hardware.nixosModules;
nixos-nftables-firewall = nixos-nftables-firewall.nixosModules;
#impermanence = impermanence.nixosModules;
};
imports =
@ -36,6 +35,7 @@ in
#impermanence.nixosModules.default
agenix.nixosModules.default
agenix-rekey.nixosModules.default
nixos-nftables-firewall.nixosModules.default
]
++ optionals nodeMeta.microVmHost [
microvm.nixosModules.host

View file

@ -15,11 +15,13 @@
flatten
foldl'
genAttrs
head
mapAttrs'
mergeAttrs
nameValuePair
partition
removeSuffix
splitString
substring
unique
;