Puppet4 (puppetserver) autosigning certificates

Daniel Schutterop
17-12-2015

In my work environment, we auto-sign puppet certificates. Well actually, I haven’t found an environment where puppet certificates are not signed automatically, based on at least the autosign.conf domain rules.

Since this is quite the security flaw, I’ve tried a different approach when deploying the shiny and new Puppetserver (currently at 2.2.1).

It’s now possible to use Policy Based Autosigning to ensure the validity of the machine that’s trying to get its certificate signed. How you ask? By giving free reign to the puppetmaster on duty.

By enabling Policy Based Autosigning (this link), you can write any code you like to validate the machine, as long as the exit code of a valid machine (as in: sign this certificate) is 0 and every other failure code is > 0.

In any case, when enabling PBA in your puppet config (usually /etc/puppetlabs/puppet/puppet.conf) you pass the executable script as the value for key “autosign”. Just make sure the script you use is actually executable by the user running the puppet server. When a new machine connects itself, its certname (FQDN of the host) is passed to the script as an argument, as well as the PEM-encoded CSR (which I don’t use and completely ignore in this case).

I check my ENC Foreman (Have you checked out Foreman yet? It’s a great Lifecycle Management Tool, or suite.. or.. whatever..) to see if a host with this hostname exists, and if it does I check if it is actually in ‘build’ mode. Since I always do a puppet run prior to curling a buildflag-complete to Foreman, I know I can autosign any request coming from a host that’s currently being built.

Anyway, enough words, here’s some code.

#!/usr/bin/env python3
#
# === Synopsis
# Very basic script to autosign certificates in Puppet
# using the ENC (Foreman) provisioning status.
#
# I use this as an alternative to the puppet native autosign.conf
# With the new Puppet server versions, it's possible to use a
# script (or executable) to replace the autosign.conf which in turn
# validates the auto signage of nodes by a list (mostly wildcards in
# my case I must shamefully admit)...
#
# Using the FQDN of the registrating host as the only command-line
# argument I grab the 'build' status of the host in Foreman.
# If the host is building, I can assume (moafu) that a new registration
# in puppet is valid.
#
# === Arguments
#  This script just takes the FQDN of the host as argument
#
# === Exit
# Signing takes place based on the exit codes:
# exit(0) signs the cert
# Any other exit code doesn't. Easy enough, right?
#
# === Built using:
#  - Foreman : 1.9.3
#  - Puppet  : puppetserver-2.2.1-1
#  - Python  : 3
#
# === About
# Author  : D. Schutterop
# Version : v0.1

import json,requests,time,socket,sys

frmHost = 'foreman.localdomain'
frmUser = 'read_only_user'
frmPass = 'myWickedAndSaf3Password01'

#Suppress SSL warnings generated when contacting Foreman
requests.packages.urllib3.disable_warnings()

def getFrmHostID(tgtHostName):
  frmUrl     = "https://%s/api/v2/hosts/%s" % (frmHost,tgtHostName)
  frmHeaders = {'Content-type': 'application/json'}
  frmRequest = requests.get(frmUrl, verify=False, auth=(frmUser,frmPass), headers=frmHeaders)

  frmValue = json.loads(frmRequest.text)

  if not 'error' in frmValue:
    if (frmValue['build'] == True):
      print ("The certificate for %s will be auto signed." % tgtHostName)
      return 0
    else:
      print ("%s is not in build mode (Foreman) and will not be auto signed." % tgtHostName)
      return 99
  else:
    print ("%s does not exist (in Foreman) and will not be auto signed." % tgtHostName)
    return 99

if len(sys.argv) == 1:
  print ('Argument needed (FQDN)')
  exit(99)

if len(sys.argv) > 2:
  print ('Too many arguments, 1 argument expected')
  exit(99)

exit(getFrmHostID(sys.argv[1]))

The only reason I did this in Python3 is, well, I try to ditch 2.7 by now, it’s been long enough…

As I said, puppetserver bases its actions purely on the exit code of the script, yet I still printed a few lines on success and error. This is mainly because these lines get printed when the Puppet server is running in debug mode.

Anyway, small snippet of code, just a tiny bit of extra security.

LEAVE A REPLY

you might also like