Configuration: External CA Support
Starting with Puppet 3.2.0 and later, Puppet can use an existing external CA for all of its SSL communications. This page describes the supported configurations for external CAs.
Note: This page uses RFC 2119 style semantics for MUST, SHOULD, MAY.
Historical note: Using an external CA with Puppet has sometimes worked (or appeared to), but it frequently broke, and there was never a concerted effort to keep it working. Most recently, a security fix in Puppet 2.7.18 broke nearly all external CAs, and prevented a lot of people from upgrading through the early 3.x series. (See issue #15561.)
Starting with Puppet 3.2, Puppet Labs officially supports and tests external CAs in certain configurations. We’re hoping that 15561 will be the last total external CA breakage. Thanks for your patience, and please get in touch if your use case isn’t covered.
Supported External CA Configurations
Puppet ≥ 3.2 supports some external CA configurations, but not every possible arrangement. We fully support the following setups:
- Single self-signed CA which directly issues SSL certificates.
- Single, intermediate CA issued by a root self-signed CA. The intermediate CA directly issues SSL certificates; the root CA doesn’t.
- Two intermediate CAs, both issued by the same root self-signed CA.
- One intermediate CA issues SSL certificates for puppet master servers.
- The other intermediate CA issues SSL certificates for agent nodes.
- Agent certificates can’t act as servers, and master certificates can’t act as clients.
These are fully supported by Puppet Labs, which means:
- Issues that arise in one of these three arrangements are considered bugs, and we’ll fix them ASAP.
- Issues that arise in any other external CA setup are considered feature requests, and we’ll consider whether to expand our support.
These configurations are all-or-nothing rather than mix-and-match. When using an external CA, the built in Puppet CA service must be disabled and cannot be used to issue SSL certificates.
Additionally, Puppet cannot automatically distribute certificates in these configurations — you must have your own complete system for issuing and distributing certificates.
General Notes and Requirements
Rack Webserver is Required
The puppet master must be running inside of a Rack-enabled web server, not the built-in Webrick server.
In practice, this means Apache or Nginx. We fully support any web server that can:
- Terminate SSL
- Verify the authenticity of a client SSL certificate
- Set two request headers for:
- Whether the client was verified
- The client’s distinguished name
PEM Encoding of Credentials is Mandatory
Puppet always expects its SSL credentials to be in .pem
format.
Normal Puppet Master Certificate Requirements Still Apply
Any puppet master certificate must contain the DNS name at which agent nodes will attempt to contact that master, either as the subject CN or as a Subject Alternative Name (DNS).
Format of X-Client-DN Request Header
Rack web servers must set a client request header, which the puppet master will check based on the ssl_client_header
setting.
This header should conform to the following specifications:
- The value of the client certificate DN should be in RFC-2253 format. The format of the
SSL_CLIENT_S_DN
environment variable (set by Apache ≥ 2.2’smod_ssl
) is fully supported. - Alternatively, the value of this request header may be in “OpenSSL” format.
Revocation
Certificate revocation list (CRL) checking works in all three supported configurations, so long as the CRL file is distributed to the agents and masters using an “out of band” process. Puppet won’t automatically update the CRL on any of the components in the system.
If Unused:
If revocation lists are not being used by the external CA, you must disable CRL checking on the agent.
Set certificate_revocation = false
in the
[agent]
section of puppet.conf on every agent node.
(If it’s not set to false and the agent doesn’t already have a CRL file, it will try to download one from the master. This will fail, because the master must have the CA service disabled.)
If Used:
If revocation lists are being used by the external CA, then the CRL file must be manually distributed to every agent node as a PEM encoded bundle. Puppet will not automatically distribute this file.
To determine where to put the CRL file, run puppet agent --configprint hostcrl
.
Option 1: Single CA
A single CA is the default configuration of Puppet when the internal CA is being used. A single, externally issued CA may also be used in a similar manner.
+------------------------+
| |
| Root self-signed CA |
| |
+------+----------+------+
| |
+----------+ +------------+
| |
v v
+-----------------+ +----------------+
| | | |
| Master SSL Cert | | Agent SSL Cert |
| | | |
+-----------------+ +----------------+
Puppet Master
Configure the puppet master in four steps:
- Disable the internal CA service
- Ensure that the certname will never change
- Put certificates/keys in place on disk
- Configure the web server
On the master, in puppet.conf
, make sure the following settings are configured:
[master]
ca = false
certname = <some static string, e.g. 'puppetmaster'>
- The internal CA service must be disabled using
ca = false
. - The certname must be set to a static value. This can still be the machine’s FQDN, but you must not leave the setting blank. (A static certname will keep Puppet from getting confused if the machine’s hostname ever changes.)
Once this configuration is set, put the external credentials into the correct filesystem locations. You can run the following commands to find the appropriate locations:
Credential | File location |
---|---|
Master SSL certificate | puppet master --configprint hostcert |
Master SSL certificate private key | puppet master --configprint hostprivkey |
Root CA certificate | puppet master --configprint localcacert |
With these files in place, the web server should be configured to:
- Use the root CA certificate, the master’s certificate, and the master’s key
- Set the verification header (as specified in the master’s
ssl_client_verify_header
setting) - Set the client DN header (as specified in the master’s
ssl_client_header
setting)
An example of this configuration for Apache:
Listen 8140
<VirtualHost *:8140>
SSLEngine on
SSLProtocol ALL -SSLv2
SSLCipherSuite ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:-LOW:-SSLv2:-EXP
# Replace with the value of `puppet master --configprint hostcert`
SSLCertificateFile "/path/to/master.pem"
# Replace with the value of `puppet master --configprint hostprivkey`
SSLCertificateKeyFile "/path/to/master.key"
# Replace with the value of `puppet master --configprint localcacert`
SSLCACertificateFile "/path/to/ca.pem"
SSLVerifyClient optional
SSLVerifyDepth 1
SSLOptions +StdEnvVars
RequestHeader set X-SSL-Subject %{SSL_CLIENT_S_DN}e
RequestHeader set X-Client-DN %{SSL_CLIENT_S_DN}e
RequestHeader set X-Client-Verify %{SSL_CLIENT_VERIFY}e
DocumentRoot "/etc/puppet/public"
PassengerRoot /usr/share/gems/gems/passenger-3.0.17
PassengerRuby /usr/bin/ruby
RackAutoDetect On
RackBaseURI /
</VirtualHost>
The config.ru
file for rack has no special configuration when using an
external CA. Please follow the standard rack documentation for using Puppet
with rack. The following example will work with Puppet 3.2.
$0 = "master"
ARGV << "--rack"
ARGV << "--confdir=/etc/puppet"
ARGV << "--vardir=/var/lib/puppet"
require 'puppet/util/command_line'
run Puppet::Util::CommandLine.new.execute
Puppet Agent
You don’t need to change any settings.
Put the external credentials into the correct filesystem locations. You can run the following commands to find the appropriate locations:
Credential | File location |
---|---|
Agent SSL certificate | puppet agent --configprint hostcert |
Agent SSL certificate private key | puppet agent --configprint hostprivkey |
Root CA certificate | puppet agent --configprint localcacert |
Option 2: Single Intermediate CA
The single intermediate CA configuration builds from the single self-signed CA configuration and introduces one additional intermediate CA.
+------------------------+
| |
| self-signed Root CA |
| |
+-----------+------------+
|
|
v
+------------------------+
| |
| Intermediate CA |
| |
+------+----------+------+
| |
+----------+ +------------+
| |
v v
+-----------------+ +----------------+
| | | |
| Master SSL Cert | | Agent SSL Cert |
| | | |
+-----------------+ +----------------+
The Root CA does not issue SSL certificates in this configuration. The intermediate CA issues SSL certificates for clients and servers alike.
Puppet Master
Configure the puppet master in four steps:
- Disable the internal CA service
- Ensure that the certname will never change
- Put certificates/keys in place on disk
- Configure the web server
On the master, in puppet.conf
, make sure the following settings are configured:
[master]
ca = false
certname = <some static string, e.g. 'puppetmaster'>
- The internal CA service must be disabled using
ca = false
. - The certname must be set to a static value. This can still be the machine’s FQDN, but you must not leave the setting blank. (A static certname will keep Puppet from getting confused if the machine’s hostname ever changes.)
Once this configuration is set, put the external credentials into the correct filesystem locations. You can run the following commands to find the appropriate locations:
Credential | File location |
---|---|
Master SSL certificate | puppet master --configprint hostcert |
Master SSL certificate private key | puppet master --configprint hostprivkey |
Root CA certificate | puppet master --configprint localcacert |
You must also create a CA bundle for the web server. Append the two CA certificates together; the Root CA certificate must be located after the intermediate CA certificate within the file.
$ cat intermediate_ca.pem root_ca.pem > ca_bundle.pem
Put this file somewhere predictable. Puppet doesn’t use it directly, but it can live alongside Puppet’s copies of the certificates.
With these files in place, the web server should be configured to:
- Use the intermediate+Root CA bundle, the master’s certificate, and the master’s key
- Set the verification header (as specified in the master’s
ssl_client_verify_header
setting) - Set the client DN header (as specified in the master’s
ssl_client_header
setting)
An example of this configuration for Apache:
Listen 8140
<VirtualHost *:8140>
SSLEngine on
SSLProtocol ALL -SSLv2
SSLCipherSuite ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:-LOW:-SSLv2:-EXP
# Replace with the value of `puppet master --configprint hostcert`
SSLCertificateFile "/path/to/master.pem"
# Replace with the value of `puppet master --configprint hostprivkey`
SSLCertificateKeyFile "/path/to/master.key"
# Replace with the value of `puppet master --configprint localcacert`
SSLCACertificateFile "/path/to/ca_bundle.pem"
SSLCertificateChainFile "/path/to/ca_bundle.pem"
# Allow only clients with a SSL certificate issued by the intermediate CA with
# common name "Puppet CA" Replace "Puppet CA" with the CN of your
# intermediate CA certificate.
SSLRequire %{SSL_CLIENT_I_DN_CN} eq "Puppet CA"
SSLVerifyClient optional
SSLVerifyDepth 2
SSLOptions +StdEnvVars
RequestHeader set X-SSL-Subject %{SSL_CLIENT_S_DN}e
RequestHeader set X-Client-DN %{SSL_CLIENT_S_DN}e
RequestHeader set X-Client-Verify %{SSL_CLIENT_VERIFY}e
DocumentRoot "/etc/puppet/public"
PassengerRoot /usr/share/gems/gems/passenger-3.0.17
PassengerRuby /usr/bin/ruby
RackAutoDetect On
RackBaseURI /
</VirtualHost>
The config.ru
file for rack has no special configuration when using an
external CA. Please follow the standard rack documentation for using Puppet
with rack. The following example will work with Puppet 3.2.
$0 = "master"
ARGV << "--rack"
ARGV << "--confdir=/etc/puppet"
ARGV << "--vardir=/var/lib/puppet"
require 'puppet/util/command_line'
run Puppet::Util::CommandLine.new.execute
Puppet Agent
With an intermediate CA, puppet agent needs a modified value for the ssl_client_ca_auth
setting in its puppet.conf:
[agent]
ssl_client_ca_auth = $certdir/issuer.pem
The value should point to somewhere in the $certdir
.
Put the external credentials into the correct filesystem locations. You can run the following commands to find the appropriate locations:
Credential | File location |
---|---|
Agent SSL certificate | puppet agent --configprint hostcert |
Agent SSL certificate private key | puppet agent --configprint hostprivkey |
Root CA certificate | puppet agent --configprint localcacert |
Intermediate CA certificate | puppet agent --configprint ssl_client_ca_auth |
Option 3: Two Intermediate CAs Issued by One Root CA
This configuration uses different CAs to issue puppet master certificates and puppet agent certificates. This makes them easily distinguishable, and prevents any agent certificate from being usable for a puppet master.
+------------------------+
| |
| Root self-signed CA |
| |
+------+----------+------+
| |
+----------+ +------------+
| |
v v
+-----------------+ +----------------+
| | | |
| Master CA | | Agent CA |
| | | |
+--------+--------+ +--------+-------+
| |
| |
v v
+-----------------+ +----------------+
| | | |
| Master SSL Cert | | Agent SSL Cert |
| | | |
+-----------------+ +----------------+
In this configuration puppet agents are configured to only authenticate peer certificates issued by the Master CA. Puppet masters are configured to only authenticate peer certificates issued by the Agent CA.
Note: If you’re using this configuration, you can’t use the ActiveRecord inventory service backend with multiple puppet master servers. Use PuppetDB for the inventory service instead.
Puppet Master
Configure the puppet master in four steps:
- Disable the internal CA service
- Ensure that the certname will never change
- Put certificates/keys in place on disk
- Configure the web server
On the master, in puppet.conf
, make sure the following settings are configured:
[master]
ca = false
certname = <some static string, e.g. 'puppetmaster'>
- The internal CA service must be disabled using
ca = false
. - The certname must be set to a static value. This can still be the machine’s FQDN, but you must not leave the setting blank. (A static certname will keep Puppet from getting confused if the machine’s hostname ever changes.)
Once this configuration is set, put the external credentials into the correct filesystem locations. You can run the following commands to find the appropriate locations:
Credential | File location |
---|---|
Master SSL certificate | puppet master --configprint hostcert |
Master SSL certificate private key | puppet master --configprint hostprivkey |
Root CA certificate | puppet master --configprint localcacert |
You must also create a CA bundle for the web server. Append the Agent CA certificate and Root CA certificate together; the Root CA certificate must be located after the Agent CA certificate within the file.
$ cat agent_ca.pem root_ca.pem > ca_bundle_for_master.pem
Put this file somewhere predictable. Puppet doesn’t use it directly, but it can live alongside Puppet’s copies of the certificates.
With these files in place, the web server should be configured to:
- Use the Agent+Root CA bundle, the master’s certificate, and the master’s key
- Set the verification header (as specified in the master’s
ssl_client_verify_header
setting) - Set the client DN header (as specified in the master’s
ssl_client_header
setting)
An example of this configuration for Apache:
Listen 8140
<VirtualHost *:8140>
SSLEngine on
SSLProtocol ALL -SSLv2
SSLCipherSuite ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:-LOW:-SSLv2:-EXP
# Replace with the value of `puppet master --configprint hostcert`
SSLCertificateFile "/path/to/master.pem"
# Replace with the value of `puppet master --configprint hostprivkey`
SSLCertificateKeyFile "/path/to/master.key"
# Replace with the value of `puppet master --configprint localcacert`
SSLCACertificateFile "/path/to/ca_bundle_for_master.pem"
SSLCertificateChainFile "/path/to/ca_bundle_for_master.pem"
# Allow only clients with a SSL certificate issued by the intermediate CA with
# common name "Puppet Agent CA" Replace "Puppet Agent CA" with the CN of your
# Agent CA certificate.
SSLRequire %{SSL_CLIENT_I_DN_CN} eq "Puppet Agent CA"
SSLVerifyClient optional
SSLVerifyDepth 2
SSLOptions +StdEnvVars
RequestHeader set X-SSL-Subject %{SSL_CLIENT_S_DN}e
RequestHeader set X-Client-DN %{SSL_CLIENT_S_DN}e
RequestHeader set X-Client-Verify %{SSL_CLIENT_VERIFY}e
DocumentRoot "/etc/puppet/public"
PassengerRoot /usr/share/gems/gems/passenger-3.0.17
PassengerRuby /usr/bin/ruby
RackAutoDetect On
RackBaseURI /
</VirtualHost>
The config.ru
file for rack has no special configuration when using an
external CA. Please follow the standard rack documentation for using Puppet
with rack. The following example will work with Puppet 3.2.
$0 = "master"
ARGV << "--rack"
ARGV << "--confdir=/etc/puppet"
ARGV << "--vardir=/var/lib/puppet"
require 'puppet/util/command_line'
run Puppet::Util::CommandLine.new.execute
Puppet Agent
With split CAs, puppet agent needs a modified value for the ssl_client_ca_auth
setting in its puppet.conf:
[agent]
ssl_client_ca_auth = $certdir/ca_master.pem
The value should point to somewhere in the $certdir
.
Put the external credentials into the correct filesystem locations. You can run the following commands to find the appropriate locations:
Credential | File location |
---|---|
Agent SSL certificate | puppet agent --configprint hostcert |
Agent SSL certificate private key | puppet agent --configprint hostprivkey |
Root CA certificate | puppet agent --configprint localcacert |
Master CA certificate | puppet agent --configprint ssl_client_ca_auth |