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:
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
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
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.
Watzmann.Blog by David Lutterkort is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.
Generated with Jekyll