I have smartOS server at home with few virtual machines, and was thinking that centralized AA server would be a good thing to have... FreeRadius seemed to be a good solution for my needs, and there are few guides online for integrating yubikey OTP authentication.

But of course, it didn't go very well...

First things first, I had to install freeradius on KVM virtual machine, because of issues with binding (?) freeradius daemon to ethernet interface when I tried LX virtual machine. Here is JSON for this VM:

{
  "brand": "kvm",
  "autoboot": true,
  "alias": "freeradius",
  "hostname": "freeradius",
  "dns_domain": "freeradius.domain_suffix",
  "resolvers": [
    "ip.addres.of.resolver"
  ],
  "ram": 384,
  "cpu_type": "host",
  "nics": [
    {
      "nic_tag": "admin",
      "ip": "ip.address.of.vm",
      "netmask": "255.255.255.0",
      "gateway": "ip.address.of.gateway",
      "model": "virtio",
      "primary": true
    }
  ],
  "disks": [
    {
      "boot": true,
      "image_uuid": "643d5524-7eba-4a72-8fca-8dbb84d8d35d",
      "image_size": 10240,
      "model": "virtio"
    }
  ],
  "customer_metadata": {},
  "internal_metadata": {}
}

Image in use is ubuntu-certified-14.04 with build date 20160627.

After creating VM, next step is to install pacakges.

sudo apt-get install postgresql-9.3 postgresql-contrib freeradius freeradius-yubikey freeradius-postgresql libyubikey0

After installing follow official guide, to create basic configuration and get it going.

Since I like to use database backend, I set up PostgreSQL database and schema following official SQL-HOWTO.

So, up until now you should have working instance of freeradius server, and testing with radtest is successful. If not, go back and check every step!


Now, here goes sugar on top! I wanted to have username/password auth, but password would be OTP key generated by yubikey. You can, and will, find few tutorials on using freeradius and yubikey OTP with pam, but I found that to be too complicated and inconsistent, and I didn't won't to create users on system, because backup would be little complicated and another auth system to maintain. All I wanted was to authenticate users by username and OTP...

Below are changes to working freeradius server that are needed to implement yubikey OTP....

First thing to change is /etc/freeradius/mods-available/yubikey (read comments in file, they are helpful)

yubikey {
        id_length = 12
        split = yes
        decrypt = no
        validate = yes
        validation {
          #
          #  URL of validation server, multiple URL config items may be used
          #  to list multiple servers.
          #
          # - %d is a placeholder for public ID of the token
          # - %s is a placeholder for the token string itself
          #
          #  If no URLs are listed, will default to the default URLs in the
          #  ykclient library, which point to the yubico validation servers
                servers {
                }
                client_id = YOUR CLIENT ID
                api_key = 'YOUR API KEY'
                pool {
                        start = ${thread[pool].start_servers}
                        min = ${thread[pool].min_spare_servers}
                        max = ${thread[pool].max_servers}
                        uses = 0
                        retry_delay = 30
                        lifetime = 0
                        idle_timeout = 60
                        spread = yes
                }
        }
}

and enable that module

ln -s /etc/freeradius/mods-available/yubikey /etc/freeradius/mods-enabled/yubikey

Next file to change is /etc/freeradius/sites-enabled/default:

in auth code block add folowing as last entry:

yubikey
if (ok) {
    update control {
        Auth-Type := yubikey
    }
}

This will try to parse password to check does it contains yubiOTP value, and if it does, it will update Auth-Type to include yubikey.

Next one is authenticate code block. This one is most important, and it took me some time to figure it out...

Auth-Type yubikey {
    yubikey
    update request {
        User-Password := Yubikey-Public-ID
    }
    pap
}

OK, some explanation. What this says is that freeradius should first check with yubikey module is the authentication valid. And if it is valid, it will update User-Password field in request with yubikey public ID (first 12 chars of OTP that never changes), and check users authentication with pap module, which will check for user either in users file or in database table radcheck. Because, if you don't put any module after yubikey, it will authenticate any user with any valid yubikey. Of course, you can put ldap, pam or what ever you want instead of pap module.

Now, you need to insert user data, either in users file or in database, or any other backend you would like to use. In my case, I inserted this into postgresql database:

INSERT INTO radcheck (username, attribute, op, value) VALUES ('username', 'Cleartext-Password',  ':=', 'Yubikey-Public-ID');

In short, you define user record with Yubikey-Public-ID as password.

And that's it!

In few days, I'll try to install local yubico OTP validation server, so that I don't need access to internet just for local AA proces.