diff --git a/hosts/by-name/servo/net.nix b/hosts/by-name/servo/net.nix index 79b1f596..484c916e 100644 --- a/hosts/by-name/servo/net.nix +++ b/hosts/by-name/servo/net.nix @@ -24,9 +24,6 @@ in sane.ports.openFirewall = true; sane.ports.openUpnp = true; - # view refused packets with: `sudo journalctl -k` - # networking.firewall.logRefusedPackets = true; - # these useDHCP lines are legacy from the auto-generated config. might be safe to remove now? networking.useDHCP = false; networking.interfaces.eth0.useDHCP = true; diff --git a/hosts/common/net/default.nix b/hosts/common/net/default.nix index e1fbf33a..9d5d788f 100644 --- a/hosts/common/net/default.nix +++ b/hosts/common/net/default.nix @@ -7,6 +7,16 @@ ./upnp.nix ./vpn.nix ]; + + systemd.network.enable = true; + networking.useNetworkd = true; + + # view refused packets with: `sudo journalctl -k` + # networking.firewall.logRefusedPackets = true; + # networking.firewall.logRefusedUnicastsOnly = false; + # networking.firewall.logReversePathDrops = true; + # networking.firewall.enable = false; #< set false to debug + # the default backend is "wpa_supplicant". # wpa_supplicant reliably picks weak APs to connect to. # see: diff --git a/hosts/common/net/vpn.nix b/hosts/common/net/vpn.nix index d1fdfca3..ecf5a88f 100644 --- a/hosts/common/net/vpn.nix +++ b/hosts/common/net/vpn.nix @@ -3,56 +3,115 @@ # to add a new OVPN VPN: # - generate a privkey `wg genkey` # - add this key to `sops secrets/universal.yaml` -# - upload pubkey to OVPN.com +# - upload pubkey to OVPN.com (`cat wg.priv | wg pubkey`) # - generate config @ OVPN.com # - copy the Address, PublicKey, Endpoint from OVPN's config # N.B.: maximum interface name in Linux is 15 characters. +# +# debugging: +# - `journalctl -u systemd-networkd` +# +# docs: +# - wireguard (nixos): +# - wireguard (arch): let def-wg-vpn = name: { endpoint, publicKey, address, dns, privateKeyFile }: { - # networking.wg-quick.interfaces."${name}" = { - # inherit address privateKeyFile dns; - # peers = [ - # { - # allowedIPs = [ - # "0.0.0.0/0" - # "::/0" - # ]; - # inherit endpoint publicKey; - # } - # ]; - # # to start: `systemctl start wg-quick-${name}` - # autostart = false; - # }; - systemd.network.netdevs."${name}" = { + systemd.network.netdevs."99-${name}" = { # see: `man 5 systemd.netdev` + netdevConfig = { + Kind = "wireguard"; + Name = name; + }; wireguardConfig = { PrivateKeyFile = privateKeyFile; + FirewallMark = 51820; }; wireguardPeers = [{ - AllowedIPs = [ - "0.0.0.0/0" - "::/0" - ]; - Endpoint = endpoint; - PublicKey = publicKey; + wireguardPeerConfig = { + AllowedIPs = [ + "0.0.0.0/0" + "::/0" + ]; + Endpoint = endpoint; + PublicKey = publicKey; + }; }]; }; - systemd.network.networks."${name}" = { + systemd.network.networks."50-${name}" = { # see: `man 5 systemd.network` matchConfig.Name = name; networkConfig.Address = address; networkConfig.DNS = dns; + # DNSDefaultRoute: system DNS queries are sent to this link's DNS server + networkConfig.DNSDefaultRoute = true; + # Domains = ~.: system DNS queries are sent to this link's DNS server + networkConfig.Domains = "~."; + routingPolicyRules = [ + { + routingPolicyRuleConfig = { + # allow all non-default-route rules in the main table to take precedence over our wireguard rules. + # this allows reaching LAN machines (and the LAN's gateway!) without traversing the VPN. + SuppressPrefixLength = 0; + Table = "main"; + Priority = 10000; + }; + } + { + routingPolicyRuleConfig = { + # redirect any outbound packet not yet marked over to the wireguard table, through which it will enter the wg device. + # the wg device will then route it over the tunnel, using the LAN gateway to reach the tunnel endpoint + # -- which it can route directly, thanks to the higher-precedent rule above which allows reaching the LAN (and therefore gateway) without tunneling. + # this defines an ip rule: show it with `ip rule`. + FirewallMark = 51820; + InvertRule = true; + Table = 51820; + Priority = 10001; + }; + } + ]; + routes = [{ + # ovpn.com tunnels don't use a gateway. it's as if this link peers with the entire internet. + # routeConfig.Gateway = address; + # routeConfig.GatewayOnLink = true; + routeConfig.Table = 51820; #< TODO: use name-based table, per VPN + routeConfig.Scope = "link"; + routeConfig.Destination = "0.0.0.0/0"; + }]; + # RequiredForOnline => should `systemd-networkd-wait-online` fail if this network can't come up? + linkConfig.RequiredForOnline = false; + # ActivationPolicy = "manual" means don't auto-up this interface (default is "up") + linkConfig.ActivationPolicy = "manual"; }; }; - def-ovpn = name: { endpoint, publicKey, address }: def-wg-vpn "ovpnd-${name}" { + def-ovpn = name: { endpoint, publicKey, address }: (def-wg-vpn "ovpnd-${name}" { inherit endpoint publicKey address; privateKeyFile = config.sops.secrets."wg/ovpnd_${name}_privkey".path; dns = [ "46.227.67.134" "192.165.9.158" ]; + }) // { + sops.secrets."wg/ovpnd_${name}_privkey" = { + # needs to be readable by systemd-network or else it says "Ignoring network device" and doesn't expose it to networkctl. + owner = "systemd-network"; + }; }; in lib.mkMerge [ + { + networking.firewall.extraCommands = with pkgs; '' + # wireguard packet marking. without this, rpfilter drops responses from a wireguard VPN + # because the "reverse path check" fails (i.e. it thinks a response to the packet would go out via a different interface than what the wireguard packet arrived at). + # debug with e.g. `iptables --list -v -n -t mangle` + # - and `networking.firewall.logReversePathDrops = true;`, `networking.firewall.logRefusedPackets = true;` + # - and `journalctl -k` to see dropped packets + # + # note that wg-quick also adds a rule to reject non-local traffic from all interfaces EXCEPT the tunnel. + # that may protect against actors trying to probe us: actors we connect to via wireguard who send their response packets (speculatively) to our plaintext IP to see if we accept them. + # but that's fairly low concern, and firewalling by the gateway/NAT helps protect against that already. + ${iptables}/bin/iptables -t mangle -I PREROUTING 1 -j CONNMARK --restore-mark + ${iptables}/bin/iptables -A POSTROUTING -t mangle -m mark --mark 51820 -j CONNMARK --save-mark + ''; + } (def-ovpn "us" { endpoint = "vpn31.prd.losangeles.ovpn.com:9929"; publicKey = "VW6bEWMOlOneta1bf6YFE25N/oMGh1E1UFBCfyggd0k=";