DEV Community

Nate Johnston
Nate Johnston

Posted on

Reissuing the Puppet Master SSL Certificate Authority Root Certificate

Reissuing the Puppet Master SSL Certificate Authority Root Certificate

Introduction

This document is a procedure for a Puppet administrator who needs to configure the Puppet Master SSL Certificate Authority facility ("CA") to use a new root SSL certificate to sign and verify agent certificates, while still retaining compatibility with already signed certificates.

Approach

The certificate signing relationship is based on a signature (cryptographic modulus) from the private key for the CA; keeping the same private key (and, implicitly, the same public key) while generating a new public certificate, with a new validity period and any other new attributes changed as needed, keeps the trust relationship in place. CRLs, too, can continue over from the old cert to the new, as they are, like certificates, signed by the private key.

Locations of Files

Note that the following are the real locations of the important files on the Puppet Master in my environment. Your environment might be different.

File Description
/opt/puppet/ssl/ca/ca_crt.pem CA public key
/opt/puppet/ssl/ca/ca_key.pem CA private key
/opt/puppet/ssl/ca/private/ca.pass CA private key passphrase

How to Test It Out in a Sandbox

Initial Setup

Step 1: Create the openssl.conf file that will specify the subject alternate names etc.

Note: There is a req_distinguished_name section here, but you will find you may need to retype the items in it. It is a pain.

cat > openssl.conf << LIMIT
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
ca_extensions = v3_ca

[req_distinguished_name]
countryName = US
stateOrProvinceName = VA
localityName = Here
organizationalUnitName  = Puppet Ops
commonName = CN=Puppet CA: puppet

[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = critical,CA:TRUE
nsComment = "Puppet Ruby/OpenSSL Internal Certificate"
keyUsage = critical, cRLSign, keyCertSign
subjectAltName = @alt_names

[ v3_ca ]
# Extensions to add to a certificate request
authorityKeyIdentifier= keyid:always,issuer:always
basicConstraints = critical,CA:TRUE
keyUsage = critical, cRLSign, keyCertSign
nsComment = "Puppet Ruby/OpenSSL Internal Certificate"
subjectAltName = @alt_names
subjectKeyIdentifier= hash

[alt_names]
DNS.1 = puppet.company.net
DNS.2 = puppet-test.company.net
DNS.3 = puppet-integration.company.net

LIMIT

Step 2: Make a root certificate named "origroot".

# openssl req -new -x509 -keyout root.key -out origroot.pem -days 3650 -nodes
Generating a 512 bit RSA private key
...++++++++++++
.++++++++++++
writing new private key to 'root.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
US []:US
VA []:VA
Reston []:Reston
IDEA Automation []:IDEA Automation
CN=Puppet CA: autopuppet []:CN=Puppet CA: autopuppet
NETO-AutomationTeam@cable.comcast.com []:

Step 3: Generate a child certificate from the "origroot" certificate.

# openssl genrsa -out cert.key 1024
Generating RSA private key, 1024 bit long modulus
....++++++
.......................++++++
e is 65537 (0x10001)

# openssl req -new -key cert.key -out cert.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
US []:US
VA []:VA
Reston []:Here
IDEA Automation []:Puppet Ops
CN=Puppet CA: puppet []:CN=Puppet CA: puppet
PuppetOps@company.net []:

Step 4: Sign the child certificate.

# openssl x509 -req -in cert.csr -CA origroot.pem -CAkey root.key -set_serial 01 -out cert.pem
Signature ok
subject=/C=US/ST=VA/L=Here/OU=Puppet Ops/CN=CN=Puppet CA: puppet
Getting CA Private Key

# rm -f cert.csr

Step 5: Verify the trust relationship between the child certificate and the "origroot" certificate.

# openssl verify -CAfile origroot.pem -verbose cert.pem
cert.pem: OK

Re-Issuing

Step 6: Now say 10 years passed and it is close to the expiration date, or another change needs to happen. Generate a "newroot" public certificate from the same root private key.

# openssl x509 -x509toreq -in origroot.pem -signkey root.key -out renewedselfsignedca.csr
Getting request Private Key
Generating certificate request

# openssl x509 -req -days 3650 -in renewedselfsignedca.csr -signkey root.key -out newroot.pem -extfile ./openssl.conf -extensions v3_ca
Signature ok
subject=/CN=Puppet CA: puppet
Getting Private key

# rm -f renewedselfsignedca.csr

Verification

Step 7: Verify the trust relationship between the child certificate and the "newroot" certificate.

# openssl verify -CAfile newroot.pem -verbose cert.pem
cert.pem: C = US, ST = VA, L = Here, OU = Puppet Ops, CN = CN=Puppet CA: puppet
error 18 at 0 depth lookup:self signed certificate
OK

Step 8: Verify that "origroot" and "newroot" are different files.

# sha1sum newroot.pem
62577e00309e5eacf210d0538cd79c3cdc834020  newroot.pem
# sha1sum origroot.pem
c1d65a6cdfa6fc0e0a800be5edd3ab3b603e1899  origroot.pem

Step 9: The critical part that must correspond for a cryptographic match is the modulus of the certificate. Compare the modulus of each certificate. Also, because of the methods Puppet uses, some of the X509v3 certificate extensions should also match.

# openssl x509 -noout -text -in origroot.pem
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1 (0x1)
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: CN=Puppet CA: puppet
        Validity
            Not Before: Aug  8 13:52:26 2012 GMT
            Not After : Aug  6 13:52:26 2027 GMT
        Subject: CN=Puppet CA: puppet
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (4096 bit)
                Modulus:
                    00:bd:56:b5:26:06:c1:f6:4c:f4:7c:14:2c:0d:dd:
                    6b:73:5b:f1:dd:9e:30:2b:f7:6e:bd:dc:d1:39:98:
                    3c:eb:8f:0a:c0:9d:d8:b4:8c:b5:d9:c7:87:4e:25:
                    4b:8d:23:fe:52:98:15:53:3a:91:r1:14:05:a7:7a:
                    9b:20:a9:b2:98:6e:67:36:04:dd:a6:cb:6c:3e:23:
                    8f:7c:92:4d:8f:b3:ee:e9:56:8d:db:f7:fd:d3:57:
                    1f:84:2a:fa:6c:ad:99:8a:fa:z5:41:68:f8:e4:10:
                    1f:17:13:25:e7:3f:79:68:9f:b5:20:c9:ef:2f:3d:
                    d7:a3:66:0a:45:bd:0e💿9d
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            Netscape Comment: 
                Puppet Ruby/OpenSSL Internal Certificate
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Subject Key Identifier: 
                67:B7:D5:93:E8:DC:FC:E2:22:66:62:23:11:46:9A:11:8E:ZA:40:1B
...

# openssl x509 -noout -text -in newroot.pem
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 9996448100145435494 (0x8aba1a2a3e7d525e)
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: CN=Puppet CA: puppet
        Validity
            Not Before: Jun 20 17:18:49 2014 GMT
            Not After : Jun 17 17:18:49 2024 GMT
        Subject: CN=Puppet CA: puppet
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (4096 bit)
                Modulus:
            RSA Public Key: (1024 bit)
                Modulus (1024 bit):
                    00:bd:56:b5:26:06:c1:f6:4c:f4:7c:14:2c:0d:dd:
                    6b:73:5b:f1:dd:9e:30:2b:f7:6e:bd:dc:d1:39:98:
                    3c:eb:8f:0a:c0:9d:d8:b4:8c:b5:d9:c7:87:4e:25:
                    4b:8d:23:fe:52:98:15:53:3a:91:r1:14:05:a7:7a:
                    9b:20:a9:b2:98:6e:67:36:04:dd:a6:cb:6c:3e:23:
                    8f:7c:92:4d:8f:b3:ee:e9:56:8d:db:f7:fd:d3:57:
                    1f:84:2a:fa:6c:ad:99:8a:fa:z5:41:68:f8:e4:10:
                    1f:17:13:25:e7:3f:79:68:9f:b5:20:c9:ef:2f:3d:
                    d7:a3:66:0a:45:bd:0e💿9d
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            Netscape Comment: 
                Puppet Ruby/OpenSSL Internal Certificate
            X509v3 Subject Alternative Name: 
                DNS:puppet.company.net, DNS:puppet-test.company.net, DNS:puppet-integration.company.net
            X509v3 Subject Key Identifier: 
                67:B7:D5:93:E8:DC:FC:E2:22:66:62:23:11:46:9A:11:8E:ZA:40:1B
...

Resources

This process was first seen in this article in ServerFault.

Top comments (1)

Collapse
 
raphink profile image
Raphaël Pinson

Nice!

There is now an official module to help with that: forge.puppet.com/puppetlabs/certregen

Also, when you renew the CA certificate, you soon find that you need to renew the agent certificates as well. I wrote about this at dev.to/camptocamp-ops/automatic-re...