homefree/profiles/router.nix
2024-01-14 01:12:45 -08:00

376 lines
11 KiB
Nix

{ config, inputs, pkgs, ... }:
let
# @TODO: How to determine interface names?
# wan-interface = "ens5";
wan-interface = "ens3";
# lan-interface = "ens6";
lan-interface = "ens5";
vlan-wan-id = 100;
vlan-lan-id = 200;
vlan-iot-id = 201;
vlan-guest-id = 202;
# lan-interface = "ens3";
dns-servers = [ "1.1.1.1" "1.0.0.1" ];
adlist = inputs.adblock-unbound.packages.${pkgs.system};
in
{
# REFERENCES:
# https://github.com/chayleaf/nixos-router
# https://github.com/chayleaf/dotfiles/blob/master/system/hosts/router/default.nix
# https://francis.begyn.be/blog/nixos-home-router
# https://discourse.nixos.org/t/do-you-use-nixos-on-your-router-firewall/18998
# https://homenetworkguy.com/how-to/set-up-a-fully-functioning-home-network-using-opnsense/
#-----------------------------------------------------------------------------------------------------
# IP Forwarding
#-----------------------------------------------------------------------------------------------------
boot.kernel.sysctl = {
# if you use ipv4, this is all you need
"net.ipv4.conf.all.forwarding" = true;
# If you want to use it for ipv6
"net.ipv6.conf.all.forwarding" = true;
# source: https://github.com/mdlayher/homelab/blob/master/nixos/routnerr-2/configuration.nix#L52
# By default, not automatically configure any IPv6 addresses.
"net.ipv6.conf.all.accept_ra" = 0;
"net.ipv6.conf.all.autoconf" = 0;
"net.ipv6.conf.all.use_tempaddr" = 0;
# On WAN, allow IPv6 autoconfiguration and tempory address use.
"net.ipv6.conf.${wan-interface}.accept_ra" = 2;
"net.ipv6.conf.${wan-interface}.autoconf" = 1;
};
networking = {
#-----------------------------------------------------------------------------------------------------
# Interface config
#-----------------------------------------------------------------------------------------------------
useDHCP = false;
nameservers = dns-servers;
## Define VLANS
## https://www.breakds.org/post/vlan-configuration-by-examples/
# vlans = {
# wan = {
# id = vlan-wan-id;
# interface = wan-interface;
# };
# lan = {
# id = vlan-lan-id;
# interface = lan-interface;
# };
# iot = {
# id = vlan-iot-id;
# interface = lan-interface;
# };
# guest = {
# id = vlan-guest-id;
# interface = lan-interface;
# };
# };
interfaces = {
# Don't request DHCP on the physical interfaces
${wan-interface} = {
# useDHCP = false;
};
${lan-interface} = {
useDHCP = false;
ipv4.addresses = [{
address = "10.1.1.1";
prefixLength = 24;
}];
};
# Handle the VLANs
# wan = {
# useDHCP = false;
# };
# lan = {
# ipv4.addresses = [{
# address = "10.1.1.1";
# prefixLength = 24;
# }];
# };
# iot = {
# ipv4.addresses = [{
# address = "10.2.1.1";
# prefixLength = 24;
# }];
# };
# guest = {
# ipv4.addresses = [{
# address = "10.3.1.1";
# prefixLength = 24;
# }];
# };
};
#-----------------------------------------------------------------------------------------------------
# Firewall
#-----------------------------------------------------------------------------------------------------
nat.enable = false;
firewall.enable = false;
## @TODO: Look into nftables Nix DSL: https://github.com/chayleaf/notnft
## https://www.reddit.com/r/NixOS/comments/14copvu/notnft_write_nftables_rules_in_nix/
nftables = {
enable = true;
ruleset = ''
flush ruleset
# table ip filter {
# # allow all packets sent by the firewall machine itself
# chain output {
# type filter hook output priority 100; policy accept;
# }
#
# # allow LAN to firewall, disallow WAN to firewall
# chain input {
# type filter hook input priority 0; policy accept;
# iifname "${lan-interface}" accept
# iifname "${wan-interface}" drop
# }
#
# # allow packets from LAN to WAN, and WAN to LAN if LAN initiated the connection
# chain forward {
# type filter hook forward priority 0; policy drop;
# iifname "${lan-interface}" oifname "${wan-interface}" accept
# iifname "${wan-interface}" oifname "${lan-interface}" ct state related,established accept
# }
# }
table ip nat {
chain prerouting {
type nat hook prerouting priority 0; policy accept;
}
# for all packets to WAN, after routing, replace source address with primary IP of WAN interface
chain postrouting {
type nat hook postrouting priority 100; policy accept;
oifname "${wan-interface}" masquerade
}
}
'';
};
};
#-----------------------------------------------------------------------------------------------------
# Performance Tuning
#-----------------------------------------------------------------------------------------------------
systemd.services.tune-router-performance = {
wantedBy = [ "multi-user.target" ];
enable = true;
serviceConfig = {
User = "root";
Group = "root";
};
# script = builtins.readFile ../scripts/tune_router_performance.sh;
script = ''
GREP=${pkgs.gnugrep}/bin/grep
AWK=${pkgs.gawk}/bin/awk
# SMP - Symmetric MultiProcessing
# RPS - Receive Packet Steering
smp1=3
rps1=2
smp2=3
rps2=2
ens3_irq=$($GREP ens3 /proc/interrupts | $AWK '{ print $1+0 }')
# set balancer for enp1s0
echo $smp1 > /proc/irq/$ens3_irq/smp_affinity
# set rps for ens3
echo $rps1 > /sys/class/net/ens3/queues/rx-0/rps_cpus
ens5_irq=$($GREP ens5 /proc/interrupts | $AWK '{ print $1+0 }')
# set balancer for enp2s0
# echo $smp2 > /proc/irq/$ens5_irq/smp_affinity
# set rps for ens5
echo $rps2 > /sys/class/net/ens5/queues/rx-0/rps_cpus
'';
};
#-----------------------------------------------------------------------------------------------------
# DHCP
#-----------------------------------------------------------------------------------------------------
# See: https://nixos.wiki/wiki/Systemd-resolved
## Disabled as it conflicts with dnsmasq
services.resolved = {
enable = false;
dnssec = "true";
domains = [ "~." ];
fallbackDns = [ "1.1.1.1#one.one.one.one" "1.0.0.1#one.one.one.one" ];
extraConfig = ''
DNSOverTLS=yes
'';
};
services.dnsmasq = {
enable = true;
settings = {
## @TODO
## @WARNING - changes to this do not clear out old entries from /etc/dnsmasq-conf.conf
## Only DHCP server on network
dhcp-authoritative = true;
## Enable Router Advertising for ipv6
enable-ra = true;
## DNS servers to pass to clients
server = dns-servers;
## Which interfaces to bind to
interface = [
# "${lan-interface}.${builtins.toString vlan-lan-id}"
# "${lan-interface}.${builtins.toString vlan-iot-id}"
# "${lan-interface}.${builtins.toString vlan-guest-id}"
lan-interface
];
## IP ranges to hand out
dhcp-range = [
# "lan,10.1.1.100,10.1.1.254,255.255.255.0,8h"
# "iot,10.2.1.100,10.2.1.254,255.255.255.0,8h"
# "guest,10.3.1.100,10.3.1.254,255.255.255.0,8h"
"${lan-interface},10.1.1.100,10.1.1.254,255.255.255.0,8h"
];
## Disable DNS
port = 0;
## Additional DHCP options
dhcp-option = [
"option6:dns-server,[::]" # @TODO: point this at Unbound when ipv6 is setup
"option:dns-server,10.1.1.1"
];
cache-size = 500;
};
};
#-----------------------------------------------------------------------------------------------------
# DNS
#-----------------------------------------------------------------------------------------------------
## @TODO - Setup Unbound
## See: https://blog.josefsson.org/2015/10/26/combining-dnsmasq-and-unbound/
services.unbound = {
enable = true;
user = "root";
resolveLocalQueries = true;
settings = {
server = {
include = [
"\"${adlist.unbound-adblockStevenBlack}\""
];
interface = [
"127.0.0.1"
"::1"
"10.1.1.1"
];
access-control = [
"127.0.0.1/8 allow"
"::1 allow"
"10.1.1.1/8 allow"
# @TODO: need ipv6 address
];
outgoing-interface = [
## @TODO: should be WAN IP - how to get this automatically?
"10.0.2.15"
# @TODO: need ipv6 address
];
local-zone = [
"\"homefree.lan.\" static"
];
local-data = [
"\"radicale.lan. IN A 10.1.1.1\""
];
local-data-ptr = [
"\"10.1.1.1 radicale.lan\""
];
};
forward-zone = [
{
name = ".";
forward-addr = [
"1.1.1.1@853#cloudflare-dns.com"
"1.0.0.1@853#cloudflare-dns.com"
];
forward-tls-upstream = "yes";
}
# {
# name = "example.org.";
# forward-addr = [
# "1.1.1.1@853#cloudflare-dns.com"
# "1.0.0.1@853#cloudflare-dns.com"
# ];
# }
];
remote-control.control-enable = true;
};
};
#-----------------------------------------------------------------------------------------------------
# Dynamic DNS
#-----------------------------------------------------------------------------------------------------
# @TODO: https://discourse.nixos.org/t/ddclient-options/20935
services.ddclient = {
enable = true;
interval = "10m";
# protocol = "zoneedit1";
# username = "erahhal";
# zone = "homefree.host";
# passwordFile = config.age.secrets.ddclient.path;
# verbose = true;
configFile = config.age.secrets.ddclient-conf.path;
};
#-----------------------------------------------------------------------------------------------------
# Service Discovery
#-----------------------------------------------------------------------------------------------------
services.avahi = {
enable = true;
reflector = true;
allowInterfaces = [
# "lan"
# "iot"
# "guest"
lan-interface
];
# network locator e.g. scanners and printers
nssmdns = true;
};
#-----------------------------------------------------------------------------------------------------
# Packages
#-----------------------------------------------------------------------------------------------------
environment.systemPackages = with pkgs; [
ethtool # manage NIC settings (offload, NIC feeatures, ...)
tcpdump # view network traffic
conntrack-tools # view network connection states
];
}