nixos-config/machines/shinobu/services/router.nix

325 lines
9.7 KiB
Nix
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Home network configuration
# (2.5GbE clients)
# | |
# +----------+ +----------+
# | | | | | | (1GbE clients)
# | | | | | +|-|-|-|-|+
# +---+----+ +-+-+-+-+-+ |5 4 3 2 1|
# |upstream| | 1 2 3 4 | |TL-SG105 |
# +--------+ | shinobu | +---------+
# +---------+
#
# It consists of shinobu as a router (this configuration),
# connected to a TP-LINK TL-SG105E “smart managed” (i.e., it can do VLANs) 5-port switch.
# The upstream comes from some plasic Huawei router/AP I dont control.
#
# Because the switch only supports GbE,
# the two clients I currently have with support for 2.5GbE are connected
# directly to the two remaining network interfaces on shinobu.
# Once I have more devices with support for 2.5GbE
# or I find a good deal on a matching switch,
# I will change this.
#
# Wireless is configured by providing the whole hostapd configuration file as a secret.
# Once nixpkgs PR 222536 is merged, I will migrate to using the NixOS module.
# Thanks to Intels wisdom, its not possible to use 5GHz in AP mode.
{ config, lib, pkgs, ... }:
let
domain = "home.sbruder.de";
noVpnFwMark = 10000;
in
{
sops.secrets.wg-upstream-private-key = {
owner = config.users.users.systemd-network.name;
sopsFile = ../secrets.yaml;
};
sops.secrets.hostapd-config = {
sopsFile = ../secrets.yaml;
};
boot.kernel.sysctl = {
"net.ipv4.conf.all.forwarding" = true;
};
networking = {
# networkd handles this
useDHCP = false;
nftables = {
enable = true;
ruleset = ''
define NAT_LAN_IFACES = { "br-lan" }
define NAT_WAN_IFACES = { "wg-upstream" }
define PHYSICAL_WAN = "enp1s0"
define MASQUERADE_IFACES = { $NAT_WAN_IFACES, $PHYSICAL_WAN }
define VUEKO_V4 = 168.119.176.53
define VUEKO_V6 = 2a01:4f8:c012:2f4::
define VUEKO_PORT = 51820
table inet filter {
chain forward {
type filter hook forward priority filter; policy drop;
iifname $NAT_LAN_IFACES oifname $NAT_WAN_IFACES counter accept;
iifname $NAT_WAN_IFACES oifname $NAT_LAN_IFACES ct state established,related counter accept;
iifname $NAT_LAN_IFACES oifname $PHYSICAL_WAN ip daddr $VUEKO_V4 udp dport $VUEKO_PORT counter accept;
iifname $PHYSICAL_WAN oifname $NAT_LAN_IFACES ip saddr $VUEKO_V4 udp sport $VUEKO_PORT ct state established,related counter accept;
iifname $NAT_LAN_IFACES oifname $PHYSICAL_WAN ip6 daddr $VUEKO_V6 udp dport $VUEKO_PORT counter accept;
iifname $PHYSICAL_WAN oifname $NAT_LAN_IFACES ip6 saddr $VUEKO_V6 udp sport $VUEKO_PORT ct state established,related counter accept;
}
}
table inet nat {
chain prerouting {
type nat hook prerouting priority filter; policy accept;
}
chain postrouting {
type nat hook postrouting priority filter; policy accept;
oifname $MASQUERADE_IFACES masquerade;
}
}
table inet mangle {
chain output {
type route hook output priority mangle;
# Add fwmark noVpnMark to packets to vueko, so it will get routed correctly
ip daddr $VUEKO_V4 udp dport $VUEKO_PORT mark set ${toString noVpnFwMark} counter;
ip6 daddr $VUEKO_V6 udp dport $VUEKO_PORT mark set ${toString noVpnFwMark} counter;
}
}
'';
};
};
systemd.network = {
enable = true;
# not all interfaces need to be up
wait-online.extraArgs = [ "--any" ];
netdevs = {
br-lan = {
netdevConfig = {
Name = "br-lan";
Kind = "bridge";
};
};
wg-upstream = {
netdevConfig = {
Kind = "wireguard";
Name = "wg-upstream";
};
wireguardConfig = {
PrivateKeyFile = config.sops.secrets.wg-upstream-private-key.path;
FirewallMark = 51820;
};
wireguardPeers = lib.singleton {
wireguardPeerConfig = {
Endpoint = "193.32.248.71:51820";
PublicKey = "eprzkkkSbXCANngQDo305DIAvkKAnZaN71IpTNaOoTk=";
AllowedIPs = [ "0.0.0.0/0" "::0/0" ];
PersistentKeepalive = 25;
};
};
};
};
networks = {
wan = {
name = "enp1s0";
networkConfig = {
# Upstream provides no IPv6 :(
# If this is not set, it waits and fails systemd-networkd-wait-online
LinkLocalAddressing = "no";
IPv6AcceptRA = "no";
};
DHCP = "ipv4";
dhcpV4Config = {
UseDNS = "no";
};
};
lan1 = {
name = "enp2s0";
bridge = [ "br-lan" ];
};
lan2 = {
name = "enp3s0";
bridge = [ "br-lan" ];
};
lan3 = {
name = "enp4s0";
bridge = [ "br-lan" ];
};
br-lan = {
name = "br-lan";
domains = [ domain ];
address = [ "10.80.1.1/24" "fd00:80:1::1/64" ];
};
wg-upstream = {
name = "wg-upstream";
address = [ "10.66.208.88/32" "fc00:bbbb:bbbb:bb01::3:d057/128" ];
dns = [ "10.64.0.1" ];
routingPolicyRules = [
{
routingPolicyRuleConfig = {
FirewallMark = 51820;
InvertRule = "yes";
Table = 51820;
Priority = 10;
#SuppressPrefixLength = 0; # cant be used here (forwarding does not work with it)
};
}
# FIXME: those two shouldnt be necessary
# It should automatically detect those routes existing and prioritise them
{
routingPolicyRuleConfig = {
To = "10.80.0.0/24";
Priority = 9;
};
}
{
routingPolicyRuleConfig = {
To = "10.80.1.0/24";
Priority = 9;
};
}
{
routingPolicyRuleConfig = {
To = "168.119.176.53";
FirewallMark = noVpnFwMark;
Priority = 9;
};
}
];
routes = [
{
routeConfig = {
Gateway = "0.0.0.0"; # point-to-point connection
Table = 51820;
};
}
{
routeConfig = {
Gateway = "::";
};
}
];
};
};
};
services.resolved.enable = false;
services.dnsmasq = {
enable = true;
settings = {
bogus-priv = true; # do not forward revese lookups of internal addresses
domain-needed = true; # do not forward names without domain
interface = "br-lan"; # only respond to queries from lan
no-hosts = true; # do not resolve hosts from /etc/hosts
no-resolv = true; # only use explicitly configured resolvers
cache-size = 10000;
inherit domain;
# Allow resolving the router
interface-name = [
"${config.networking.hostName}.${domain},br-lan"
"${config.networking.hostName},br-lan"
];
# DHCPv4
dhcp-range = [
"10.80.1.20,10.80.1.150,12h" # DHCPv4
"fd00:80:1::,ra-stateless,ra-names" # SLAAC (for addresses) / DHCPv6 (for DNS)
];
dhcp-option = [
"option:router,10.80.1.1"
"option6:dns-server,fd00:80:1::1"
];
# Despite its name, the switch does not have a “smart” configuration,
# that would allow me to tell it not to get DHCP from wan,
# but from lan instead.
# So it has to use static configuration.
host-record = "switchviech,switchviech.${domain},10.80.1.19";
server = [
"10.64.0.1" # mullvad DNS, should be fastest overall
#"9.9.9.9" # dns.quad9.net
#"2620:fe::fe"
];
};
};
systemd.services.dnsmasq.after = [ "systemd-networkd.service" ];
services.prometheus.exporters.dnsmasq = {
enable = true;
listenAddress = config.sbruder.wireguard.home.address;
leasesPath = "/var/lib/dnsmasq/dnsmasq.leases";
};
networking.firewall.allowedUDPPorts = [ 53 67 ];
networking.firewall.allowedTCPPorts = [ 53 ];
# Wireless
boot.kernelModules = [ "nl80211" ];
environment.systemPackages = with pkgs; [
iw
wirelesstools
];
# The service is mostly taken from nixpkgs pr 222536.
systemd.services.hostapd = {
path = with pkgs; [ hostapd ];
after = [ "sys-subsystem-net-devices-wlp5s0.device" ];
bindsTo = [ "sys-subsystem-net-devices-wlp5s0.device" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${pkgs.hostapd}/bin/hostapd ${config.sops.secrets.hostapd-config.path}";
Restart = "always";
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
RuntimeDirectory = "hostapd";
# Hardening
LockPersonality = true;
MemoryDenyWriteExecute = true;
DevicePolicy = "closed";
DeviceAllow = "/dev/rfkill rw";
NoNewPrivileges = true;
PrivateUsers = false; # hostapd requires true root access.
PrivateTmp = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProcSubset = "pid";
ProtectSystem = "strict";
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_NETLINK"
"AF_UNIX"
];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallFilter = [
"@system-service"
"~@privileged"
"@chown"
];
UMask = "0077";
};
};
}