Skip to content

Instantly share code, notes, and snippets.

@henrydobson
Last active October 30, 2021 00:27
Show Gist options
  • Save henrydobson/edaa07827a31d3a653e7dd1b2139fb7e to your computer and use it in GitHub Desktop.
Save henrydobson/edaa07827a31d3a653e7dd1b2139fb7e to your computer and use it in GitHub Desktop.
A Puppet Server autosign script, which includes some whitelisted certnames, CSR attribute evaluation and Duo push auth via the auth API. Credit to nmcspadden who's script https://github.com/macadmins/docker-puppetmaster-whdcli/blob/master/check_csr.py helped greatly for the CSR eval portion of this script. Big thanks to futureimperfect for guida…
#!/usr/bin/env python
import os, sys, time, subprocess, logging
import urllib, duo_client
### Puppet VARS ###
###################
CERTNAME = sys.argv[1]
CSR = sys.stdin.read()
### DUO VARS ###
################
### Enter your own values ###
DUO_API_HOSTNAME = ''
DUO_INTEGRATION_KEY = ''
DUO_SECRET_KEY = ''
DUO_USERNAME = ''
### Setup logging ###
#####################
LOG_FILENAME = '/var/log/check_csr.log'
logging.basicConfig(filename=LOG_FILENAME, level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info('Start script')
logger.debug("Number of arguments: %s",len(sys.argv))
logger.info("Certname: %s", CERTNAME)
logger.debug("CSR: %s", CSR)
### Whitelist certnames ###
###########################
### Enter your own values ###
if CERTNAME == "":
logger.info("It's the puppetmaster, of course we'll approve it.")
sys.exit(0)
if CERTNAME == "":
logger.info("It's the puppetdb, of course we'll approve it.")
sys.exit(0)
### Start Duo auth request ###
##############################
INFO = {
'Certname': CERTNAME
}
### Start Duo auth request ### Read CSR for PSK attribute ###
#############################################################
cmd = ['/usr/bin/openssl', 'req', '-noout', '-text']
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(output, err) = proc.communicate(CSR)
logger.debug("CSR output: %s", output)
logger.debug("CSR error: %s", err)
lineList = output.splitlines()
strippedLineList = [line.lstrip() for line in lineList]
strippedLineList2 = [line.rstrip() for line in strippedLineList]
logger.debug("Stripped list: %s", strippedLineList2)
try:
psk_attribute = strippedLineList2.index("1.3.6.1.4.1.34380.1.1.4:")
except:
logger.info("No PSK in CSR. Notifing Duo.")
INFO['Preshared key'] = 'No PSK in CSR.'
psk = strippedLineList2[psk_attribute+1]
logger.info("Attribute: %s", psk)
try:
f = open("/etc/puppetlabs/psk/psk.txt", "r")
approved_key = ". %s" % f.read(32)
except:
logger.info("No approved key. Notifing Duo.")
INFO['Preshared key'] = 'No approved key found on Server.'
if psk == approved_key:
INFO['Preshared key'] = 'Success!'
else:
logger.info("Key mismatch. Notifing Duo.")
INFO['Preshared key'] = 'Key mismatch.'
### Send Duo auth request ###
#############################
PUSH_INFO = urllib.urlencode(INFO)
client = duo_client.Auth(ikey=DUO_INTEGRATION_KEY, skey=DUO_SECRET_KEY, host=DUO_API_HOSTNAME)
request = client.auth(factor='push', username=DUO_USERNAME, device='auto', pushinfo=PUSH_INFO, async=True)
txid = request['txid']
time.sleep(15)
check_req = client.auth_status(txid)
if check_req['status'] == 'deny':
logger.info("Denied by Duo.")
sys.exit(1)
elif check_req['status'] == 'allow':
logger.info("Allowed by Duo.")
sys.exit(0)
elif check_req['status'] != 'timeout':
timeout = time.time() + 60*5
while (time.time() < timeout):
check_req = client.auth_status(txid)
req_status = check_req['status']
if req_status == 'allow':
logger.info("Allowed by Duo.")
sys.exit(0)
else:
logger.info("Duo request waiting for approval. Sleeping 20...")
time.sleep(20)
else:
logger.info("Duo request timeout: CSR for %s not approved.", CERTNAME)
sys.exit(1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment