From 3685affc4c8470db64e3255572ecf4ec999a5987 Mon Sep 17 00:00:00 2001
From: Paul Buetow
Date: Sat, 17 Jan 2026 00:05:46 +0200
Subject: Update content for html
---
...5-05-11-f3s-kubernetes-with-freebsd-part-5.html | 274 +++++++++++++++++++-
...5-07-14-f3s-kubernetes-with-freebsd-part-6.html | 1 +
gemfeed/atom.xml | 279 ++++++++++++++++++++-
3 files changed, 550 insertions(+), 4 deletions(-)
(limited to 'gemfeed')
diff --git a/gemfeed/2025-05-11-f3s-kubernetes-with-freebsd-part-5.html b/gemfeed/2025-05-11-f3s-kubernetes-with-freebsd-part-5.html
index 896d4e04..80a0173f 100644
--- a/gemfeed/2025-05-11-f3s-kubernetes-with-freebsd-part-5.html
+++ b/gemfeed/2025-05-11-f3s-kubernetes-with-freebsd-part-5.html
@@ -13,7 +13,7 @@
f3s: Kubernetes with FreeBSD - Part 5: WireGuard mesh network
-Published at 2025-05-11T11:35:57+03:00, last updated Sun 11 Jan 21:33:40 EET 2026
+Published at 2025-05-11T11:35:57+03:00, last updated Thu 15 Jan 19:30:46 EET 2026
This is the fifth blog post about my f3s series for my self-hosting demands in my home lab. f3s? The "f" stands for FreeBSD, and the "3s" stands for k3s, the Kubernetes distribution I will use on FreeBSD-based physical machines.
+
+After setting up the IPv4-only mesh network, I decided to add dual-stack IPv6 support to enable more networking capabilities and prepare for the future. All 10 hosts (8 infrastructure + 2 roaming clients) now have both IPv4 and IPv6 addresses on their WireGuard interfaces.
+
+
IPv6 addressing scheme
+
+We use ULA (Unique Local Address) private IPv6 space, analogous to RFC1918 private IPv4 addresses:
+
+
+
+The mesh generator required two modifications to support dual-stack configurations:
+
+**1. Address generation (address method)**
+
+The generator now outputs multiple Address directives when IPv6 is present:
+
+
+
def address
+ return '# No Address = ... for OpenBSD here' if hosts[myself]['os'] == 'OpenBSD'
+
+ ipv4 = hosts[myself]['wg0']['ip']
+ ipv6 = hosts[myself]['wg0']['ipv6']
+
+ # WireGuard supports multiple Address directives for dual-stack
+ if ipv6
+ "Address = #{ipv4}\nAddress = #{ipv6}/64"
+ else
+ "Address = #{ipv4}"
+ end
+end
+
+
+**2. AllowedIPs generation (peers method)**
+
+For mesh peers, both IPv4 and IPv6 addresses are included in AllowedIPs:
+
+
+
if is_roaming
+ allowed_ips = '0.0.0.0/0, ::/0'
+else
+ # For mesh peers, allow both IPv4 and IPv6 if present
+ ipv4 = data['wg0']['ip']
+ ipv6 = data['wg0']['ipv6']
+ allowed_ips = ipv6 ? "#{ipv4}/32, #{ipv6}/128" : "#{ipv4}/32"
+end
+
+
+Roaming clients keep AllowedIPs = 0.0.0.0/0, ::/0 to route all traffic (IPv4 and IPv6) through the VPN.
+
+
IPv6 NAT on OpenBSD gateways
+
+To allow roaming clients to access the internet via IPv6, we added NAT66 rules to the OpenBSD gateways' pf.conf:
+
+
+# NAT for WireGuard clients to access internet (IPv4)
+match out on vio0 from 192.168.2.0/24 to any nat-to (vio0)
+
+# NAT66 for WireGuard clients to access internet (IPv6)
+# Uses NPTv6 (Network Prefix Translation) to translate ULA to public IPv6
+match out on vio0 inet6 from fd42:beef:cafe:2::/64 to any nat-to (vio0)
+
+# Allow all UDP traffic on WireGuard port (IPv4 and IPv6)
+pass in inet proto udp from any to any port 56709
+pass in inet6 proto udp from any to any port 56709
+
+
+OpenBSD's PF firewall supports IPv6 NAT with the same syntax as IPv4, using NPTv6 (RFC 6296) to translate the ULA addresses to the gateway's public IPv6 address.
+
+
Manual OpenBSD interface configuration
+
+Since OpenBSD doesn't use the Address directive in WireGuard configs, IPv6 must be manually configured on the wg0 interfaces. On blowfish:
+
+
+
rex@blowfish:~ $ doas vi /etc/hostname.wg0
+
+
+Add the IPv6 address (note the order - IPv6 must be configured before up):
+
+
+
+**Important**: The IPv6 address must be specified before the up directive. This ensures the interface has both addresses configured before WireGuard peers are loaded.
+
+Apply the configuration:
+
+
+
rex@blowfish:~ $ doas sh /etc/netstart wg0
+rex@blowfish:~ $ ifconfig wg0 | grep inet6
+inet6 fd42:beef:cafe:2::110 prefixlen 64
+
+
+Repeat for fishfinger with address fd42:beef:cafe:2::111.
+
+After reboot, the interface will automatically come up with both IPv4 and IPv6 addresses. WireGuard peers may take 30-60 seconds to establish handshakes after boot.
+
+
Verifying dual-stack connectivity
+
+After regenerating and deploying the configurations, both IPv4 and IPv6 work across the mesh:
+
+
+
# From r0 (Rocky Linux VM)
+root@r0:~ # ping -c 2 192.168.2.130 # IPv4 to f0
+64 bytes from 192.168.2.130: icmp_seq=1 ttl=64 time=2.12 ms
+64 bytes from 192.168.2.130: icmp_seq=2 ttl=64 time=0.681 ms
+
+root@r0:~ # ping6 -c 2 fd42:beef:cafe:2::130 # IPv6 to f0
+64 bytes from fd42:beef:cafe:2::130: icmp_seq=1 ttl=64 time=2.16 ms
+64 bytes from fd42:beef:cafe:2::130: icmp_seq=2 ttl=64 time=0.909 ms
+
+
+The dual-stack configuration is backward compatible—hosts without the ipv6 field in the YAML configuration will continue to generate IPv4-only configs.
+
+
Benefits of dual-stack
+
+Adding IPv6 to the mesh network provides:
+
+
+
**Future-proofing**: Ready for IPv6-only services and networks
+
**Compatibility**: Dual-stack maintains full IPv4 compatibility
+
**Learning**: Hands-on experience with IPv6 networking
+
**Flexibility**: Roaming clients can access both IPv4 and IPv6 internet resources
+
+
Manual gateway failover for roaming clients
+
+WireGuard doesn't automatically failover between multiple peers with identical AllowedIPs routes. When both gateways (blowfish and fishfinger) are configured with AllowedIPs = 0.0.0.0/0, ::/0, WireGuard uses the first peer with a recent handshake. If that gateway goes down, traffic won't automatically switch to the backup.
+
+To enable manual failover, separate configuration files have been created for roaming clients (earth laptop and pixel7pro phone), each containing only a single gateway peer.
+
+
Configuration files for pixel7pro (phone)
+
+Two separate configs in /home/paul/git/wireguardmeshgenerator/dist/pixel7pro/etc/wireguard/:
+
+
+
**wg0-blowfish.conf** - Routes all traffic through blowfish gateway (23.88.35.144)
+
**wg0-fishfinger.conf** - Routes all traffic through fishfinger gateway (46.23.94.99)
+
+
Configuration files for earth (laptop)
+
+Two separate configs in /home/paul/git/wireguardmeshgenerator/dist/earth/etc/wireguard/:
+
+
+
**wg0-blowfish.conf** - Routes all traffic through blowfish gateway
+
**wg0-fishfinger.conf** - Routes all traffic through fishfinger gateway
+
+
Using manual failover on Android
+
+On the pixel7pro phone, import both QR codes using the WireGuard app to create two separate tunnel profiles:
+
+
+
+
+In the WireGuard app, you can then manually enable/disable each tunnel to select which gateway to use. Only enable one tunnel at a time.
+
+
Using manual failover on Linux
+
+On the earth laptop, copy both configs and use systemd to switch between them:
+
+
+
# Install both configurations
+sudo cp dist/earth/etc/wireguard/wg0-blowfish.conf /etc/wireguard/
+sudo cp dist/earth/etc/wireguard/wg0-fishfinger.conf /etc/wireguard/
+
+# Start with blowfish gateway
+sudo systemctl start wg-quick@wg0-blowfish.service
+
+# To switch to fishfinger gateway
+sudo systemctl stop wg-quick@wg0-blowfish.service
+sudo systemctl start wg-quick@wg0-fishfinger.service
+
+
+This approach provides explicit control over which gateway handles roaming client traffic, useful when one gateway needs maintenance or experiences connectivity issues.
+
Happy WireGuard-ing
All is set up now. E.g. on f0:
diff --git a/gemfeed/2025-07-14-f3s-kubernetes-with-freebsd-part-6.html b/gemfeed/2025-07-14-f3s-kubernetes-with-freebsd-part-6.html
index a9aaa5fb..fe828914 100644
--- a/gemfeed/2025-07-14-f3s-kubernetes-with-freebsd-part-6.html
+++ b/gemfeed/2025-07-14-f3s-kubernetes-with-freebsd-part-6.html
@@ -1008,6 +1008,7 @@ ifconfig_re0_alias0="inet vhid 1 advskew 100 pass testpass
This allows clients to connect to f3s-storage-ha regardless of which physical server is currently the MASTER.
diff --git a/gemfeed/atom.xml b/gemfeed/atom.xml
index d24610e7..92baa74d 100644
--- a/gemfeed/atom.xml
+++ b/gemfeed/atom.xml
@@ -1,6 +1,6 @@
- 2026-01-11T22:40:26+02:00
+ 2026-01-17T00:03:44+02:00foo.zone feedTo be in the .zone!
@@ -7454,6 +7454,7 @@ ifconfig_re0_alias0="inet vhid 1 advskew 100 pass testpass
This allows clients to connect to f3s-storage-ha regardless of which physical server is currently the MASTER.
@@ -9566,7 +9567,7 @@ Jul 06 10:f3s: Kubernetes with FreeBSD - Part 5: WireGuard mesh network
https://foo.zone/gemfeed/2025-05-11-f3s-kubernetes-with-freebsd-part-5.html
- 2025-05-11T11:35:57+03:00, last updated Sun 11 Jan 21:33:40 EET 2026
+ 2025-05-11T11:35:57+03:00, last updated Thu 15 Jan 19:30:46 EET 2026Paul Buetow aka snonuxpaul@dev.buetow.org
@@ -9576,7 +9577,7 @@ Jul 06 10:
f3s: Kubernetes with FreeBSD - Part 5: WireGuard mesh network
-Published at 2025-05-11T11:35:57+03:00, last updated Sun 11 Jan 21:33:40 EET 2026
+Published at 2025-05-11T11:35:57+03:00, last updated Thu 15 Jan 19:30:46 EET 2026
This is the fifth blog post about my f3s series for my self-hosting demands in my home lab. f3s? The "f" stands for FreeBSD, and the "3s" stands for k3s, the Kubernetes distribution I will use on FreeBSD-based physical machines.
+
+After setting up the IPv4-only mesh network, I decided to add dual-stack IPv6 support to enable more networking capabilities and prepare for the future. All 10 hosts (8 infrastructure + 2 roaming clients) now have both IPv4 and IPv6 addresses on their WireGuard interfaces.
+
+
IPv6 addressing scheme
+
+We use ULA (Unique Local Address) private IPv6 space, analogous to RFC1918 private IPv4 addresses:
+
+
+
+The mesh generator required two modifications to support dual-stack configurations:
+
+**1. Address generation (address method)**
+
+The generator now outputs multiple Address directives when IPv6 is present:
+
+
+
def address
+ return '# No Address = ... for OpenBSD here' if hosts[myself]['os'] == 'OpenBSD'
+
+ ipv4 = hosts[myself]['wg0']['ip']
+ ipv6 = hosts[myself]['wg0']['ipv6']
+
+ # WireGuard supports multiple Address directives for dual-stack
+ if ipv6
+ "Address = #{ipv4}\nAddress = #{ipv6}/64"
+ else
+ "Address = #{ipv4}"
+ end
+end
+
+
+**2. AllowedIPs generation (peers method)**
+
+For mesh peers, both IPv4 and IPv6 addresses are included in AllowedIPs:
+
+
+
if is_roaming
+ allowed_ips = '0.0.0.0/0, ::/0'
+else
+ # For mesh peers, allow both IPv4 and IPv6 if present
+ ipv4 = data['wg0']['ip']
+ ipv6 = data['wg0']['ipv6']
+ allowed_ips = ipv6 ? "#{ipv4}/32, #{ipv6}/128" : "#{ipv4}/32"
+end
+
+
+Roaming clients keep AllowedIPs = 0.0.0.0/0, ::/0 to route all traffic (IPv4 and IPv6) through the VPN.
+
+
IPv6 NAT on OpenBSD gateways
+
+To allow roaming clients to access the internet via IPv6, we added NAT66 rules to the OpenBSD gateways' pf.conf:
+
+
+# NAT for WireGuard clients to access internet (IPv4)
+match out on vio0 from 192.168.2.0/24 to any nat-to (vio0)
+
+# NAT66 for WireGuard clients to access internet (IPv6)
+# Uses NPTv6 (Network Prefix Translation) to translate ULA to public IPv6
+match out on vio0 inet6 from fd42:beef:cafe:2::/64 to any nat-to (vio0)
+
+# Allow all UDP traffic on WireGuard port (IPv4 and IPv6)
+pass in inet proto udp from any to any port 56709
+pass in inet6 proto udp from any to any port 56709
+
+
+OpenBSD's PF firewall supports IPv6 NAT with the same syntax as IPv4, using NPTv6 (RFC 6296) to translate the ULA addresses to the gateway's public IPv6 address.
+
+
Manual OpenBSD interface configuration
+
+Since OpenBSD doesn't use the Address directive in WireGuard configs, IPv6 must be manually configured on the wg0 interfaces. On blowfish:
+
+
+
rex@blowfish:~ $ doas vi /etc/hostname.wg0
+
+
+Add the IPv6 address (note the order - IPv6 must be configured before up):
+
+
+
+**Important**: The IPv6 address must be specified before the up directive. This ensures the interface has both addresses configured before WireGuard peers are loaded.
+
+Apply the configuration:
+
+
+
rex@blowfish:~ $ doas sh /etc/netstart wg0
+rex@blowfish:~ $ ifconfig wg0 | grep inet6
+inet6 fd42:beef:cafe:2::110 prefixlen 64
+
+
+Repeat for fishfinger with address fd42:beef:cafe:2::111.
+
+After reboot, the interface will automatically come up with both IPv4 and IPv6 addresses. WireGuard peers may take 30-60 seconds to establish handshakes after boot.
+
+
Verifying dual-stack connectivity
+
+After regenerating and deploying the configurations, both IPv4 and IPv6 work across the mesh:
+
+
+
# From r0 (Rocky Linux VM)
+root@r0:~ # ping -c 2 192.168.2.130 # IPv4 to f0
+64 bytes from 192.168.2.130: icmp_seq=1 ttl=64 time=2.12 ms
+64 bytes from 192.168.2.130: icmp_seq=2 ttl=64 time=0.681 ms
+
+root@r0:~ # ping6 -c 2 fd42:beef:cafe:2::130 # IPv6 to f0
+64 bytes from fd42:beef:cafe:2::130: icmp_seq=1 ttl=64 time=2.16 ms
+64 bytes from fd42:beef:cafe:2::130: icmp_seq=2 ttl=64 time=0.909 ms
+
+
+The dual-stack configuration is backward compatible—hosts without the ipv6 field in the YAML configuration will continue to generate IPv4-only configs.
+
+
Benefits of dual-stack
+
+Adding IPv6 to the mesh network provides:
+
+
+
**Future-proofing**: Ready for IPv6-only services and networks
+
**Compatibility**: Dual-stack maintains full IPv4 compatibility
+
**Learning**: Hands-on experience with IPv6 networking
+
**Flexibility**: Roaming clients can access both IPv4 and IPv6 internet resources
+
+
Manual gateway failover for roaming clients
+
+WireGuard doesn't automatically failover between multiple peers with identical AllowedIPs routes. When both gateways (blowfish and fishfinger) are configured with AllowedIPs = 0.0.0.0/0, ::/0, WireGuard uses the first peer with a recent handshake. If that gateway goes down, traffic won't automatically switch to the backup.
+
+To enable manual failover, separate configuration files have been created for roaming clients (earth laptop and pixel7pro phone), each containing only a single gateway peer.
+
+
Configuration files for pixel7pro (phone)
+
+Two separate configs in /home/paul/git/wireguardmeshgenerator/dist/pixel7pro/etc/wireguard/:
+
+
+
**wg0-blowfish.conf** - Routes all traffic through blowfish gateway (23.88.35.144)
+
**wg0-fishfinger.conf** - Routes all traffic through fishfinger gateway (46.23.94.99)
+
+
Configuration files for earth (laptop)
+
+Two separate configs in /home/paul/git/wireguardmeshgenerator/dist/earth/etc/wireguard/:
+
+
+
**wg0-blowfish.conf** - Routes all traffic through blowfish gateway
+
**wg0-fishfinger.conf** - Routes all traffic through fishfinger gateway
+
+
Using manual failover on Android
+
+On the pixel7pro phone, import both QR codes using the WireGuard app to create two separate tunnel profiles:
+
+
+
+
+In the WireGuard app, you can then manually enable/disable each tunnel to select which gateway to use. Only enable one tunnel at a time.
+
+
Using manual failover on Linux
+
+On the earth laptop, copy both configs and use systemd to switch between them:
+
+
+
# Install both configurations
+sudo cp dist/earth/etc/wireguard/wg0-blowfish.conf /etc/wireguard/
+sudo cp dist/earth/etc/wireguard/wg0-fishfinger.conf /etc/wireguard/
+
+# Start with blowfish gateway
+sudo systemctl start wg-quick@wg0-blowfish.service
+
+# To switch to fishfinger gateway
+sudo systemctl stop wg-quick@wg0-blowfish.service
+sudo systemctl start wg-quick@wg0-fishfinger.service
+
+
+This approach provides explicit control over which gateway handles roaming client traffic, useful when one gateway needs maintenance or experiences connectivity issues.
+