chore: wip router firewall options
Some checks failed
/ check (push) Failing after 10s

This commit is contained in:
Daniel Flanagan 2025-02-18 22:37:45 -06:00
parent 48caa98989
commit f0c5293adf
3 changed files with 479 additions and 448 deletions

View file

@ -18,6 +18,7 @@ in
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
services.xserver.desktopManager.gnome.enable = true; services.xserver.desktopManager.gnome.enable = true;
environment.systemPackages = [ pkgs.wl-clipboard ];
fonts.packages = [ fonts.packages = [
( (

View file

@ -22,45 +22,8 @@ in
example = "lan"; example = "lan";
}; };
/* openPorts = lib.mkOption { };
hosts = { hosts = lib.mkOption { };
dragon = {
ip = "192.168.0.10";
};
bald = {
ip = "192.168.0.11";
additionalHosts = [
"ourcraft.lyte.dev"
];
};
beefcake = {
ip = "192.168.0.9";
additionalHosts = [
".beefcake.lan"
"a.lyte.dev"
"atuin.h.lyte.dev"
"audio.lyte.dev"
"bw.lyte.dev"
"files.lyte.dev"
"finances.h.lyte.dev"
"git.lyte.dev"
"grafana.h.lyte.dev"
"idm.h.lyte.dev"
"matrix.lyte.dev"
"nextcloud.h.lyte.dev"
"nix.h.lyte.dev"
"onlyoffice.h.lyte.dev"
"paperless.h.lyte.dev"
"prometheus.h.lyte.dev"
"video.lyte.dev"
"vpn.h.lyte.dev"
];
};
};
*/
hosts = lib.mkOption {
};
interfaces = { interfaces = {
wan = { wan = {
@ -164,30 +127,75 @@ in
ff02::2 ip6-allrouters ff02::2 ip6-allrouters
''; '';
nftables = { # tcp dport 2201 accept comment "Accept SSH on port 2201"
# tcp dport 53 accept comment "Accept DNS"
# udp dport 53 accept comment "Accept DNS"
# tcp dport { 80, 443 } accept comment "Allow HTTP/HTTPS to server (see nat prerouting)"
# udp dport { 80, 443 } accept comment "Allow QUIC to server (see nat prerouting)"
nftables =
let
mkOpenPortRule =
protocol: rules:
builtins.concatStringsSep "\n " (
lib.attrsets.mapAttrsToList (
name: ports:
let
nfports = builtins.concatStringsSep ", " (lib.lists.toList (builtins.toString ports));
in
''${protocol} dport {${nfports}} accept comment "${name}"''
) rules
);
tcpRulesString = mkOpenPortRule "tcp" cfg.openPorts.tcp;
udpRulesString = mkOpenPortRule "udp" cfg.openPorts.udp;
tcpHostRulesString = mkOpenPortRule "tcp" cfg.hosts.all.nat;
udpHostRulesString = mkOpenPortRule "udp" cfg.hosts.all.nat;
builtins.mapAttrs (name: { nat ? {} }: nat) cfg.hosts
acceptPorts = builtins.concatStringsSep "\n " [
tcpRulesString
udpRulesString
tcpHostRulesString
udpHostRulesString
];
in
{
enable = true; enable = true;
checkRuleset = true; checkRuleset = true;
flushRuleset = true; flushRuleset = true;
/*
set LANv4 {
type ipv4_addr
flags interval
elements = { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 }
}
set LANv6 {
type ipv6_addr
flags interval
elements = { fd00::/8, fe80::/10 }
}
TODO: maybe tailnet?
chain my_input_lan {
udp sport 1900 udp dport >= 1024 meta pkttype unicast limit rate 4/second burst 20 packets accept comment "Accept UPnP IGD port mapping reply"
udp sport netbios-ns udp dport >= 1024 meta pkttype unicast accept comment "Accept Samba Workgroup browsing replies"
}
chain forward {
type filter hook forward priority filter; policy drop;
iifname { "${lan}" } oifname { "${wan}" } accept comment "Allow trusted LAN to WAN"
iifname { "tailscale0" } oifname { "${wan}" } accept comment "Allow trusted LAN to WAN"
iifname { "${wan}" } oifname { "${lan}" } ct state { established, related } accept comment "Allow established back to LAN"
}
*/
ruleset = '' ruleset = ''
table inet filter { table inet filter {
## set LANv4 {
## type ipv4_addr
## flags interval
## elements = { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 }
## }
## set LANv6 {
## type ipv6_addr
## flags interval
## elements = { fd00::/8, fe80::/10 }
## }
## TODO: maybe tailnet?
## chain my_input_lan {
## udp sport 1900 udp dport >= 1024 meta pkttype unicast limit rate 4/second burst 20 packets accept comment "Accept UPnP IGD port mapping reply"
## udp sport netbios-ns udp dport >= 1024 meta pkttype unicast accept comment "Accept Samba Workgroup browsing replies"
## }
chain input { chain input {
type filter hook input priority 0; policy drop; type filter hook input priority 0; policy drop;
@ -213,6 +221,8 @@ in
udp dport mdns ip6 daddr ff02::fb accept comment "Accept mDNS" udp dport mdns ip6 daddr ff02::fb accept comment "Accept mDNS"
udp dport mdns ip daddr 224.0.0.251 accept comment "Accept mDNS" udp dport mdns ip daddr 224.0.0.251 accept comment "Accept mDNS"
${acceptPorts}
tcp dport 2201 accept comment "Accept SSH on port 2201" tcp dport 2201 accept comment "Accept SSH on port 2201"
tcp dport 53 accept comment "Accept DNS" tcp dport 53 accept comment "Accept DNS"
udp dport 53 accept comment "Accept DNS" udp dport 53 accept comment "Accept DNS"
@ -241,14 +251,6 @@ in
type filter hook forward priority 0; type filter hook forward priority 0;
accept accept
} }
## chain forward {
## type filter hook forward priority filter; policy drop;
## iifname { "${lan}" } oifname { "${wan}" } accept comment "Allow trusted LAN to WAN"
## iifname { "tailscale0" } oifname { "${wan}" } accept comment "Allow trusted LAN to WAN"
## iifname { "${wan}" } oifname { "${lan}" } ct state { established, related } accept comment "Allow established back to LAN"
## }
} }
table ip nat { table ip nat {
@ -426,8 +428,9 @@ in
{ {
ip, ip,
additionalHosts ? [ ], additionalHosts ? [ ],
identifier ? name, # identifier ? name,
time ? "12h", # time ? "12h",
...
}: }:
[ [
"/${name}.${cfg.domain}/${ip}" "/${name}.${cfg.domain}/${ip}"
@ -447,4 +450,319 @@ in
}; };
} }
); );
# NOTE: see flake.nix 'nnf.nixosModules.default'
/*
nftables.firewall = let
me = config.networking.nftables.firewall.localZoneName;
in {
enable = true;
snippets.nnf-common.enable = true;
zones = {
${interfaces.wan.name} = {
interfaces = [interfaces.wan.name interfaces.lan.name];
};
${interfaces.lan.name} = {
parent = interfaces.wan.name;
ipv4Addresses = [cidr];
};
## banned = {
## ingressExpression = [
## "ip saddr @banlist"
## "ip6 saddr @banlist6"
## ];
## egressExpression = [
## "ip daddr @banlist"
## "ip6 daddr @banlist6"
## ];
## };
};
rules = {
dhcp = {
from = "all";
to = [hosts.beefcake.ip];
allowedTCPPorts = [67];
allowedUDPPorts = [67];
};
http = {
from = "all";
to = [me];
allowedTCPPorts = [80 443];
};
router-ssh = {
from = "all";
to = [me];
allowedTCPPorts = [2201];
};
server-ssh = {
from = "all";
to = [hosts.beefcake.ip];
allowedTCPPorts = [22];
};
};
};
*/
/*
dnsmasq serves as our DHCP and DNS server
almost all the configuration should be derived from the values at the top of
this file
*/
/*
since the home network reserves port 22 for ssh to the big server and to
gitea, the router uses port 2201 for ssh
*/
/*
NOTE: everything from here on is deprecated or old stuff
TODO: may not be strictly necessary for IPv6?
TODO: also may not even be the best implementation?
services.radvd = {
enable = false;
## NOTE: this config is just the default arch linux config I think and may
## need tweaking? this is what I had on the arch linux router, though :shrug:
config = ''
interface lo
{
AdvSendAdvert on;
MinRtrAdvInterval 3;
MaxRtrAdvInterval 10;
AdvDefaultPreference low;
AdvHomeAgentFlag off;
prefix 2001:db8:1:0::/64
{
AdvOnLink on;
AdvAutonomous on;
AdvRouterAddr off;
};
prefix 0:0:0:1234::/64
{
AdvOnLink on;
AdvAutonomous on;
AdvRouterAddr off;
Base6to4Interface ppp0;
AdvPreferredLifetime 120;
AdvValidLifetime 300;
};
route 2001:db0:fff::/48
{
AdvRoutePreference high;
AdvRouteLifetime 3600;
};
RDNSS 2001:db8::1 2001:db8::2
{
AdvRDNSSLifetime 30;
};
DNSSL branch.example.com example.com
{
AdvDNSSLLifetime 30;
};
};
'';
};
TODO: old config, should be deleted ASAP
services.dnsmasq = {
enable = false;
settings = {
# server endpoints
listen-address = "::1,127.0.0.1,${ip}";
port = "53";
# DNS cache entries
cache-size = "10000";
# local domain entries
local = "/lan/";
domain = "lan";
expand-hosts = true;
dhcp-authoritative = true;
conf-file = "/usr/share/dnsmasq/trust-anchors.conf";
dnssec = true;
except-interface = "${wan_if}";
interface = "${lan_if}";
enable-ra = true;
# dhcp-option = "121,${cidr},${ip}";
dhcp-range = [
"lan,${dhcp_lease_space.min},${dhcp_lease_space.max},${netmask},10m"
"tag:${lan_if},::1,constructor:${lan_if},ra-names,12h"
];
dhcp-host = [
"${hosts.dragon.host},${hosts.dragon.ip},12h"
"${hosts.beefcake.host},${hosts.beefcake.ip},12h"
];
# may need to go in /etc/hosts (networking.extraHosts), too?
address = [
"/video.lyte.dev/192.168.0.9"
"/git.lyte.dev/192.168.0.9"
"/bw.lyte.dev/192.168.0.9"
"/files.lyte.dev/192.168.0.9"
"/vpn.h.lyte.dev/192.168.0.9"
"/.h.lyte.dev/192.168.0.9"
];
server = [
"${ip}"
"8.8.8.8"
"8.8.4.4"
"1.1.1.1"
"1.0.0.1"
];
};
};
TODO: old config, should be deleted ASAP
nftables = {
enable = false;
flushRuleset = true;
tables = {
filter = {
family = "inet";
content = ''
chain input {
# type filter hook input priority filter; policy accept;
type filter hook input priority 0;
# anything from loopback interface
iifname "lo" accept
# accept traffic we originated
ct state { established, related } counter accept
ct state invalid counter drop
# ICMP
ip6 nexthdr icmpv6 icmpv6 type { echo-request, nd-neighbor-solicit, nd-neighbor-advert, nd-router-solicit, nd-router-advert, mld-listener-query, destination-unreachable, packet-too-big, time-exceeded, parameter-problem } counter accept
ip protocol icmp icmp type { echo-request, destination-unreachable, router-advertisement, time-exceeded, parameter-problem } counter accept
ip protocol icmpv6 counter accept
ip protocol icmp counter accept
meta l4proto ipv6-icmp counter accept
udp dport dhcpv6-client counter accept
tcp dport { 64022, 22, 53, 67, 25565 } counter accept
udp dport { 64020, 22, 53, 67 } counter accept
## iifname "iot" ip saddr $iot-ip tcp dport { llmnr } counter accept
## iifname "iot" ip saddr $iot-ip udp dport { mdns, llmnr } counter accept
iifname "${lan_if}" tcp dport { llmnr } counter accept
iifname "${lan_if}" udp dport { mdns, llmnr } counter accept
counter drop
}
# allow all outgoing
chain output {
type filter hook output priority 0;
accept
}
chain forward {
type filter hook forward priority 0;
accept
}
'';
};
nat = {
family = "ip";
content = ''
set masq_saddr {
type ipv4_addr
flags interval
elements = { ${cidr} }
}
map map_port_ipport {
type inet_proto . inet_service : ipv4_addr . inet_service
}
chain prerouting {
iifname ${lan_if} accept
type nat hook prerouting priority dstnat + 1; policy accept;
fib daddr type local dnat ip addr . port to meta l4proto . th dport map @map_port_ipport
iifname ${wan_if} tcp dport { 22, 80, 443, 25565, 64022 } dnat to ${hosts.beefcake.ip}
iifname ${wan_if} udp dport { 64020 } dnat to ${hosts.beefcake.ip}
## iifname ${wan_if} tcp dport { 25565 } dnat to 192.168.0.244
## iifname ${wan_if} udp dport { 25565 } dnat to 192.168.0.244
## router
iifname ${wan_if} tcp dport { 2201 } dnat to ${ip}
}
chain output {
type nat hook output priority -99; policy accept;
ip daddr != 127.0.0.0/8 oif "lo" dnat ip addr . port to meta l4proto . th dport map @map_port_ipport
}
chain postrouting {
type nat hook postrouting priority srcnat + 1; policy accept;
oifname ${lan_if} masquerade
ip saddr @masq_saddr masquerade
}
'';
};
};
};
TODO: also want to try to avoid using dhcpcd for IPv6 since systemd-networkd
should be sufficient?
dhcpcd = {
enable = false;
extraConfig = ''
duid
## No way.... https://github.com/NetworkConfiguration/dhcpcd/issues/36#issuecomment-954777644
## issues caused by guests with oneplus devices
noarp
persistent
vendorclassid
option domain_name_servers, domain_name, domain_search
option classless_static_routes
option interface_mtu
option host_name
#option ntp_servers
require dhcp_server_identifier
slaac private
noipv4ll
noipv6rs
static domain_name_servers=${ip}
interface ${wan_if}
gateway
ipv6rs
iaid 1
## option rapid_commit
## ia_na 1
ia_pd 1 ${lan_if}
interface ${lan_if}
static ip_address=${cidr}
static routers=${ip}
static domain_name_servers=${ip}
'';
};
*/
} }

View file

@ -113,18 +113,45 @@ in
lan.mac = "00:01:2e:82:73:5a"; lan.mac = "00:01:2e:82:73:5a";
}; };
openPorts = {
tcp = {
"Accept SSH to router" = 2201;
};
udp = {
"Accept DNS" = 53;
};
};
# TODO: nftables
hosts = { hosts = {
dragon = { dragon = {
ip = "192.168.0.10"; ip = "192.168.0.10";
}; };
bald = { bald = {
ip = "192.168.0.11"; ip = "192.168.0.11";
nat.tcp.minecraft = 25565;
additionalHosts = [ additionalHosts = [
"ourcraft.lyte.dev" "ourcraft.lyte.dev"
]; ];
}; };
beefcake = { beefcake = {
ip = "192.168.0.9"; ip = "192.168.0.9";
nat = {
tcp = {
"SSH" = 22;
"HTTP" = 80;
"HTTPS" = 443;
"Minecraft Flanilla" = 26966;
};
udp = {
"QUIC" = [
80
443
];
"Factorio" = 34197;
};
};
additionalHosts = [ additionalHosts = [
".beefcake.lan" ".beefcake.lan"
"a.lyte.dev" "a.lyte.dev"
@ -147,321 +174,6 @@ in
]; ];
}; };
}; };
# NOTE: see flake.nix 'nnf.nixosModules.default'
/*
nftables.firewall = let
me = config.networking.nftables.firewall.localZoneName;
in {
enable = true;
snippets.nnf-common.enable = true;
zones = {
${interfaces.wan.name} = {
interfaces = [interfaces.wan.name interfaces.lan.name];
}; };
${interfaces.lan.name} = {
parent = interfaces.wan.name;
ipv4Addresses = [cidr];
};
## banned = {
## ingressExpression = [
## "ip saddr @banlist"
## "ip6 saddr @banlist6"
## ];
## egressExpression = [
## "ip daddr @banlist"
## "ip6 daddr @banlist6"
## ];
## };
};
rules = {
dhcp = {
from = "all";
to = [hosts.beefcake.ip];
allowedTCPPorts = [67];
allowedUDPPorts = [67];
};
http = {
from = "all";
to = [me];
allowedTCPPorts = [80 443];
};
router-ssh = {
from = "all";
to = [me];
allowedTCPPorts = [2201];
};
server-ssh = {
from = "all";
to = [hosts.beefcake.ip];
allowedTCPPorts = [22];
};
};
};
*/
};
/*
dnsmasq serves as our DHCP and DNS server
almost all the configuration should be derived from the values at the top of
this file
*/
/*
since the home network reserves port 22 for ssh to the big server and to
gitea, the router uses port 2201 for ssh
*/
/*
NOTE: everything from here on is deprecated or old stuff
TODO: may not be strictly necessary for IPv6?
TODO: also may not even be the best implementation?
services.radvd = {
enable = false;
## NOTE: this config is just the default arch linux config I think and may
## need tweaking? this is what I had on the arch linux router, though :shrug:
config = ''
interface lo
{
AdvSendAdvert on;
MinRtrAdvInterval 3;
MaxRtrAdvInterval 10;
AdvDefaultPreference low;
AdvHomeAgentFlag off;
prefix 2001:db8:1:0::/64
{
AdvOnLink on;
AdvAutonomous on;
AdvRouterAddr off;
};
prefix 0:0:0:1234::/64
{
AdvOnLink on;
AdvAutonomous on;
AdvRouterAddr off;
Base6to4Interface ppp0;
AdvPreferredLifetime 120;
AdvValidLifetime 300;
};
route 2001:db0:fff::/48
{
AdvRoutePreference high;
AdvRouteLifetime 3600;
};
RDNSS 2001:db8::1 2001:db8::2
{
AdvRDNSSLifetime 30;
};
DNSSL branch.example.com example.com
{
AdvDNSSLLifetime 30;
};
};
'';
};
TODO: old config, should be deleted ASAP
services.dnsmasq = {
enable = false;
settings = {
# server endpoints
listen-address = "::1,127.0.0.1,${ip}";
port = "53";
# DNS cache entries
cache-size = "10000";
# local domain entries
local = "/lan/";
domain = "lan";
expand-hosts = true;
dhcp-authoritative = true;
conf-file = "/usr/share/dnsmasq/trust-anchors.conf";
dnssec = true;
except-interface = "${wan_if}";
interface = "${lan_if}";
enable-ra = true;
# dhcp-option = "121,${cidr},${ip}";
dhcp-range = [
"lan,${dhcp_lease_space.min},${dhcp_lease_space.max},${netmask},10m"
"tag:${lan_if},::1,constructor:${lan_if},ra-names,12h"
];
dhcp-host = [
"${hosts.dragon.host},${hosts.dragon.ip},12h"
"${hosts.beefcake.host},${hosts.beefcake.ip},12h"
];
# may need to go in /etc/hosts (networking.extraHosts), too?
address = [
"/video.lyte.dev/192.168.0.9"
"/git.lyte.dev/192.168.0.9"
"/bw.lyte.dev/192.168.0.9"
"/files.lyte.dev/192.168.0.9"
"/vpn.h.lyte.dev/192.168.0.9"
"/.h.lyte.dev/192.168.0.9"
];
server = [
"${ip}"
"8.8.8.8"
"8.8.4.4"
"1.1.1.1"
"1.0.0.1"
];
};
};
TODO: old config, should be deleted ASAP
nftables = {
enable = false;
flushRuleset = true;
tables = {
filter = {
family = "inet";
content = ''
chain input {
# type filter hook input priority filter; policy accept;
type filter hook input priority 0;
# anything from loopback interface
iifname "lo" accept
# accept traffic we originated
ct state { established, related } counter accept
ct state invalid counter drop
# ICMP
ip6 nexthdr icmpv6 icmpv6 type { echo-request, nd-neighbor-solicit, nd-neighbor-advert, nd-router-solicit, nd-router-advert, mld-listener-query, destination-unreachable, packet-too-big, time-exceeded, parameter-problem } counter accept
ip protocol icmp icmp type { echo-request, destination-unreachable, router-advertisement, time-exceeded, parameter-problem } counter accept
ip protocol icmpv6 counter accept
ip protocol icmp counter accept
meta l4proto ipv6-icmp counter accept
udp dport dhcpv6-client counter accept
tcp dport { 64022, 22, 53, 67, 25565 } counter accept
udp dport { 64020, 22, 53, 67 } counter accept
## iifname "iot" ip saddr $iot-ip tcp dport { llmnr } counter accept
## iifname "iot" ip saddr $iot-ip udp dport { mdns, llmnr } counter accept
iifname "${lan_if}" tcp dport { llmnr } counter accept
iifname "${lan_if}" udp dport { mdns, llmnr } counter accept
counter drop
}
# allow all outgoing
chain output {
type filter hook output priority 0;
accept
}
chain forward {
type filter hook forward priority 0;
accept
}
'';
};
nat = {
family = "ip";
content = ''
set masq_saddr {
type ipv4_addr
flags interval
elements = { ${cidr} }
}
map map_port_ipport {
type inet_proto . inet_service : ipv4_addr . inet_service
}
chain prerouting {
iifname ${lan_if} accept
type nat hook prerouting priority dstnat + 1; policy accept;
fib daddr type local dnat ip addr . port to meta l4proto . th dport map @map_port_ipport
iifname ${wan_if} tcp dport { 22, 80, 443, 25565, 64022 } dnat to ${hosts.beefcake.ip}
iifname ${wan_if} udp dport { 64020 } dnat to ${hosts.beefcake.ip}
## iifname ${wan_if} tcp dport { 25565 } dnat to 192.168.0.244
## iifname ${wan_if} udp dport { 25565 } dnat to 192.168.0.244
## router
iifname ${wan_if} tcp dport { 2201 } dnat to ${ip}
}
chain output {
type nat hook output priority -99; policy accept;
ip daddr != 127.0.0.0/8 oif "lo" dnat ip addr . port to meta l4proto . th dport map @map_port_ipport
}
chain postrouting {
type nat hook postrouting priority srcnat + 1; policy accept;
oifname ${lan_if} masquerade
ip saddr @masq_saddr masquerade
}
'';
};
};
};
TODO: also want to try to avoid using dhcpcd for IPv6 since systemd-networkd
should be sufficient?
dhcpcd = {
enable = false;
extraConfig = ''
duid
## No way.... https://github.com/NetworkConfiguration/dhcpcd/issues/36#issuecomment-954777644
## issues caused by guests with oneplus devices
noarp
persistent
vendorclassid
option domain_name_servers, domain_name, domain_search
option classless_static_routes
option interface_mtu
option host_name
#option ntp_servers
require dhcp_server_identifier
slaac private
noipv4ll
noipv6rs
static domain_name_servers=${ip}
interface ${wan_if}
gateway
ipv6rs
iaid 1
## option rapid_commit
## ia_na 1
ia_pd 1 ${lan_if}
interface ${lan_if}
static ip_address=${cidr}
static routers=${ip}
static domain_name_servers=${ip}
'';
};
*/
}; };
} }