In celebration of World IPv6 Day—OK, not really—I managed to get the Linode running this site and a few other things working on IPv6. I thought I'd point out a few notes, problems I ran into, and all that fun stuff to save some other people any frustration. Most of these tips will be Linux-specific given the nature of IP stacks, so don't go blaming me for your BSD troubles.

IPv6 autoconfiguration is pretty awesome by the way- I told Linode I wanted an IPv6 address, they rebooted my VPS to do something with the MAC address, and the address is just there, no config needed.

Update: I made a few small fixes to things, notably the nginx config so that it listens on 127.0.0.1 as well as ::1, as well as added the section regarding Django.

Good to Go

Linode DNS

A few clicks and typing away and I had my AAAA records without much trouble. Just know that you can't enter the address with a netmask (e.g. 2600:3c00::f03c:91ff:fe96:cbca/64).

$ host toofishes.net
toofishes.net has address 173.255.192.10
toofishes.net has IPv6 address 2600:3c00::f03c:91ff:fe96:cbca

Ip6tables

Most of your existing firewall rules should "just work". Obviously there are a few exceptions:

  • Any rules with source (-s) or destination (-d) addresses. Either let your IPv4 setup take care of it, or if you have new hosts with IPv6 addresses to make rules for, add some new ones.
  • -p icmp-p icmpv6
  • --reject-with icmp-port-unreachable--reject-with icmp6-port-unreachable
  • --reject-with icmp-proto-unreachable doesn't exist; just using the default REJECT type works fine (dropping the --reject-with override).

Nginx

This was where I spent the most time, but not really for good reason. I just got a bit confused by the documentation and what to do to ensure servers were listening on both v4 and v6 addresses.

The bit about my setup I struggled with was having one vhost (server) listening to localhost only, while the rest listened on all addresses. Reading the listen directive documentation, I thought I might need to use the ipv6only=on directive. However, that lead to nothing but trouble. Someone in IRC said the helpful bit that using ipv6only=on is the same as using bind, which after reading through all the documentation explained my problems.

Anyway, here is an very bare snippet for having multiple servers, one of which listens only on localhost on both address families, and the other on all interfaces. Note that to get a vhost to listen to both localhost addresses (127.0.0.1 and ::1), you need the crazy mapping address specified which is documented well in RFC 4291, 2.5.5. IPv6 Addresses with Embedded IPv4 Addresses.

http {
    server {
        listen       [::1]:80;
        # Linux will listen on 127.0.0.1:80 as well, given this
        listen       [::ffff:7f00:1]:80;
        server_name  localhost;
    }

    server {
        listen       [::]:80;
        listen       [::]:443 ssl;
        server_name  example.com;
    }

    server {
        listen       [::]:80 default_server;
        listen       [::]:443 ssl;
        server_name  www.example.com;
    }
}

Xinetd

Out of the box (at least on Arch Linux), xinetd does not bind to an IPv6 address. It is as simple as adding the below bind line to your defaults in the root config file. Once I did this, my git-daemon install had no problems listening over IPv6.

defaults
{
    bind = ::
    ...
}

OpenSSH

Just make sure you have both IPv4 and IPv6 addresses enabled:

#AddressFamily any
ListenAddress 0.0.0.0
ListenAddress ::

NTP

Just worked out of the box, judging from the log messages, which showed it listening on all IPv6 scoped addresses (Global, Link, and Host).

Not Ready for Primetime

OpenVPN

Support for listening to and tunneling over IPv6 is coming in the 2.3 release after this commit and finding this bug report, but I'm not quite sure when that is coming out.

Django

This is a disappointment, but unfortunately is nothing new given the glacial pace they slog through bugs and features on their tracker. For sake of backing up this statement, tell me what more I need to do to push my two bugs along.

The bad news: The IPv6 Address Field ticket, opened six years ago, is still open and stalled in bureaucracy. Too bad if you want to have a field that supports all addresses in your code; you'll have to write it yourself. I already had to roll my own field to support netmasks in the archweb project.

The good news: I had someone leave a comment on this very post yesterday, they accessed the site via IPv6, and the address was stored correctly anyway. So the built-in comments application supports addresses without trying because no validation is done except at the form level. I'm guessing the admin won't let me edit this comment though. However, I am using PostgreSQL which has a native inet type supporting both IPv4 and IPv6 addresses Your luck may vary using something like MySQL which stores IP addresses in a limited-length char field, much too short for all valid IPv6 addresses.