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.
This commit is contained in:
Simon Bruder 2024-08-27 00:13:25 +02:00
parent 771489a176
commit 959f7be3d0
Signed by: simon
GPG key ID: 347FF8699CDA0776
9 changed files with 176 additions and 17 deletions

View file

@ -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-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] 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] 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: [] azure_kv: []
hc_vault: [] hc_vault: []
age: [] age: []
lastmodified: "2023-08-08T09:43:37Z" lastmodified: "2024-08-26T18:50:19Z"
mac: ENC[AES256_GCM,data:lxoKzGyPwdfeI5Dlmgx9K9SBhfRIaokvum+dJWABUoGtIMtrhp4K4ZRF1Rjja8oTi4w3b+s9aUBpxt8TLu9vJZFsUkhY2gqW5bX3Ub/3xMAR9YSG3LtijRSMuKkdVlAkdjB6Guz9aHNVBG3fTZ+SfTlyOQdImW6bK4tydbGHKgY=,iv:6kVR4zZfHnqhcOT3N2tClGST8h7FLjIseXDu2xS2DEY=,tag:rd/f7cHSoxLT3O7HluVWLA==,type:str] mac: ENC[AES256_GCM,data:k26ZEKuFtS0GLMqFIbY0QiVfHvmpxt3JgLvZIhEHcC3wQ80OhRNeyKocZhua1T5iSfhfvlckXYZl6tTZkCEh4fj3NmYMtQ9vwpoexdYWwx5ylPT3rpByfBbO+foHgQ3JXk6Kyt2R9ULjghMU3/lEcsG4AuGU1XMomsTzrdigXY8=,iv:ls3nIFIwTM//tSvee/aHj6Qv2nn/gZMKgGF+aQWNxeg=,tag:l58uHLpvPfIkbUn9gl+lzg==,type:str]
pgp: pgp:
- created_at: "2024-01-22T00:20:19Z" - created_at: "2024-01-22T00:20:19Z"
enc: |- enc: |-
@ -79,4 +80,4 @@ sops:
-----END PGP MESSAGE----- -----END PGP MESSAGE-----
fp: 28677f2e3584b39f528a779caf445ebb39c882b7 fp: 28677f2e3584b39f528a779caf445ebb39c882b7
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.7.3 version: 3.8.1

View file

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2023 Simon Bruder <simon@sbruder.de> # SPDX-FileCopyrightText: 2023-2024 Simon Bruder <simon@sbruder.de>
# #
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
@ -35,22 +35,22 @@ in
vlan = { vlan = {
lan = { lan = {
id = 10; 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"; domain = "lan.shinonome-lab.de";
}; };
management = { management = {
id = 20; 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"; domain = "management.shinonome-lab.de";
}; };
guest = { guest = {
id = 30; 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"; domain = "guest.shinonome-lab.de";
}; };
iot = { iot = {
id = 40; 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"; domain = "iot.shinonome-lab.de";
}; };
}; };

View file

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2023 Simon Bruder <simon@sbruder.de> # SPDX-FileCopyrightText: 2023-2024 Simon Bruder <simon@sbruder.de>
# #
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
@ -36,6 +36,8 @@ in
./tc.nix ./tc.nix
]; ];
sbruder.wireguard.he.enable = true;
boot.kernel.sysctl = { boot.kernel.sysctl = {
"net.ipv4.conf.all.forwarding" = true; "net.ipv4.conf.all.forwarding" = true;
"net.ipv6.conf.all.forwarding" = true; "net.ipv6.conf.all.forwarding" = true;
@ -106,6 +108,7 @@ in
# Only use RA # Only use RA
DHCPv6Client = false; DHCPv6Client = false;
UseDNS = "no"; UseDNS = "no";
UseGateway = false; # should not be used by default for routing (wg-he takes precendence)
}; };
}; };
physical-lan = { physical-lan = {
@ -128,6 +131,13 @@ in
name = "enp4s0"; name = "enp4s0";
bridge = [ "br-lan" ]; bridge = [ "br-lan" ];
}; };
# extended from common config
wg-he = {
address = lib.singleton "2001:470:73b9::1";
routes = lib.singleton {
routeConfig.Gateway = "::"; # on link
};
};
} }
]; ];
}; };

View file

@ -54,6 +54,12 @@ in
server = [ server = [
"127.0.0.1#5053" "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" ]; systemd.services.dnsmasq.after = [ "systemd-networkd.service" ];

View file

@ -4,34 +4,51 @@
define NAT_LAN_IFACES = { "br-lan", "br-guest" } define NAT_LAN_IFACES = { "br-lan", "br-guest" }
define PHYSICAL_WAN = "enp1s0" define PHYSICAL_WAN = "enp1s0"
# only includes interfaces that use NAT
define NAT_WAN_IFACES = { $PHYSICAL_WAN } 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 { table inet filter {
chain forward { chain forward {
type filter hook forward priority filter; policy drop 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) # plastic router, might be vulnerable (FIXME v6 is still reachable)
iifname "br-guest" ip daddr "192.168.0.1" drop iifname "br-guest" ip daddr "192.168.0.1" drop
# allow traffic between selected VLANs and wan # allow traffic between selected VLANs and wan
iifname $NAT_LAN_IFACES oifname $NAT_WAN_IFACES counter accept iifname $NAT_LAN_IFACES oifname $WAN_IFACES counter accept
iifname $NAT_WAN_IFACES oifname $NAT_LAN_IFACES ct state established,related 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 # traffic from lan to all other vlans is allowed
iifname "br-lan" oifname $VLAN_BRIDGES counter accept; iifname "br-lan" oifname $VLAN_BRIDGES counter accept;
iifname $VLAN_BRIDGES oifname "br-lan" ct state established,related 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 { chain postrouting {
type nat hook postrouting priority filter; policy accept type nat hook postrouting priority filter; policy accept
oifname $NAT_WAN_IFACES masquerade 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, # Only allow select connections from and to (physical) wan,
# overriding NixOS firewall in some cases. # overriding NixOS firewall in some cases.
table inet restrict-wan { table inet restrict-wan {

View file

@ -16,7 +16,10 @@
sbruder = { sbruder = {
nginx.hardening.enable = true; nginx.hardening.enable = true;
full = false; full = false;
wireguard.home.enable = true; wireguard = {
he.enable = true;
home.enable = true;
};
infovhost.enable = true; infovhost.enable = true;
}; };

View file

@ -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] 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] li7y-environment: ENC[AES256_GCM,data:cm4+672JelbYsBm0rwrF/I9gS72XfAlj335v0+EfXmPSD1LCBJ3clR7jZC7SVH5D9ZSaSlrY8J/+7hgDmzsiR2kypNBvfMvN825AF5QFehnYeHhxUktU+uig7RzpRUeWSPM0r8j6lmpGNc7vd3S+L3TWn2ZfCJ8Kc28Ad2M9yFiZ7PPqB6qqLnsx2peQuafDhefuohLPOYA=,iv:84yL6l7zqeb7l3w3ARskJoQEvI1+HxoCCKrLhB0kx7E=,tag:GCetAOW7pvyjKEM26A9ZbA==,type:str]
sops: sops:
@ -6,8 +7,8 @@ sops:
azure_kv: [] azure_kv: []
hc_vault: [] hc_vault: []
age: [] age: []
lastmodified: "2024-07-14T17:32:43Z" lastmodified: "2024-08-26T17:49:34Z"
mac: ENC[AES256_GCM,data:7D9xHNpdhI6CgX94PAoJJIJqVZ403ZL7dXbdnod2do4M+Qf0yRrRDxi6hPipf0BX0vsSq1npdiXcnwP50PZHal8LW7IJRjfefW5WnO+BLD42sIxt5mikdNfZhpyg3dHB7j+8m1lE1+veK/Ho06V32sckibhBG4AFBfMZ/k1VIns=,iv:NS9CaSyEUdmJEKFejiaugtZ5Nf8norhoaCaOwPZsxow=,tag:Y2Nu92iYO0PSqtXMLc3D7g==,type:str] mac: ENC[AES256_GCM,data:tnjZq65XULfFeEa7fDwTbOFTgjyvZ+UHW+T7Hqny3kzUb5bgaX6D3Xj2Ij0CrTxgyEzRLpkBG/TpEDdY1//NaruGl6CDYmYHEb5yHAzDRzpbPoZhteGfyuiRKGblK3EvMWpj2x4ZTnO7Y13fGpOzA5o38maAZL3eYPUb2yueD58=,iv:70dlzD6/uteJZZMo2T5R+H7QvZH60AWDrwc8iljIEb4=,tag:ADWZObS6Aq/iwRLPLlqPjg==,type:str]
pgp: pgp:
- created_at: "2024-01-22T00:20:20Z" - created_at: "2024-01-22T00:20:20Z"
enc: |- enc: |-

View file

@ -1,9 +1,10 @@
# SPDX-FileCopyrightText: 2020-2023 Simon Bruder <simon@sbruder.de> # SPDX-FileCopyrightText: 2020-2024 Simon Bruder <simon@sbruder.de>
# #
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
{ {
imports = [ imports = [
./he.nix
./home.nix ./home.nix
./support.nix ./support.nix
]; ];

120
modules/wireguard/he.nix Normal file
View file

@ -0,0 +1,120 @@
# SPDX-FileCopyrightText: 2020-2024 Simon Bruder <simon@sbruder.de>
#
# 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;
};
}