From 959f7be3d03535916fe9312cc8f244731f206d76 Mon Sep 17 00:00:00 2001 From: Simon Bruder Date: Tue, 27 Aug 2024 00:13:25 +0200 Subject: [PATCH] Connect home network with IPv6 addresses It adds a bit of latency (and is definitely not the best solution in theory), but finally allows dropping IPv6 NAT and it works within the constraits my home network has to live in. --- machines/shinobu/secrets.yaml | 7 +- machines/shinobu/services/router/common.nix | 10 +- machines/shinobu/services/router/default.nix | 12 +- machines/shinobu/services/router/dnsmasq.nix | 6 + machines/shinobu/services/router/rules.nft | 25 +++- machines/yuzuru/configuration.nix | 5 +- machines/yuzuru/secrets.yaml | 5 +- modules/wireguard/default.nix | 3 +- modules/wireguard/he.nix | 120 +++++++++++++++++++ 9 files changed, 176 insertions(+), 17 deletions(-) create mode 100644 modules/wireguard/he.nix diff --git a/machines/shinobu/secrets.yaml b/machines/shinobu/secrets.yaml index 9e1245a..0219aa6 100644 --- a/machines/shinobu/secrets.yaml +++ b/machines/shinobu/secrets.yaml @@ -1,3 +1,4 @@ +wg-he-private-key: ENC[AES256_GCM,data:bT1G2nZHJO9j04I4j3QZYn7BxGX4XHxzgXDr3iFTnu/kirik6+0Eh/AUp+4=,iv:SeowjlP64t8lPn+WXqrOtZJWA3geTSO9ST9JNuPQwu0=,tag:ctLXe7BP0Ob/ADD4q7yOmg==,type:str] wg-home-private-key: ENC[AES256_GCM,data:gm4INfmp226u4wp+LuKgf5m2nTFFw4S24w4PRPcW/A7CU713c9NtQ+kPDKg=,iv:JAir9z5/Db6+Oroq+0vXPZLZLA2gjY2Be6hRAmgV5AE=,tag:fxL9nK3v5xERfcoBbCUsXg==,type:str] wg-upstream-private-key: ENC[AES256_GCM,data:CO50H7QsLQ2x0QQXnB7c0leG8NdV66gWrdWBWOR9z4ukSN7qj/qqe83t82k=,iv:2as2HfTfRje3TEap8QpPfzz4saNDgjo6Ty1DTF23JVE=,tag:ZYe+59wrpX7mV1HcDllMdg==,type:str] hostapd-config: ENC[AES256_GCM,data:a0ESrrsquLq6VRJM588C5A+FmVxJwJSzwRuv2o//LL5OybcDS8jkVUajosXEs0qmQ6Xfc1gFDcevCYUwJ24eZ+ynKLWwoNx8RXXwbpllO7FkI68vcauUij1CtUgVb8aHheKfrFuyW7WU1wE3NTtOt2gij1+nM3iKS3vFXtX2n9L2fuy2b3EhOUBiakxAeQmyVmclSVBDYt12i4h4tW7GpPr8AjoIiZgz0Hyx5zA5f/JTPzz/P200eM0tCttNPbMNPBGztJfw7raRIX+v6xw7QNPMgf03TOae17mt6uggTNKJfEPeanzcEMA3xR6xoFUqJL6Hvowyl4MrSFc+E5Rvft+qhp8m6tAqQln9Z3MzaDtxSBWnWdvWEcyeK1aDBQ57/aIwo8kVs47Iblqbi5+jM/n4DoeQtqTM1kS7sZ3XDQ26suW5KCw+VIeqEEqdu6g5ZXMO2SipSOzP5jPjX+5ubX3SXcyoAIo41Efa6YGdWtl3,iv:oLk5tatZEY5AI/PlTBJHShGCKiyvve9rPhGARAtMMj4=,tag:Bkan2Hff8L8ZcC67r+fWjg==,type:str] @@ -7,8 +8,8 @@ sops: azure_kv: [] hc_vault: [] age: [] - lastmodified: "2023-08-08T09:43:37Z" - mac: ENC[AES256_GCM,data:lxoKzGyPwdfeI5Dlmgx9K9SBhfRIaokvum+dJWABUoGtIMtrhp4K4ZRF1Rjja8oTi4w3b+s9aUBpxt8TLu9vJZFsUkhY2gqW5bX3Ub/3xMAR9YSG3LtijRSMuKkdVlAkdjB6Guz9aHNVBG3fTZ+SfTlyOQdImW6bK4tydbGHKgY=,iv:6kVR4zZfHnqhcOT3N2tClGST8h7FLjIseXDu2xS2DEY=,tag:rd/f7cHSoxLT3O7HluVWLA==,type:str] + lastmodified: "2024-08-26T18:50:19Z" + mac: ENC[AES256_GCM,data:k26ZEKuFtS0GLMqFIbY0QiVfHvmpxt3JgLvZIhEHcC3wQ80OhRNeyKocZhua1T5iSfhfvlckXYZl6tTZkCEh4fj3NmYMtQ9vwpoexdYWwx5ylPT3rpByfBbO+foHgQ3JXk6Kyt2R9ULjghMU3/lEcsG4AuGU1XMomsTzrdigXY8=,iv:ls3nIFIwTM//tSvee/aHj6Qv2nn/gZMKgGF+aQWNxeg=,tag:l58uHLpvPfIkbUn9gl+lzg==,type:str] pgp: - created_at: "2024-01-22T00:20:19Z" enc: |- @@ -79,4 +80,4 @@ sops: -----END PGP MESSAGE----- fp: 28677f2e3584b39f528a779caf445ebb39c882b7 unencrypted_suffix: _unencrypted - version: 3.7.3 + version: 3.8.1 diff --git a/machines/shinobu/services/router/common.nix b/machines/shinobu/services/router/common.nix index d85cfa3..f2a214d 100644 --- a/machines/shinobu/services/router/common.nix +++ b/machines/shinobu/services/router/common.nix @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2023 Simon Bruder +# SPDX-FileCopyrightText: 2023-2024 Simon Bruder # # SPDX-License-Identifier: AGPL-3.0-or-later @@ -35,22 +35,22 @@ in vlan = { lan = { id = 10; - subnet = mkSubnet "10.80.1.0/24" "fd00:80:1::/64"; + subnet = mkSubnet "10.80.1.0/24" "2001:470:73b9:1::/64"; domain = "lan.shinonome-lab.de"; }; management = { id = 20; - subnet = mkSubnet "10.80.2.0/24" "fd00:80:2::/64"; + subnet = mkSubnet "10.80.2.0/24" "2001:470:73b9:2::/64"; domain = "management.shinonome-lab.de"; }; guest = { id = 30; - subnet = mkSubnet "10.80.3.0/24" "fd00:80:3::/64"; + subnet = mkSubnet "10.80.3.0/24" "2001:470:73b9:3::/64"; domain = "guest.shinonome-lab.de"; }; iot = { id = 40; - subnet = mkSubnet "10.80.4.0/24" "fd00:80:4::/64"; + subnet = mkSubnet "10.80.4.0/24" "2001:470:73b9:4::/64"; domain = "iot.shinonome-lab.de"; }; }; diff --git a/machines/shinobu/services/router/default.nix b/machines/shinobu/services/router/default.nix index a66af45..8710a6a 100644 --- a/machines/shinobu/services/router/default.nix +++ b/machines/shinobu/services/router/default.nix @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2023 Simon Bruder +# SPDX-FileCopyrightText: 2023-2024 Simon Bruder # # SPDX-License-Identifier: AGPL-3.0-or-later @@ -36,6 +36,8 @@ in ./tc.nix ]; + sbruder.wireguard.he.enable = true; + boot.kernel.sysctl = { "net.ipv4.conf.all.forwarding" = true; "net.ipv6.conf.all.forwarding" = true; @@ -106,6 +108,7 @@ in # Only use RA DHCPv6Client = false; UseDNS = "no"; + UseGateway = false; # should not be used by default for routing (wg-he takes precendence) }; }; physical-lan = { @@ -128,6 +131,13 @@ in name = "enp4s0"; bridge = [ "br-lan" ]; }; + # extended from common config + wg-he = { + address = lib.singleton "2001:470:73b9::1"; + routes = lib.singleton { + routeConfig.Gateway = "::"; # on link + }; + }; } ]; }; diff --git a/machines/shinobu/services/router/dnsmasq.nix b/machines/shinobu/services/router/dnsmasq.nix index 7bfb7b6..7b3e432 100644 --- a/machines/shinobu/services/router/dnsmasq.nix +++ b/machines/shinobu/services/router/dnsmasq.nix @@ -54,6 +54,12 @@ in server = [ "127.0.0.1#5053" ]; + + # Authoritative zones for external reachability (only AAAA records) + auth-server = "shinobu.shinonome-lab.de,2001:470:73b9::1"; + auth-zone = map + (vlan: "${vlan.domain},${vlan.subnet.v6.cidr}") + (lib.attrValues cfg.vlan); }; }; systemd.services.dnsmasq.after = [ "systemd-networkd.service" ]; diff --git a/machines/shinobu/services/router/rules.nft b/machines/shinobu/services/router/rules.nft index 169425f..05cdaed 100644 --- a/machines/shinobu/services/router/rules.nft +++ b/machines/shinobu/services/router/rules.nft @@ -4,34 +4,51 @@ define NAT_LAN_IFACES = { "br-lan", "br-guest" } define PHYSICAL_WAN = "enp1s0" +# only includes interfaces that use NAT define NAT_WAN_IFACES = { $PHYSICAL_WAN } +# also includes interfaces that do not use NAT +define WAN_IFACES = { $NAT_WAN_IFACES, "wg-he" } table inet filter { chain forward { type filter hook forward priority filter; policy drop + # Use MSS clamping to avoid too large packets not going through the tunnel. + tcp flags syn / syn,rst tcp option maxseg size set rt mtu + # plastic router, might be vulnerable (FIXME v6 is still reachable) iifname "br-guest" ip daddr "192.168.0.1" drop # allow traffic between selected VLANs and wan - 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 $WAN_IFACES counter accept + iifname $WAN_IFACES oifname $NAT_LAN_IFACES ct state established,related counter accept + + # allow lan clients to be publicly reachable + iifname "wg-he" oifname "br-lan" counter accept # traffic from lan to all other vlans is allowed iifname "br-lan" oifname $VLAN_BRIDGES counter accept; iifname $VLAN_BRIDGES oifname "br-lan" ct state established,related counter accept - iifname $NAT_WAN_IFACES oifname "br-iot" ct state established,related counter accept + iifname $WAN_IFACES oifname "br-iot" ct state established,related counter accept } } -table inet nat { +table ip nat { chain postrouting { type nat hook postrouting priority filter; policy accept oifname $NAT_WAN_IFACES masquerade } } +table ip6 public-access { + chain input { + type filter hook input priority filter; policy accept + + iifname "wg-he" oifname "br-lan" counter accept + } +} + # Only allow select connections from and to (physical) wan, # overriding NixOS firewall in some cases. table inet restrict-wan { diff --git a/machines/yuzuru/configuration.nix b/machines/yuzuru/configuration.nix index e873c1c..d46c940 100644 --- a/machines/yuzuru/configuration.nix +++ b/machines/yuzuru/configuration.nix @@ -16,7 +16,10 @@ sbruder = { nginx.hardening.enable = true; full = false; - wireguard.home.enable = true; + wireguard = { + he.enable = true; + home.enable = true; + }; infovhost.enable = true; }; diff --git a/machines/yuzuru/secrets.yaml b/machines/yuzuru/secrets.yaml index ff423c9..bb10668 100644 --- a/machines/yuzuru/secrets.yaml +++ b/machines/yuzuru/secrets.yaml @@ -1,3 +1,4 @@ +wg-he-private-key: ENC[AES256_GCM,data:aTH+AUBgG2D1CUF0zp1OzTUBu5Td2J2fsq3EpYEUuPGQFA+EbAYS+4AEipg=,iv:vNkqtoixZ+I+C5L4Vbck3EhCYGKzzIvwHIjiNs5PPIQ=,tag:6SMQ9oqKd7FdLvQNt2SAYA==,type:str] wg-home-private-key: ENC[AES256_GCM,data:0ylkx9p62CBGqVg+T52eHbMwbLcZM/v3tg/wJukDq76heN1TtQqbbqgVZKc=,iv:/aUkqKhihnBWQFLIRjS7kHigBCBXX7L4KY5q+cO9Q00=,tag:jQSMVElMfIyrG5hs7HuxUQ==,type:str] li7y-environment: ENC[AES256_GCM,data:cm4+672JelbYsBm0rwrF/I9gS72XfAlj335v0+EfXmPSD1LCBJ3clR7jZC7SVH5D9ZSaSlrY8J/+7hgDmzsiR2kypNBvfMvN825AF5QFehnYeHhxUktU+uig7RzpRUeWSPM0r8j6lmpGNc7vd3S+L3TWn2ZfCJ8Kc28Ad2M9yFiZ7PPqB6qqLnsx2peQuafDhefuohLPOYA=,iv:84yL6l7zqeb7l3w3ARskJoQEvI1+HxoCCKrLhB0kx7E=,tag:GCetAOW7pvyjKEM26A9ZbA==,type:str] sops: @@ -6,8 +7,8 @@ sops: azure_kv: [] hc_vault: [] age: [] - lastmodified: "2024-07-14T17:32:43Z" - mac: ENC[AES256_GCM,data:7D9xHNpdhI6CgX94PAoJJIJqVZ403ZL7dXbdnod2do4M+Qf0yRrRDxi6hPipf0BX0vsSq1npdiXcnwP50PZHal8LW7IJRjfefW5WnO+BLD42sIxt5mikdNfZhpyg3dHB7j+8m1lE1+veK/Ho06V32sckibhBG4AFBfMZ/k1VIns=,iv:NS9CaSyEUdmJEKFejiaugtZ5Nf8norhoaCaOwPZsxow=,tag:Y2Nu92iYO0PSqtXMLc3D7g==,type:str] + lastmodified: "2024-08-26T17:49:34Z" + mac: ENC[AES256_GCM,data:tnjZq65XULfFeEa7fDwTbOFTgjyvZ+UHW+T7Hqny3kzUb5bgaX6D3Xj2Ij0CrTxgyEzRLpkBG/TpEDdY1//NaruGl6CDYmYHEb5yHAzDRzpbPoZhteGfyuiRKGblK3EvMWpj2x4ZTnO7Y13fGpOzA5o38maAZL3eYPUb2yueD58=,iv:70dlzD6/uteJZZMo2T5R+H7QvZH60AWDrwc8iljIEb4=,tag:ADWZObS6Aq/iwRLPLlqPjg==,type:str] pgp: - created_at: "2024-01-22T00:20:20Z" enc: |- diff --git a/modules/wireguard/default.nix b/modules/wireguard/default.nix index c06f856..d464ab5 100644 --- a/modules/wireguard/default.nix +++ b/modules/wireguard/default.nix @@ -1,9 +1,10 @@ -# SPDX-FileCopyrightText: 2020-2023 Simon Bruder +# SPDX-FileCopyrightText: 2020-2024 Simon Bruder # # SPDX-License-Identifier: AGPL-3.0-or-later { imports = [ + ./he.nix ./home.nix ./support.nix ]; diff --git a/modules/wireguard/he.nix b/modules/wireguard/he.nix new file mode 100644 index 0000000..008c9c9 --- /dev/null +++ b/modules/wireguard/he.nix @@ -0,0 +1,120 @@ +# SPDX-FileCopyrightText: 2020-2024 Simon Bruder +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +{ lib, config, ... }: +let + serverHostName = "yuzuru"; + serverPort = 51820; + peers = { + yuzuru = { + subnets = [ ]; + publicKey = "mWm92aZybisoLtd11g4XqwUZvQGVxMfPW9/za3/1/0Y="; + }; + shinobu = { + subnets = [ "2001:470:73b9::/56" ]; + publicKey = "c8lnzMWFeTzQmXwNV0DlD2ROJqBcDL0F9WN5u4lVeFQ="; + }; + }; + + cfg = config.sbruder.wireguard.he; + enableServer = config.networking.hostName == serverHostName; +in +{ + options.sbruder.wireguard.he.enable = lib.mkEnableOption "WireGuard tunnel wg-he"; + + config = lib.mkIf cfg.enable { + sops.secrets.wg-he-private-key = { + owner = config.users.users.systemd-network.name; + sopsFile = ./../../machines + "/${config.networking.hostName}/secrets.yaml"; + }; + + systemd.network = { + enable = true; + netdevs = { + wg-he = { + netdevConfig = { + Kind = "wireguard"; + Name = "wg-he"; + }; + wireguardConfig = { + PrivateKeyFile = config.sops.secrets.wg-he-private-key.path; + } // (lib.optionalAttrs enableServer { + ListenPort = serverPort; + }); + wireguardPeers = + if enableServer + then + map + ({ publicKey, subnets }: { + wireguardPeerConfig = { + PublicKey = publicKey; + AllowedIPs = subnets; + }; + }) + (lib.attrValues + (lib.filterAttrs + (n: v: n != config.networking.hostName) + peers)) + else + lib.singleton { + wireguardPeerConfig = { + PublicKey = peers."${serverHostName}".publicKey; + AllowedIPs = "::/0"; + Endpoint = "85.215.73.203:${toString serverPort}"; + PersistentKeepalive = 25; + }; + }; + }; + } // (lib.optionalAttrs enableServer { + he = { + netdevConfig = { + Name = "he"; + Kind = "sit"; + MTUBytes = "1480"; + }; + tunnelConfig = { + Remote = "216.66.80.30"; # tserv1.fra1.he.net + Local = "85.215.73.203"; + TTL = 255; + }; + }; + }); + networks = { + wg-he = { + name = "wg-he"; + networkConfig = lib.optionalAttrs enableServer { + IPForward = "ipv6"; + }; + routes = lib.singleton { + routeConfig.Destination = "2001:470:73b9::/48"; + }; + }; + } // (lib.optionalAttrs enableServer { + he = { + name = "he"; + address = lib.singleton "2001:470:1f0a:5db::2/64"; + gateway = lib.singleton "2001:470:1f0a:5db::1"; + routingPolicyRules = lib.singleton { + routingPolicyRuleConfig = { + From = "2001:470:73b9::/48"; + Table = "0x73b9"; + }; + }; + routes = lib.singleton { + routeConfig = { + Gateway = "2001:470:1f0a:5db::1"; + Table = "0x73b9"; + }; + }; + }; + # FIXME interface name is hardcoded + eth0 = { + networkConfig.Tunnel = "he"; + }; + }); + }; + + networking.firewall.allowedUDPPorts = lib.optional enableServer serverPort; + }; +}