Using Puppet's policy-based autosigning

13 June 2014

Handling SSL certificates is not a lot of fun, and while Puppet’s use of client certificates protects the server and all its deep, dark secrets very well from rogue clients, it also leads to a lot of frustration. In many cases, users would configure their autosign.conf to allow any (or almost any) client’s certificate to be signed automatically, which isn’t exactly great for security. Since Puppet 3.4.0, it is possible to use policy-based autosigning to have much more control over autosigning, and to do that in a much more secure manner than the old autosigning based solely on client’s hostnames.

One of the uses for this is automatically providing certificates to instances in EC2. Chris Barker wrote a nice module, based on a gist by Jeremy Bouse that uses policy-based autosigning to provide EC2 instances with certificates, based on their instance_id.

I recently got curious, and wanted to use that same mechanism but with preshared keys. Here’s a quick step-by-step guide of what I had to do:

The autosign script

When you set autosign in puppet.conf to point at a script, Puppet will call that script every time a client request a certificate with the client’s certname as the sole command line argument of the script and the CSR on stdin. If the script exits successfully, Puppet will sign the certificate, and refuse to sign it otherwise.

On the master, we’ll maintain a directory /etc/puppet/autosign/psk; files in that directory must have the certname of the client and contain the preshared key.

Here is the autosign-psk script; the OID’s for Puppet-specific certificate extensions can be found here:

#! /bin/bash

PSK_DIR=/etc/puppet/autosign/psk

csr=$(< /dev/stdin)
certname=$1

# Get the certificate extension with OID $1 from the csr
function extension {
  echo "$csr" | openssl req -noout -text | fgrep -A1 "$1" | tail -n 1 \
      | sed -e 's/^ *//;s/ *$//'
}

psk=$(extension '1.3.6.1.4.1.34380.1.1.4')

echo "autosign $1 with PSK $psk"

psk_file=$PSK_DIR/$certname
if [ -f "$psk_file" ]; then
    if grep -q "$psk" "$psk_file"; then
        exit 0
    else
        echo "File for '$psk' does not contain '$certname'"
        exit 1
    fi
else
    echo "Could not find PSK file for $certname"
    exit 1
fi

Puppet master setup

On the Puppet master, we put the above script into /usr/local/bin/autosign-psk, make it world-executable, and point autosign at it:

cp somewhere/autosign-psk /usr/local/bin
chmod a+x /usr/local/bin/autosign-psk
mkdir -p /etc/puppet/autosign/psk
puppet config set --section master autosign /usr/local/bin/autosign-psk

A PSK for client $clientname can easily be generated with

tr -cd 'a-f0-9' < /dev/urandom | head -c 32 >/etc/puppet/autosign/psk/$certname

Puppet agent setup

On the agent, we create the file /etc/puppet/csr_attributes.yaml with the PSK in it:

---
extension_requests:
  pp_preshared_key: @the_psk@

With all that in place, we can now run the Puppet agent and have it get its certificate automatically; that process is as secure as we keep the preshared key.

Creative Commons License Watzmann.Blog by David Lutterkort is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.

Generated with Jekyll