Kickstart: Securely save your Rudder/CFEngine's agent keys during installation

Reinstalling a server, be it for testing or upgrade, I'm sure you've had the need to.
If you use Rudder/CFEngine, you know you need to take care of those little agent keys if you don't want to have problems with your configuration management. Indeed you could end up deleting your node in your Rudder interface, but this is taking you a bit away of a fully automated process isn't it?
I've noted that the less I mess up with my agent keys, the less I need to do manual tasks, so it would be nice if I could ensure a server keeps the same agent key for as long as it exists.

Now the main issue is that the kickstart file concerning a server is publicly available as it must be fetched by the machine when it starts its installation - One could try to put some address restrictions in place, but I felt like it would be a burden... I'm not sure I'll be on the correct subnet next time I need to verify a Kickstart file for a server - that means that the saving process will be in clear for everyone that gets access to the kickstart file.
Having someone getting access to a server agent key could have dramatic results, so the saving process must be as secure as possible.

To summarize:

  • Must be easy to save/retrieve the key during Kickstart process - No need to fire up a restore from a backup agent or anything, it's fully automated
  • Saving process logic is in clear for everyone
  • Secure enough that anyone can't just retrieve your server key
  • Server identifier must be unique so that keys can be retrieved easily across reinstallations
  • Lowest dependancy possible during Kickstart - curl, sha512sum
  • It's fine for Root user to be able to retrieve the saved server keys - If you're root on the server you can already access its CFEngine keys...

I've decided to use the system-uuid that is contained in /sys/class/dmi/id/product_uuid. You can find it as well using dmidecode or lshw as Root.
Mind you that depending of your setup, this may not work for you - especially on VMs where you could get duplicates if they're not centrally managed.
Warning: Older distributions that still use HAL may let this information leak to non privileged users through lshal. It requires HAL to be installed and the service to be started though ... Beware of default installations!

Now that I have my system UUID, I'll make a hash of it - a long one - and upload/retrieve my keys from an HTTP server using my hash as filename/prefix.
Why over HTTP ? Simply because it requires nothing else than curl to do so, and that it can be hosted pretty much anywhere you want. For that very specific need, I've made a tiny HTTP file server that only does:

  • Stores a file uploaded by its name
  • Cannot overwrite a file that already exists
  • Download a file by its name
  • Returns 404 if filename is unknown

Please find below the Cobbler snippet that will do just that.

#set $rudder_cf_path    = '/var/rudder/CFEngine-community'
#set $rudder_opt_path   = '/opt/rudder'
#set $rudder_keys_path  = $rudder_cf_path + '/ppkeys'

# Generate server unique hash
SRV_UUID=\$(cat /sys/class/dmi/id/product_uuid)
SRV_HASH=\$(echo -n \$SRV_UUID | sha512sum | cut -f 1 -d " ")

# Check if our hash exist
curl -X GET https://$http_store:8080/get/\$SRV_HASH.pub > /tmp/localhost.pub
grep "404 page not found" /tmp/localhost.pub &> /dev/null
RET=\$?

# Process result
if [ \$RET == 0 ]; then
    # Keys doesn't exist upload them
    curl -s -X POST https://$http_store:8080/add/\$SRV_HASH.priv -F "file=@$rudder_keys_path/localhost.priv"
    curl -s -X POST https://$http_store:8080/add/\$SRV_HASH.pub -F "file=@$rudder_keys_path/localhost.pub"
    curl -s -X POST https://$http_store:8080/add/\$SRV_HASH.hive -F "file=@$rudder_opt_path/etc/uuid.hive"
else
    # They seem to exist download them 
    curl -s -X GET https://$http_store:8080/get/\$SRV_HASH.priv > $rudder_keys_path/localhost.priv
    curl -s -X GET https://$http_store:8080/get/\$SRV_HASH.pub > $rudder_keys_path/localhost.pub
    curl -s -X GET https://$http_store:8080/get/\$SRV_HASH.hive > $rudder_opt_path/etc/uuid.hive
fi

So with this code you generate a hash that is never printed anywhere in the kickstart file nor on the installed system but only gets saved/retrieved remotely on/from a HTTP file server.
This example uses Rudder default path but by using Cobbler you could easily set the path based on a profil ksmeta to make it available for CFEngine as well.

#set $ppkeys_path = $getVar('$ppkeys_path', '/var/lib/cfengine/ppkeys')

The code will store files looking like this on your HTTP file server:

0bc29c7d604b38c14c42b495c075e003e2b1cc36f92d60ad70eb9c5c2408175ff72d6038dfca6a2a1fe1a8c4032642650295a4bc7dab8d57177942092c83e78b.hive
0bc29c7d604b38c14c42b495c075e003e2b1cc36f92d60ad70eb9c5c2408175ff72d6038dfca6a2a1fe1a8c4032642650295a4bc7dab8d57177942092c83e78b.priv
0bc29c7d604b38c14c42b495c075e003e2b1cc36f92d60ad70eb9c5c2408175ff72d6038dfca6a2a1fe1a8c4032642650295a4bc7dab8d57177942092c83e78b.pub
906e84086789e32421a45622286e096a735c36cd636934bc91dcb8c280a8b4424958452903199bb133330721402fb12bbf175c78ea706d4f54d089dc15567863.hive
906e84086789e32421a45622286e096a735c36cd636934bc91dcb8c280a8b4424958452903199bb133330721402fb12bbf175c78ea706d4f54d089dc15567863.priv
906e84086789e32421a45622286e096a735c36cd636934bc91dcb8c280a8b4424958452903199bb133330721402fb12bbf175c78ea706d4f54d089dc15567863.pub

So that even you shouldn't know which file comes from which server.

Now your reinstallation should be as simple as wiping your disk boot sectors and reboot.

As usual, feel free to contact me if you have any question!