SSL Configuration: CSR Attributes and Certificate Extensions
Included in Puppet Enterprise 2016.1.
When Puppet agent nodes request their certificates, the certificate signing request (CSR) usually contains only their certname and the necessary cryptographic information. However, agents can also embed more data in their CSR. This extra data can be useful for policy-based autosigning and adding new trusted facts.
Embedding additional data into CSRs is mostly useful when:
- Large numbers of nodes are regularly created and destroyed as part of an elastic scaling system.
- You are willing to build custom tooling to make certificate autosigning more secure and useful.
It might also be useful in deployments where Puppet is used to deploy private keys or other sensitive information, and you want extra control over which nodes receive this data.
If your deployment doesn’t match one of these descriptions, you might not need this feature.
Timing: When Data Can be Added to CSRs and Certificates
When Puppet agent starts the process of requesting a catalog, it first checks whether it has a valid signed certificate. If it does not, it generates a key pair, crafts a CSR, and submits it to the certificate authority (CA) Puppet master. The steps are covered in more detail in the reference page about agent/master HTTPS traffic.
For all practical purposes, a certificate is locked and immutable as soon as it is signed. For any data to persist in the certificate, it has to be added to the CSR before the CA signs the certificate.
This means any desired extra data must be present before Puppet agent attempts to request its catalog for the first time.
Practically speaking, you should populate any extra data when provisioning the node. If you mess up, see Recovering From Failed Data Embedding below.
Data Location and Format
csr_attributes file must be a YAML hash containing one or both of the following keys:
The value of each key must also be a hash, where:
- Each key is a valid object identifier (OID) — Puppet-specific OIDs can optionally be referenced by short name instead of by numeric ID.
- Each value is an object that can be cast to a string (that is, numbers are allowed but arrays are not).
See the respective sections below for information about how each hash is used and recommended OIDs for each hash.
Custom Attributes (Transient CSR Data)
Custom Attributes are pieces of data that are only embedded in the CSR. The CA can use them when deciding whether to sign the certificate, but they are discarded after that and aren’t transferred to the final certificate.
By default, Puppet’s CA tools don’t do anything with custom attributes. The
puppet cert list command doesn’t display custom attributes for pending CSRs, and basic autosigning (autosign.conf) doesn’t check them before signing.
If you use policy-based autosigning, your policy executable receives the complete CSR in PEM format. The executable can extract and inspect the custom attributes, and use them when deciding whether to sign the certificate.
The simplest use is to embed a pre-shared key of some kind in the custom attributes. A policy executable can compare it to a list of known keys and autosign certificates for any pre-authorized nodes.
A more complex use might be to embed an instance-specific ID and write a policy executable that can check it against a list of your recently requested instances on a public cloud, like EC2 or GCE.
Manually Checking for Custom Attributes in CSRs
You can check for custom attributes by using OpenSSL to dump a PEM-format CSR to text format. Do this by running:
openssl req -noout -text -in <name>.pem
In the output, look for a section called “Attributes,” which generally appears below the “Subject Public Key Info” block:
Attributes: challengePassword :342thbjkt82094y0uthhor289jnqthpc2290
Recommended OIDs for Attributes
Custom attributes can use any public or site-specific OID, with the exception of the OIDs used for core X.509 functionality. This means you can’t re-use existing OIDs for things like subject alternative names.
One useful OID is the “challengePassword” attribute —
1.2.840.113518.104.22.168. This is a rarely-used corner of X.509 that can easily be repurposed to hold a pre-shared key. The benefit of using this instead of an arbitrary OID is that it appears by name when using OpenSSL to dump the CSR to text; OIDs that
openssl req can’t recognize are displayed as numerical strings.
You can also use the Puppet-specific OIDs referenced below in the section on extension requests.
Extension Requests (Permanent Certificate Data)
Extension requests are pieces of data that are transferred to the final certificate (as extensions) when the CA signs the CSR. They persist as trusted, immutable data, that cannot be altered after the certificate is signed.
They can also be used by the CA when deciding whether or not to sign the certificate.
When signing a certificate, Puppet’s CA tools transfer any extension requests into the final certificate.
You can access certificate extensions in manifests as
Any OIDs in the ppRegCertExt range (see below) appear using their short names. By default, any other OIDs appear as plain dotted numbers, but you can use the
custom_trusted_oid_mapping.yaml file to assign short names to any other OIDs you use at your site. If you do, those OIDs will appear in
$trusted as their short names instead of their full numerical OID.
See the page on facts and special variables for more information about
Visibility of extensions is somewhat limited:
puppet cert listcommand does not display custom attributes for any pending CSRs, and basic autosigning (autosign.conf) doesn’t check them before signing. Either use policy-based autosigning or inspect CSRs manually with the
opensslcommand (see below).
puppet cert printcommand does display any extensions in a signed certificate, under the “X509v3 extensions” section.
Puppet’s authorization system (
auth.conf) does not use certificate extensions.
If you use policy-based autosigning, your policy executable receives the complete CSR in PEM format. The executable can extract and inspect the extension requests, and use them when deciding whether to sign the certificate.
Manually Checking for Extensions in CSRs and Certificates
You can check for extension requests in a CSR by using OpenSSL to dump a PEM-format CSR to text format. Do this by running:
openssl req -noout -text -in <name>.pem
In the output, look for a section called “Requested Extensions,” which generally appears below the “Subject Public Key Info” and “Attributes” blocks:
Requested Extensions: pp_uuid: .$ED803750-E3C7-44F5-BB08-41A04433FE2E 22.214.171.124.4.1.343126.96.36.199: ..my_ami_image 188.8.131.52.4.1.343184.108.40.206: .$342thbjkt82094y0uthhor289jnqthpc2290
Note that every extension is preceded by any combination of two characters (
.. in the above example) that contain ASN.1 encoding information. Since OpenSSL is unaware of Puppet’s custom extensions OIDs, it’s unable to properly display the values.
Any Puppet-specific OIDs (see below) appear as numeric strings when using OpenSSL.
You can check for extensions in a signed certificate by running
puppet cert print <name>. In the output, look for the “X509v3 extensions” section. Any of the Puppet-specific registered OIDs (see below) appear as their descriptive names:
X509v3 extensions: Netscape Comment: Puppet Ruby/OpenSSL Internal Certificate X509v3 Subject Key Identifier: 47:BC:D5:14:33:F2:ED:85:B9:52:FD:A2:EA:E4:CC:00:7F:7F:19:7E Puppet Node UUID: ED803750-E3C7-44F5-BB08-41A04433FE2E X509v3 Extended Key Usage: critical TLS Web Server Authentication, TLS Web Client Authentication X509v3 Basic Constraints: critical CA:FALSE Puppet Node Preshared Key: 342thbjkt82094y0uthhor289jnqthpc2290 X509v3 Key Usage: critical Digital Signature, Key Encipherment Puppet Node Image Name: my_ami_image
Recommended OIDs for Extensions
Extension request OIDs must be under the “ppRegCertExt” (
220.127.116.11.4.1.34380.1.1) or “ppPrivCertExt” (
18.104.22.168.4.1.34380.1.2) OID arcs.
Puppet provides several registered OIDs (under “ppRegCertExt”) for the most common kinds of extension information, as well as a private OID range (“ppPrivCertExt”) for site-specific extension information. There are several benefits to using the registered OIDs:
- You can reference them in
csr_attributes.yamlwith their short names instead of their numeric IDs.
- You can access them in
$trusted[extensions]with their short names instead of their numeric IDs.
- When using Puppet tools to print certificate info, they will appear using their descriptive names instead of their numeric IDs.
The private range is available for any information you want to embed into a certificate that isn’t already in wide use elsewhere. It is completely unregulated, and its contents are expected to be different in every Puppet deployment.
You can use the
custom_trusted_oid_mapping.yaml file to set short names for any private extension OIDs you use. Note that this only enables the short names in the
Puppet-Specific Registered IDs
The “ppRegCertExt” OID range contains the following OIDs:
|Numeric ID||Short Name||Descriptive Name|
||Puppet Node UUID|
||Puppet Node Instance ID|
||Puppet Node Image Name|
||Puppet Node Preshared Key|
||Puppet Node Cost Center Name|
||Puppet Node Product Name|
||Puppet Node Project Name|
||Puppet Node Application Name|
||Puppet Node Service Name|
||Puppet Node Employee Name|
||Puppet Node Environment Name|
||Puppet Node Role Name|
||Puppet Node Software Version|
||Puppet Node Department Name|
||Puppet Node Cluster Name|
||Puppet Node Provisioner Name|
||Puppet Node Region Name|
||Puppet Node Datacenter Name|
||Puppet Node Zone Name|
||Puppet Node Network Name|
||Puppet Node Security Policy Name|
||Puppet Node Cloud Platform Name|
||Puppet Node Application Tier|
||Puppet Node Hostname|
AWS Attributes and Extensions Population Example
You can use an automated script (possibly using cloud-init or a similar tool) to populate the
csr_attributes.yaml file when you provision a node.
As an example, you can enter the following script into the “Configure Instance Details —> Advanced Details” section when provisioning a new node from the AWS EC2 dashboard:
#!/bin/sh if [ ! -d /etc/puppetlabs/puppet ]; then mkdir /etc/puppetlabs/puppet fi cat > /etc/puppetlabs/puppet/csr_attributes.yaml << YAML custom_attributes: 1.2.840.113522.214.171.124: mySuperAwesomePassword extension_requests: pp_instance_id: $(curl -s http://169.254.169.254/latest/meta-data/instance-id) pp_image_name: $(curl -s http://169.254.169.254/latest/meta-data/ami-id) YAML
Assuming your image has the
erb binary available, this populates the attributes file with the AWS instance ID, image name, and a pre-shared key to use with policy-based autosigning.
Recovering from Failed Data Embedding
When first testing this feature, you might fail to embed the right information in a CSR or certificate and want to start over for your test nodes. (This is not really a problem once your provisioning system is changed to populate the data, but it can happen pretty easily when doing things manually.)
To start over, do the following:
On the test node:
- Turn off Puppet agent, if it’s running.
- Check whether a CSR is present; it will be at
$ssldir/certificate_requests/<name>.pem. If it exists, delete it.
- Check whether a certificate is present; it will be at
$ssldir/certs/<name>.pem. If it exists, delete it.
On the CA Puppet master:
- Check whether a signed certificate exists; use
puppet cert list --allto see the complete list. If it exists, revoke and delete it with
puppet cert clean <name>.
- Check whether a CSR for the node exists; it will be in
$ssldir/ca/requests/<name>.pem. If it exists, delete it.
After you’ve done that, you can start over.