Hello Word!

Published
1 Aug 2026
Updated
2 Aug 2026
Reading
5 min
Tags
example
阅读中文版本

Why I Need WireGuard

After obtaining my own ASN and IPv6 address space, I wanted to use those addresses from my home network. A normal residential connection cannot provide me with a BGP session, however, and my ISP, Vocus Retail, does not currently assign IPv6 addresses on this service. It does at least provide a static IPv4 address, which gives me a stable endpoint for a tunnel. The practical solution is an IPv6-over-IPv4 tunnel between my home network and a server that can announce and route my prefix. After comparing several Layer 2 and Layer 3 tunnelling protocols, I chose WireGuard. WireGuard is a small, modern VPN built around public-key cryptography. It is often used to secure traffic from laptops and phones on untrusted hotel or café networks, but the same peer-to-peer model also works well as a clean transport for routed IPv6. Compared with OpenVPN or IPsec deployments based on certificate infrastructure, WireGuard has a deliberately small configuration surface. Each peer has a private key, a public key, a set of permitted addresses, and an endpoint when one is needed. That simplicity makes the tunnel easier to understand, automate, and recover when something goes wrong.

Setting Up the Server

The following steps assume that the server already has working Internet access and that the routing preparation described in the previous article has been completed.

Install WireGuard and generate a key pair

First update the package index and install WireGuard:

sudo apt-get update
sudo apt-get install wireguard

Create the WireGuard directory with restrictive permissions, then generate the server key pair:

sudo install -m 0700 -d /etc/wireguard
umask 077
wg genkey | sudo tee /etc/wireguard/server_private_key   | wg pubkey | sudo tee /etc/wireguard/server_public_key

The private key must remain on the server. The public key is the value that will later be copied into each peer configuration.

Choose the tunnel address ranges

Before writing the server configuration, the tunnel needs an internal IPv4 range and an internal IPv6 range. These addresses identify the WireGuard interfaces themselves; they are separate from the public IPv6 prefix that the server routes through the tunnel.

Choose a private IPv4 range

RFC 1918 reserves three IPv4 blocks for private networks:

  • 10.0.0.0/8
  • 172.16.0.0/12
  • 192.168.0.0/16 For this example I use 10.8.0.0/24. It provides more addresses than this tunnel is likely to need and is less likely to overlap with the common 192.168.0.0/24 networks found in homes and hotels. The server uses 10.8.0.1/24. Peers can use 10.8.0.2/24, 10.8.0.3/24, and so on. If 10.8.0.0/24 already exists anywhere the tunnel must reach, choose a different private block and record it before continuing.

Generate a private IPv6 range

For the internal IPv6 side of the tunnel, use a Unique Local Address prefix from fd00::/8. RFC 4193 describes how locally assigned prefixes can be generated with enough entropy to make accidental collisions very unlikely. One simple input is a high-resolution timestamp:

date +%s%N

Example output:

1628101352127592197

Combine that value with a stable, machine-specific identifier:

cat /var/lib/dbus/machine-id

Example output:

20086c25853947c7aeee2ca1ea849d7d

Hash the combined values:

printf '%s' '162810135212759219720086c25853947c7aeee2ca1ea849d7d' | sha1sum

Then take the final 40 bits—the last ten hexadecimal characters—of the hash:

printf '%s' '4f267c51857d6dc93a0bca107bca2f0d86fac3bc' | cut -c 31-

Example output:

0d86fac3bc

Group those bytes beneath the fd prefix to produce a /48, then choose a /64 subnet for the WireGuard link. With the example bytes, the tunnel prefix is:

fd0d:86fa:c3bc::/64

The server can use fd0d:86fa:c3bc::1/64, with peers incrementing the final value: fd0d:86fa:c3bc::2/64, fd0d:86fa:c3bc::3/64, and so forth. These are private tunnel addresses. My public IPv6 allocation is routed separately through the WireGuard peer and should not be confused with this ULA range.

Create the server configuration

Create /etc/wireguard/wg0.conf:

[Interface]
PrivateKey = <SERVER_PRIVATE_KEY>
Address = 10.8.0.1/24
Address = fd0d:86fa:c3bc::1/64
ListenPort = 51820
SaveConfig = true

PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostUp = ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -A FORWARD -o wg0 -j ACCEPT; ip6tables -t nat -A POSTROUTING -j SNAT --to-source <YOUR_PUBLIC_IPV6_ADDRESS>

PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
PostDown = ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -D FORWARD -o wg0 -j ACCEPT; ip6tables -t nat -D POSTROUTING -j SNAT --to-source <YOUR_PUBLIC_IPV6_ADDRESS>

This file defines:

  • The server private key.
  • The internal IPv4 and IPv6 addresses assigned to the tunnel.
  • The UDP port used for incoming WireGuard handshakes.
  • Forwarding and NAT rules for IPv4 and IPv6 traffic.
  • Cleanup commands that remove those rules when the interface stops. The PostUp commands run when wg0 starts. They permit forwarding between the tunnel and the rest of the server, then apply the required source-address translation. The matching PostDown commands remove the same rules when the interface is stopped. Replace <SERVER_PRIVATE_KEY> with the server's actual private key and <YOUR_PUBLIC_IPV6_ADDRESS> with the public IPv6 source address appropriate for the routed prefix. Also replace eth0 if the server's public network interface has a different name. Before enabling the tunnel, review these firewall rules against the server's existing nftables or iptables policy. A production deployment should also enable kernel forwarding explicitly, restrict each peer's AllowedIPs, and verify that the public prefix is routed to the WireGuard server correctly. To be continued…

Bojin Li

Writes about software, systems, and the parts that are still uneven.