{ config, lib, pkgs, ... }: with lib; let cfg = config.orbekk.router; devices = ["eno1" "eno2"]; mullvadMark = 2; heMark = 1; router-netns-up = pkgs.writeScript "router-netns-up" '' #!${pkgs.bash}/bin/bash ip netns add router ip netns exec router ip link set lo up ${lib.concatMapStrings (device: '' ip link set ${device} netns router '') devices} ip link add router-vport type veth peer name dragon-vport netns router ip netns exec router ${pkgs.procps}/bin/sysctl -w net.ipv4.conf.default.forwarding=1 ip netns exec router ${pkgs.procps}/bin/sysctl -w net.ipv4.conf.all.forwarding=1 ip netns exec router ${pkgs.procps}/bin/sysctl -w net.ipv6.conf.default.forwarding=1 ip netns exec router ${pkgs.procps}/bin/sysctl -w net.ipv6.conf.all.forwarding=1 ''; router-netns-down = pkgs.writeScript "router-netns-down" '' #!${pkgs.bash}/bin/bash ip netns del router ''; router-config = { config, lib, pkgs, ... }: { system.stateVersion = "22.05"; environment.systemPackages = with pkgs; [ tcpdump ]; virtualisation.vswitch.enable = true; virtualisation.vswitch.resetOnStart = true; networking.vswitches.kjlan = { interfaces.wan-vport = { vlan = 10; type = "internal"; }; interfaces.lan-vport = { vlan = 100; type = "internal"; }; interfaces.servers-vport = { vlan = 20; type = "internal"; }; interfaces.admin-vport = { vlan = 255; type = "internal"; }; interfaces.dragon-vport = { vlan = 20; }; extraOvsctlCmds = '' add-bond kjlan bond0 eno1 eno2 lacp=active set interface wan-vport mac=\"3c:97:0e:19:7e:5c\" ''; }; networking.interfaces.eno1 = {}; networking.interfaces.eno2 = {}; networking.interfaces.wan-vport = { useDHCP = true; }; networking.interfaces.lan-vport = { ipv4.addresses = [{address = "172.20.100.1"; prefixLength = 24;}]; }; networking.interfaces.servers-vport = { ipv4.addresses = [{address = "172.20.20.1"; prefixLength = 24;}]; ipv6.addresses = [{address = "2001:470:8e2e:20::1"; prefixLength = 64;}]; }; networking.interfaces.admin-vport = { ipv4.addresses = [{address = "10.10.255.18"; prefixLength = 24;}]; ipv4.routes = [{address = "10.10.255.0"; prefixLength = 24;}]; }; networking.sits.he0 = { dev = "wan-vport"; remote = "209.51.161.14"; }; networking.interfaces.he0.ipv6.addresses = [{ address = "2001:470:1f06:1194::2"; prefixLength = 64; }]; systemd.services.he0-netdev.after = ["kjlan-netdev.service"]; networking.iproute2.enable = true; networking.iproute2.rttablesExtraConfig = '' ${toString mullvadMark} mullvad ${toString heMark} he ''; systemd.services.network-route-setup = { description = "HE tunnel route setup"; requires = [ "network-online.target" ]; after = [ "network.target" "network-online.target" ]; wantedBy = [ "multi-user.target" ]; path = [ pkgs.iproute ]; script = '' ip -6 rule add fwmark ${toString heMark} table he || true ip -6 route replace default dev he0 table he ip -6 route flush cache ip rule add fwmark ${toString mullvadMark} table mullvad || true ip route replace default dev mullvad table mullvad ip route flush cache ''; }; services.dnsmasq = { enable = true; servers = [ "1.1.1.1" "8.8.8.8" "8.8.4.4" ]; resolveLocalQueries = false; extraConfig = '' no-resolv no-hosts log-debug dhcp-authoritative enable-ra address=/localhost/::1 address=/localhost/127.0.0.1 dhcp-range=tag:servers-vport,172.20.20.10,172.20.20.254,5m dhcp-option=tag:servers-vport,option:router,172.20.20.1 dhcp-option=tag:servers-vport,option:dns-server,172.20.20.1 dhcp-range=tag:servers-vport,::2,::1000,constructor:servers-vport,2h dhcp-host=id:dragon,tag:servers-vport,172.20.20.2 dhcp-host=id:00:01:00:01:21:a2:4e:a8:d0:bf:9c:45:a6:ec,tag:servers-vport,[::d] # dhcp-host=id:dragon,::d dhcp-range=tag:lan-vport,172.20.100.10,172.20.100.254,5m dhcp-option=tag:lan-vport,option:router,172.20.100.1 dhcp-option=tag:lan-vport,option:dns-server,172.20.100.1 ''; }; networking.dhcpcd = { enable = true; extraConfig = '' noipv6rs noipv6 nohook resolv.conf interface wan-vport dhcp ''; }; networking.firewall.enable = false; systemd.services.nftables.before = mkForce ["network.target"]; systemd.services.nftables.after = ["kjlan-netdev.service"]; systemd.services.nftables.wantedBy = ["network.target"]; networking.nftables.enable = true; networking.nftables.ruleset = let ports-to-csv = ports: concatStringsSep "," (map toString ports); in '' table inet filter { chain input { type filter hook input priority 0 iif lo accept ct state {established, related} counter accept meta l4proto {tcp, udp} th dport {bootps, bootpc, domain, dhcpv6-client, dhcpv6-server} counter accept ip protocol ipv6 counter accept comment "sit tunnel" ip protocol icmp limit rate 4/second counter accept comment "icmp v4" ip6 nexthdr ipv6-icmp counter accept comment "accept all ICMP types" iif wan-vport counter drop meta nftrace set 1 counter drop } chain output { type filter hook output priority 0 counter accept } chain forward { type filter hook forward priority 0; policy drop ct state vmap { established : accept, related : accept, invalid : drop } iif lan-vport counter accept iif servers-vport counter accept counter drop } } table inet mangle { chain prerouting { type filter hook prerouting priority -150 ip6 saddr 2001:470:8e2e::/48 ip6 daddr != 2001:470:8e2e::/48 ip6 daddr != fe80::/64 meta nftrace set 1 ip6 saddr 2001:470:8e2e::/48 ip6 daddr != 2001:470:8e2e::/48 ip6 daddr != fe80::/64 meta mark set ${toString heMark} } } table ip nat { chain prerouting { type nat hook prerouting priority -100; policy accept } chain postrouting { type nat hook postrouting priority 100; policy accept ip saddr 172.16.0.0/12 oif {"wan-vport"} masquerade } } ''; }; in { options = { orbekk.router = { enable = mkEnableOption "Enable router config"; }; }; config = mkIf cfg.enable { boot.kernel.sysctl = { "net.ipv4.conf.all.forwarding" = true; "net.ipv4.conf.default.forwarding" = true; "net.ipv6.conf.all.forwarding" = true; "net.ipv6.conf.default.forwarding" = true; }; systemd.services."router-netns" = { description = "router network namespace"; before = ["network.target"]; after = ["network-interfaces.target"]; path = with pkgs; [bash iproute]; serviceConfig = { Type = "oneshot"; RemainAfterExit = "yes"; ExecStart = "${router-netns-up}"; ExecStop = "${router-netns-down}"; }; }; orbekk.mullvad.enable = true; networking.wireguard.interfaces.mullvad.interfaceNamespace = "router"; systemd.services."container@router" = { after = ["router-netns.service"]; requires = ["router-netns.service"]; wantedBy = ["network.target"]; }; containers.router = { autoStart = true; extraFlags = ["--network-namespace-path" "/var/run/netns/router"]; privateNetwork = false; config = router-config; additionalCapabilities = ["CAP_SYS_ADMIN" "CAP_NET_ADMIN"]; }; services.ddclient = { enable = true; configFile = "/opt/secret/he-ddclient.conf"; }; # FIXME: Workaround for ddclient.conf not being available to ddclient. systemd.services.ddclient.serviceConfig.DynamicUser = lib.mkForce false; }; }