Setting up IPv6 on Linode with nginx

So Linode has gotten onto the IPv6 bandwagon. In addition to each Linode getting an IPv6 address, they will also gladly assign an entire /64 netblock to your specific node on request. That gives you all sorts of flexibility for bringing up IPv6 services. And this blog post is going to be all about how to make use of multiple IPv6 addresses.

As of this writing, everything I mention will work on a Linode running Ubuntu 12.04 LTS and Nginx 1.1. There’s no reason that these instructions shouldn’t work on other distros or versions of Ubuntu with some modifications.

Setting An IPv6 Address

Warning: You’re playing around with networking settings. If something goes wrong, your Linode could find itself unreachable from the network and you’ll need to reboot it. And that’s not fun. I strongly recommend proceeding through the LISH console so that you can undo any mistakes you make. 🙂

To start with, send a support request to Linde and get your /64 assigned. For the purposes of this post, let’s assume you get the subnet 2600:3000:4000:1000/64. That will give you a range of 2^64 addresses from 2600:3000:4000:1000:0:0:0:0 to 2600:3000:4000:1000:ffff:ffff:ffff:ffff. That should be plenty of addresses to work with. 

To bring up one of those addresses, run this command:

ip -6 addr add 2600:3000:4000:1000::100/64 dev eth0

The address should now show up when you run ip -6 addr show eth0, and you should be able to use the ping6 utility to ping 2600:3000:4000:1000::100. If ping6 is not working, then something is wrong.

$ ip -6 addr show eth0
3: eth0: <broadcast,multicast,up,lower_up> mtu 1500 qlen 1000
    inet6 2600:3000:4000:1000::100/64 scope global 
       valid_lft forever preferred_lft forever
</broadcast,multicast,up,lower_up>

Keeping Your IPv6 Address Between Reboots

Having an IPv6 address is nice, but we want to keep it between reboots as well. The way to do that is to modify the file /etc/network/interfaces. Edit that file, and change it to this:

auto eth0
iface eth0 inet dhcp
    up /sbin/ip -6 addr add 2600:3000:4000:1000:100/64 dev eth0
    down /sbin/ip -6 addr del 2600:3000:4000:1000:100/64 dev eth0

One way to test this is by rebooting the machine. A slightly safer way to test for IPv6 address persistence is by bringing the interface down and then back up, as follows:

ip link set eth0 down
ip link set eth0 up

If you didn’t do this from the console like I suggested higher up, you’re going to have a bad time. 

If you use Chef to manage your systems, I wrote a cookbook that does this. You can find it over on GitHub. (Not familiar with Chef? You might want to read my introduction to Chef!)

Configuring Nginx

Now that we have a machine with 1 or more IPv6 addresses, we need to tell nginx to listen on IPv6, which it does not do by default. First, make sure your version of nginx has IPv6 support compiled in. Run nginx -V and look for the string –with-ipv6 in the output.

Once you’ve verified that nginx supports IPv6, edit /etc/nginx/sites-enables/default and add the following lines inside the “server” block:

listen 80 default;
listen [::]:80 default ipv6only=on;

Now, for each website that you want to respond to IPv6 requests (ideally, all of them!), the following directives need to go into the “server” block:

listen 80;   
listen [::]:80;

If you want each website to listen on a separate IPv6 address (which is really a good idea…), you can specify a single IP address to listen on in the “server” block as follows:

listen [2600:3000:4000:1000:101]:80;

I’m not entirely clear why the same directives need to be listed twice, but it’s the configuration that works for me.

SSL Support

Doing SSL over IPv6 is virtually identical to doing SSL over IPv4. Just be sure to specify port 443 instead:

listen [2600:3000:4000:1000:101]:443;

Testing Your Nginx Configuration

Having gotten burned a few times by having certain webservers serve up the right docroot in IPv4 but not IPv6 or vice-versa, I recommend testing your configuration.

Rather than dealing with HTML source, I recommend first creating a file called host.txt in the docroot of each webserver you want to test, and place the name of the webserver into it. In a pinch, you could also use each site’s robots.txt, but multiple sites may have similar looking robots.txt files so that method is less reliable.

Now, retrieve each file with curl as follows:

$ echo -n "Testing IPv4: "; \
   curl -4 -s www.dmuth.org/host.txt; \
   echo -n "Testing IPv6: "; \
   curl -6 -s www.dmuth.org/host.txt
Testing www.dmuth.org with IPv4...Host: dmuth.org
 
Testing www.dmuth.org with IPv6...Host: dmuth.org

As is obvious from the output, the same docroot is being served up via both IPv4 and IPv6. Success!

(Does your ISP not yet support IPv6? Try Tunnel Broker from Hurricane Electric. It’s free!)

I hope this post was helpful for anyone trying to get IPv6 up and running on Linode or anywhere else. Have comments or questions? Feel free to leave a note below or reach out to me.