February 15, 2011

Gentoo and DHCPv6

Amongst playing with the YubiKey, I also had a look at DHCPv6.  As people well know, IPv4’s days are numbered, and given we’re all going to have to jump across to IPv6 fairly soon, I figured I had better get acquainted with the newer protocols that come with it.

I’ve had my network running dual-stack for some time now.  This has been achieved using stateless autoconfiguration and router advertisments, which work fairly well.  Today though I decided I’d give DHCPv6 a crack.  For this, you will need the latest ISC dhcp package, net-misc/dhcp-4.1.0, which is hard-masked.

I hope to get something more mature going, but here are some notes who may wish to try this at home.

Setting up DHCPv6

Start by installing the net-misc/dhcp-4.1.0, on both server and clients…you will need to unmask it first:

# echo =net-misc/dhcp-4.1.0 | tee -a /etc/portage/package.unmask >> /etc/portage/package.keywords
# emerge -a dhcp

The -4 and -6 flags

Now, that will install the DHCP server and client.  The catch that initially caught me is that ISC dhcpd cannot run on IPv6 and IPv4 simultaneously. Neither can the client, but we’ll get to that.  Both server and client are put into IPv4 mode by running with -4 in the options, or -6 for IPv6 mode.  Documentation says it defaults to IPv6 mode, but my experience has been the opposite (maybe a Gentoo patch does this).

Server set-up

Needless to say, if you’ve got your network running IPv4, at most you might have to edit /etc/conf.d/dhcpd to add -4 to the start-up options (DHCPD_OPTS to be exact).  Easy.  It’ll work as before. If you want IPv6, okay, make that -6 in DHCPD_OPTS, no sweat. But what if you want both? Ohh fun, we need a second dhcpd instance.

My solution; copy each /etc/init.d/dhcpd and /etc/conf.d/dhcpd to /etc/init.d/dhcpd-v6 and /etc/conf.d/dhcpd-v6 respectively.  Make the necessary changes to /etc/init.d/dhcpd-v6 and /etc/conf.d/dhcpd-v6.  So that the two don’t clash, I thought it wise to substitute DHCPD with DHCPDV6 using a text-editor (replace all).

You’ll also want to rename the leases file (dhcpd.leases, I chose dhcpd-v6.leases) and the PID file.  In addition the init script calls dhcpd to check the configuration in checkconfig(), so add -6 there too.  To save you going back and adding it in /etc/conf.d/dhcpd-v6, you can also add the -6 flag to the start-stop-daemon call in in start().  Do similar manipulations to /etc/conf.d/dhcpd-v6.

As for the server configuration file itself, I called my IPv6 config file /etc/dhcp/dhcpd-v6.conf to differentiate it from v4.  The two will need separate configuration files.  At the top of the v6 configuration file, you’ll want to point it to new PID and leases files:

pid-file-name "/var/run/dhcp/dhcpd-v6.pid";
lease-file-name "/var/lib/dhcp/dhcpd-v6.leases";

Adding that to the top of dhcpd-v6.conf will take care of this.  If you’ve done everything right, you should be able to start the DHCPv6 daemon, and add it to your runlevels as per normal.  DHCPv6 listens on port 547/UDP — look for it in netstat.

Client set-up

Client set-up isn’t too difficult, the fun bit is integrating dhclient into the init scripts.  OpenRC knows how to drive dhclient in v4-mode, but not v6.  It too, cannot run in both v4 and v6 mode simultaneously.  The solution: a new net module.  Copy /lib/rc/net/dhclient.sh to /lib/rc/net/dhclientv6.sh.

Rename all the functions changing dhclient to dhclientv6 (don’t use replace-all this time), and change the “provide” line in dhclientv6_depend to dhcpv6.  In dhcpclientv6_expose, do likewise, rename the variables dhclientv6 and dhcpv6.  Finally,  in each  call to the dhclient binary itself, add -6 to it to put it in IPv6 mode, and rename the PID file to add -v6 to the file name.

Save the new file.  Now in /etc/conf.d/net, use the following:

config_eth0=( "dhcp" "dhcpv6" )

Things that I have not yet figured out

Dynamic DNS

This is one of the reasons why I wanted DHCPv6 in the first place.  Remembering IPv4 addresses is bad enough.  IPv6 is a pain.  I have more success citing Pi than remembering the IPv6 address of all my computers.  The statically assigned ones aren’t too bad since the prefixes are all the same, it’s the autoconfigured ones that are a nuisance.

It should be doable, but I haven’t yet worked out how to make dhcp update the nameserver with AAAA records.  This is still in its infancy though.  Lots of rough edges.  I note dhclient doesn’t seem to be passing on the hostname of the computer, which could be part of the problem.

Address pools and class-based assignment

Address pools are handy things.  In ISC dhcpd, I can classify each of the computers I have by their MAC address and assign each class an address pool.  Or at least I could when it’s in IPv4 mode.

I have a nice set-up on IPv4 where if the DHCP server knows the MAC address, it’ll put that computer on the right subnet.  We have three IPv4 subnets here; one for my computers, one for my father’s and a “de-militarised zone” where any foreign computers get put (along with the web server, well actually it exists on all three).  Below is an example:

subnet netmask {
  pool {
    range dynamic-bootp;
    allow members of "stuartslan";

  ddns-domainname "redhatters.yi.org.";
  ddns-rev-domainname "in-addr.arpa.";
  ddns-updates on;
  update-conflict-detection off;
  allow client-updates;

  /* ... */
/* ... */
subclass "stuartslan" 1:6c:f0:49:ef:84:7c; # beast eth0
subclass "stuartslan" 1:6c:f0:49:ef:84:7e; # beast eth1
subclass "stuartslan" 1:08:00:27:ab:7c:b9; # Win2K VirtualBox
subclass "stuartslan" 1:08:00:27:27:bf:55; # uClibc VirtualBox
subclass "stuartslan" 1:00:08:0d:5c:08:51; # Laptop "vk4mslp2" ethernet
subclass "stuartslan" 1:00:12:f0:bd:de:06; # Laptop "vk4mslp2" wireless
/* ... */

I haven’t figured out how to replicate this in DHCPv6.  The following does not work:

subnet6 2001:388:d000:1153::/64 {
  pool {
    allow members of "stuartslan";
    range6 2001:388:d000:1153::1000 2001:388:d000:1153::ffff:ffff;

  ddns-domainname "redhatters.yi.org.";
  ddns-rev-domainname "ip6.arpa.";
  ddns-updates on;
  allow client-updates;
  /* ... */

I plan to keep researching these things, and I’ll see what I can do about getting the updates into Gentoo’s init scripts so that DHCPv6 is handled.  A lot of what I did today were quick hacks that will likely make people shudder, but it’s working for now, we’ll see how it goes.

Fun with the YubiKey

At linux.conf.au, we all got given a YubiKey each.  These are a proprietary one-time-password generator device which plugs into USB and emulates a USB HID keyboard.  Full documentation on how the algorithm works is provided by Yubico and they have also provided a lot of software for interfacing to the keys under a quite liberal BSD license. The device itself, being a USB HID device, needs no drivers other than what the operating system provides. Plug it in, press the button, and you get:


And don’t bother trying to use that, I have deliberately mangled some of the characters so it isn’t valid. (I’ve also used it in a few places since, so it’s old anyway.) The first 12-16 characters form the public ID, and are always the same, but unique for each key. The remaining 32 characters form the OTP data, and is encrypted internally using a 128-bit AES key.  The data is a variant of hexadecimal called modhex — the digits have been mapped to keycodes that should be the same on every model of keyboard.  This means the key will still work whether the computer is configured for QWERTY, QWERTZ, AZERTY, etc.  Not sure if it handles Dvorak though.

I’ve been doing a bit of tinkering with mine.  They can be used out-of-the-box with Yubico’s authentication servers for things such as OpenID.  The programming tool however, lets you define your own parameters, and use them completely stand-alone.  Yubico have a facility for uploading the key’s new AES key when you do this.  The bonus  with doing this is that you can use the same key for both stand-alone services you might set up, and for web-based services (with the caveat that it does open to replay attacks).

By the second day of the conference, I had my Yeeloong authenticating me using YubiPAM, a stand-alone PAM module. I’ve since configured my other laptop the same way, although I notice I get a buffer overflow when the authentication succeeds — not sure why as the Yeeloong works fine. I’m looking into what’s needed for Gentoo. I haven’t figured out how to get two-factor authentication to work there with KDM. I’m thinking maybe pam_python, and a homebrew solution may give me the flexibility I’m after.

Today, I had another look at it. This time, I was looking at what services I use that could make use of it. The obvious candidates: this blog, and OpenID.

On the OpenID front, I initially toyed with a copy of Yubico’s OpenID server, which is a very crude thing intended as a demo. I thought maybe I could extend it, but couldn’t figure it out. Figuring there must be a better solution, I went hunting, and found Community-ID. I managed to get Community-ID installed on my server without too much sweat, I had single-factor authentication using either a password or the YubiKey working in minutes. My instance is here, and now my devspace homepage functions as an OpenID, as does my blog.

As for two-factor authentication, I went digging for how it processed the password. Community-ID has a very strict model-view-controller structure that made things very easy.  I wasn’t sure how to go about adding a new field, but I figured, I didn’t have to.  The database stores the prefix so that it can identify who the person is logging in, and from that, I know the OTP will be the length of that prefix, plus 32 characters.  I was able to modify Community-ID to take the last strlen(prefix)+32 characters, check that using Yubico’s servers, then process the remainder and compare that against the stored password.  Bingo, two-factor authentication with one password field.  The patch is already upstream.

Now if I can make YubiPAM do this, I’ll be very happy.

For the blog, I ended up doing both.  I found a WordPress plug-in that does OpenID authentication.  At first I couldn’t figure out how to link my new OpenID identity to my existing account, so I then turned to the YubiKey and installed a plug-in that performs that task.  No sooner had I got that going, then I spotted where the fields were for associating OpenIDs with accounts, so I’ve configured that.  My blog will now accept any of the three, although using single-factor authentication (unless I use OpenID).

Guess that’s enough for a blog, it’s not like someone can lock me out of it given I have database access anyway and the password is stored as a hash.

Needless to say though, you can expect some further improvements for things using these keys.  I’ve got some other places in mind for the thing.