In IPv6 everywhere with tinc we saw how to use a tinc VPN to let a host with its own public IPv6 network provide entire subnetworks to remote devices like home computers.
The setup allows each device to have its own public IPv6 addresses to access or provide services to the IPv6 Internet. However, with such a setup the device always uses the same address for outgoing traffic, which may be awful for end-user devices from the privacy perspective, since they are very easy to be uniquely identified worldwide.
Also, the network topology diagram in that post showed that each device may
serve its /64
public IPv6 network locally, thus allowing other hosts in the
local network to also have full public IPv6. A very simple setup using IPv6
stateless address autoconfiguration (SLAAC) for such hosts, based on the IPv6
router advertisement daemon (radvd
) is explained here. However,
autoconfigured addresses have the very nasty effect of revealing the host's
MAC address, which is even worse privacy-wise since hosts become uniquely
identifiable regardless of the network they are in (see this post for a deeper
discussion).
This article explains how to easily configure a Debian IPv6 router and its hosts to use temporary IPv6 addresses (privacy extensions for SLAAC) that avoid the problems mentioned above.
Basic routing
Let's review the /etc/tinc/inet6/tinc-up
script used by non-gateway nodes as
described in the previous post:
#!/bin/sh ip -6 link set "$INTERFACE" up mtu 1400 ip -6 addr add 2002:0102:0304:0004::1/48 dev "$INTERFACE" ip -6 route add default dev "$INTERFACE"
The device has the address 2002:0102:0304:0004::1/48
added to tinc's
$INTERFACE
. It also has a default route to the IPv6 Internet on that
interface.
The first thing we need to enable for this device to become a router is IPv6 forwarding in the firewall and in the kernel:
# ip6tables -P FORWARD ACCEPT # this is usually the default case # cat > /etc/sysctl.d/local-forwarding.conf << EOF net.ipv6.conf.all.forwarding=1 EOF # sysctl -p /etc/sysctl.d/local-forwarding.conf
However the router still has no configuration for the IPv6 local network.
Assuming it's on the eth0
interface, an easy way to do it is adding a static
configuration like this in /etc/network/interfaces
:
auto eth0 # or ``allow-hotplug eth0`` or whatever is already there iface eth0 inet6 static address 2002:0102:0304:0004::1/64
Please note that it's the same address as above, with a /64
prefix instead
(the only valid prefix for IPv6 end networks). There's no need to use
additional addresses as long as different prefixes are used. Remember to put
the interface up (e.g. with service networking restart
).
That should be enough for hosts in the local network to use static
configurations like this in their /etc/network/interfaces
:
iface eth0 inet6 static address 2002:0102:0304:0004::1234/64 gateway 2002:0102:0304:0004::1
Address autoconfiguration
Instead of using a static configuration for hosts in the local network, we may use IPv6's stateless address configuration (SLAAC), which uses the Network Discovery Protocol (NDP) for hosts to choose their own address (based on the interface's MAC address), thus simplifying the whole network configuration.
To do this we install the radvd
package in the router and create a very simple
configuration file for it to announce its presence as a router in the local
network:
# apt-get install radvd # echo > /etc/radvd.conf << EOF interface eth0 { AdvSendAdvert on; prefix ::/64 {}; }; EOF # service radvd restart
Please note that the prefix
option doesn't specify the
2002:0102:0304:0004::/64
network, but just ::/64
. This is a special value
that tells radvd
to automatically announce the router for any networks in the
given interface which have a non-link-local address. Since the router's IPv6
address is already configured in eth0
(because of the static
entry in the
interfaces
file) by the time radvd
service starts, we can avoid some
repetition of addresses in configuration files.
With this in place, local network hosts just need an entry like this in their
/etc/network/interfaces
to configure their interface's IPv6:
iface eth0 inet6 auto
What if we want that host to provide a network service? It would be nice for
it to still have a fixed public address which doesn't depend on the particular
hardware. We may add that address with the pre-up
option of interfaces
, so
that it is assigned before autoconfiguration, even if this fails for some
reason:
iface eth0 inet6 auto pre-up ip addr add 2002:0102:0304:0004::1234/64 dev $IFACE preferred_lft 0
Why this preferred_lft 0
setting? It marks this static address as deprecated,
i.e. it may be used for receiving incoming connections from the outside, but
when an outgoing connection is done, other addresses will be preferred, in our
case the autoconfigured one.
Temporary addresses
As I mentioned in the introduction, autoconfigured addresses have serious privacy issues. Fortunately, SLAAC and NDP support the use of temporary addresses which are generated randomly and discarded after a while. To use these addresses on autoconfiguration instead of the MAC-based ones, we need to first enable them in the kernel of local network hosts:
# cat > /etc/sysctl.d/local-ipv6-privacy.conf << EOF net.ipv6.conf.all.use_tempaddr = 2 net.ipv6.conf.default.use_tempaddr = 2 EOF # sysctl -p /etc/sysctl.d/local-ipv6-privacy.conf
We also need to add the privext 2
setting in the /etc/network/interfaces
entry:
iface eth0 inet6 auto privext 2
After restarting the host's network, you may see that running ip -6 addr show
dev eth0
lists some temporary
addresses: old addresses are kept for a while to
receive pending traffic and they end up going away.
We can also make the router enjoy temporary addresses for outgoing
connections. Its interfaces
entry should be similar to this one:
iface eth0 inet6 auto pre-up ip addr add 2002:0102:0304:0004::1/64 dev $IFACE preferred_lft 0 privext 2
We must remember to also add the preferred_lft 0
setting to the ip -6 addr
add...
command in the tinc-up
script and restart tinc, otherwise we'll end up
using that address for outgoing connections!
The configuration above seems to introduce a chicken-and-egg problem. How can
we autoconfigure eth0
in the router when radvd
is not running there yet? How
does radvd
know what networks to announce when the interface isn't yet
configured? The loop-breaker here is the pre-up
command, which sets the
network before the interface is configured. Autoconfiguration keeps running
in the background, so that when radvd
starts, it can see that network, start
and thus the autoconfiguration is completed.
This is all we need to do to have a public IPv6 local network for all our hosts. It wasn't that difficult, was it?
A note on my particular setup
I have a laptop with the whole tinc + radvd
+ privacy extensions enabled.
However, since its Ethernet port may be plugged into untrusted networks, I'm
not comfortable with running radvd
on eth0
and having unknown hosts access the
IPv6 Internet through the laptop. I still want to use temporary IPv6
addresses for my browsing, though. What to do, then?
The solution I found was to create a dummy interface and have radvd
run on it.
I just added dummy
to my /etc/modules
, changed eth0
by dummy0
in
/etc/radvd.conf
, and used this interfaces
entry:
auto dummy0 iface dummy0 inet6 auto pre-up ip link set multicast on dev $IFACE pre-up ip addr add X:Y:Z:T::1/64 dev $IFACE preferred_lft 0 privext 2
The first pre-up
command is needed since multicasting is not enabled by
default on dummy interfaces, and radvd 2
refuses to announce on interfaces
without multicast support (see radvd's issue #46).