Creating Puppet CA and Certificates Manually with OpenSSL: A Step-by-Step Guide for Multi-Master Deployment


2 views

When automating Puppet infrastructure deployment, the standard puppet cert command often falls short for multi-master environments. The main issue arises from certificate mismatches between masters and agents, as shown in the error message:

Could not prepare for execution: The certificate retrieved from the master does not match the agent's private key.
Certificate fingerprint: 4F:08:AE:01:B9:14:AC:A4:EA:A7:92:D7:02:E9:34:39:1C:5F:0D:93:A0:85:1C:CF:68:E4:52:B8:25:D1:11:64
  • OpenSSL installed on your system
  • Basic understanding of x509 certificates
  • Puppet master configuration files access

First, generate the private key and root certificate for your Puppet CA:

openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt \
  -subj "/CN=Puppet CA/OU=Operations/O=Example Corp"

Modify your puppet.conf to point to the custom CA:

[master]
ssldir = /etc/puppetlabs/puppet/ssl
cadir = /etc/puppetlabs/puppet/ssl/ca
certname = puppetmaster.example.com

Create a certificate signing request (CSR) for your Puppet master:

openssl genrsa -out puppetmaster.key 2048
openssl req -new -key puppetmaster.key -out puppetmaster.csr \
  -subj "/CN=puppetmaster.example.com/OU=Puppet Masters/O=Example Corp"

Then sign it with your CA:

openssl x509 -req -in puppetmaster.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out puppetmaster.crt -days 365 -sha256

For each agent node, create a certificate in similar fashion:

# Generate private key
openssl genrsa -out agent01.key 2048

# Create CSR
openssl req -new -key agent01.key -out agent01.csr \
  -subj "/CN=agent01.example.com/OU=Puppet Agents/O=Example Corp"

# Sign with CA
openssl x509 -req -in agent01.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out agent01.crt -days 365 -sha256

For multiple masters, you'll want to:

  1. Generate one root CA
  2. Distribute ca.crt to all masters
  3. Create unique certificates for each master
  4. Use the same CA for all agent certificates

To verify certificate consistency:

openssl x509 -noout -modulus -in puppetmaster.crt | openssl md5
openssl rsa -noout -modulus -in puppetmaster.key | openssl md5

The MD5 hashes must match. If they don't, you'll see the fingerprint mismatch error mentioned in the original problem.

For multiple environments, consider this bash script template:

#!/bin/bash

# Variables
CA_PEM="ca.crt"
CA_KEY="ca.key"
CERT_DAYS=365
MASTER_CN="puppetmaster.example.com"

generate_cert() {
  local name=$1
  local ou=$2
  local cn=${3:-$name}
  
  openssl genrsa -out ${name}.key 2048
  openssl req -new -key ${name}.key -out ${name}.csr \
    -subj "/CN=${cn}/OU=${ou}/O=Example Corp"
  openssl x509 -req -in ${name}.csr -CA ${CA_PEM} -CAkey ${CA_KEY} \
    -CAcreateserial -out ${name}.crt -days ${CERT_DAYS} -sha256
}

# Generate master cert
generate_cert "puppetmaster" "Puppet Masters" "${MASTER_CN}"

# Generate agent certs
generate_cert "web01" "Web Servers"
generate_cert "db01" "Database Servers"

Puppet relies on a Public Key Infrastructure (PKI) system where the Puppet master acts as the Certificate Authority (CA). While Puppet provides built-in commands like puppet cert, there are scenarios where manual certificate generation is preferred:

  • Automated deployment across multiple masters
  • Custom certificate attributes
  • Pre-staging certificates before server deployment

Here's the complete workflow to create a Puppet-compatible CA and certificates:

1. Create the CA Structure

mkdir -p puppet_ssl/{certs,private_keys,public_keys,requests}
cd puppet_ssl

2. Generate the CA Key and Certificate

openssl genrsa -out private_keys/ca.pem 4096
openssl req -new -x509 -days 3650 \
    -key private_keys/ca.pem \
    -out certs/ca.pem \
    -subj "/CN=Puppet CA/OU=Operations/O=Example Corp"

3. Create Puppet Master Certificate

# Generate private key
openssl genrsa -out private_keys/puppetmaster.pem 2048

# Create CSR
openssl req -new -sha256 \
    -key private_keys/puppetmaster.pem \
    -out requests/puppetmaster.csr \
    -subj "/CN=puppet.example.com"

# Sign the certificate
openssl x509 -req -in requests/puppetmaster.csr \
    -CA certs/ca.pem \
    -CAkey private_keys/ca.pem \
    -CAcreateserial \
    -out certs/puppetmaster.pem \
    -days 365

In your puppet.conf:

[master]
ssldir = /etc/puppetlabs/puppet/ssl
certname = puppet.example.com
ca_name = 'Puppet CA'

The error you encountered typically indicates a key/certificate mismatch. Verify with:

openssl rsa -noout -modulus -in private_keys/puppetmaster.pem | openssl md5
openssl x509 -noout -modulus -in certs/puppetmaster.pem | openssl md5

Both commands should output identical hash values.

For multiple Puppet masters, consider this bash script template:

#!/bin/bash
DOMAIN=$1
MASTER_NAME=$2

openssl genrsa -out ${MASTER_NAME}.pem 2048
openssl req -new -key ${MASTER_NAME}.pem \
    -out ${MASTER_NAME}.csr \
    -subj "/CN=${MASTER_NAME}.${DOMAIN}"
openssl x509 -req -in ${MASTER_NAME}.csr \
    -CA ca.pem -CAkey ca.key \
    -out ${MASTER_NAME}.crt -days 365

# Deploy to Puppet master
scp ${MASTER_NAME}.{pem,crt} puppet@${MASTER_NAME}:/etc/puppetlabs/puppet/ssl/

After deployment, verify the chain of trust:

openssl verify -CAfile /etc/puppetlabs/puppet/ssl/certs/ca.pem \
    /etc/puppetlabs/puppet/ssl/certs/puppetmaster.pem