Using Chef to Set Up SSL Client Certificates

Previously, I’ve written about setting up Chef Server 12, and then configuring it to use a trusted SSL certificate.

Now, we’re going to go another step, which is to use Chef to generate client certificates by creating a CA, and signing CSRs from clients.

This is based on the documentation for setting up a simple x509 PKI infrastructure with Chef, based on the x509 cookbook. That blog post is very helpful, but is missing some crucial steps, so I’m going to recreate it here and fill in some of the blanks.

Note: Despite my best efforts, I could not get this to work with the default Chef self-signed certificate. This only functioned when I switched to a real SSL certificate. See my previous blog post for a guide.

Set Up the Server:

Most of these instructions will require root or sudo. Also, it’s helpful to use the Ruby provided by Chef, so check which ruby to make sure you’re using the one in /opt/chefdk/embedded/bin/ruby
Let’s go through the process of setting up the x509 cookbook to create a CA for us:

  1. Install the chef-ssl-client gem:
    gem install chef-ssl-client
  2. Install the x509 cookbook:
    knife cookbook site install x509
  3. Replace the x509 cookbook with a fork I created to fix some gaps in the OS X implementation:
    rm -rf chef-repo/cookbooks/x509
    git clone --branch OSX https://github.com/nmcspadden/chef-cookbook-ssl.git x509
  4. Create the ‘certificates’ data bag:
    knife data bag create certificates
  5. Create a test recipe that will install the certificates on the client, located at chef-repo/cookbooks/x509/recipes/chef-ca.rb:
    include_recipe "x509::default"
    
    x509_certificate "chef.sacredsf.org" do
    	certificate "/etc/ssl/chef.sacredsf.org.cert"
    	cacertificate "/etc/ssl/chef_ca.cert"
    	key "/etc/ssl/chef.sacredsf.org.key" 
    	ca "ChefCA" 
    	type "server" 
    	bits 2048 
    	days 365 
    end
    
  6. Now that the recipe is created, we need to upload the cookbooks to the server:
    knife cookbook upload -a
    That command uploads all cookbooks. If you have more than the x509 and vt-gpg cookbooks installed, you can be more precise:
    knife cookbook upload x509 vt-gpg
  7. Finally, create the actual CA. Substitute the ca-path with a valid one of your own:
    chef-ssl makeca --dn '/CN=ChefCA' --ca-path /home/nmcspadden/chefCA
    Enter a passphrase when prompted.

Test It:

Now, it’s time to test it on a Chef client. I tested this on a Mac OS 10.10.2 Yosemite VM, which I set up in the original article about OS X client configuration:
sudo chef-client --force-logger --runlist "recipe[x509::chef-ca]"

You should see output similar to this:

[2015-02-25T13:59:46-08:00] INFO: Processing x509_certificate[chef.sacredsf.org] action create (x509::chef-ca line 3)
[2015-02-25T13:59:46-08:00] INFO: Processing directory[/etc/ssl] action create (/var/chef/cache/cookbooks/x509/providers/certificate.rb line 11)
[2015-02-25T13:59:46-08:00] INFO: directory[/etc/ssl] created directory /etc/ssl
[2015-02-25T13:59:46-08:00] INFO: directory[/etc/ssl] mode changed to 755
[2015-02-25T13:59:46-08:00] INFO: Processing file[/etc/ssl/chef.sacredsf.org.cert] action create (/var/chef/cache/cookbooks/x509/providers/certificate.rb line 17)
[2015-02-25T13:59:46-08:00] INFO: file[/etc/ssl/chef.sacredsf.org.cert] created file /etc/ssl/chef.sacredsf.org.cert
[2015-02-25T13:59:46-08:00] INFO: file[/etc/ssl/chef.sacredsf.org.cert] updated file contents /etc/ssl/chef.sacredsf.org.cert
[2015-02-25T13:59:46-08:00] INFO: file[/etc/ssl/chef.sacredsf.org.cert] owner changed to 0
[2015-02-25T13:59:46-08:00] INFO: file[/etc/ssl/chef.sacredsf.org.cert] group changed to 0
[2015-02-25T13:59:46-08:00] INFO: file[/etc/ssl/chef.sacredsf.org.cert] mode changed to 644
[2015-02-25T13:59:46-08:00] INFO: Processing file[/etc/ssl/chef.sacredsf.org.key] action create (/var/chef/cache/cookbooks/x509/providers/certificate.rb line 23)
[2015-02-25T13:59:46-08:00] INFO: file[/etc/ssl/chef.sacredsf.org.key] created file /etc/ssl/chef.sacredsf.org.key
[2015-02-25T13:59:46-08:00] INFO: file[/etc/ssl/chef.sacredsf.org.key] updated file contents /etc/ssl/chef.sacredsf.org.key
[2015-02-25T13:59:46-08:00] INFO: file[/etc/ssl/chef.sacredsf.org.key] owner changed to 0
[2015-02-25T13:59:46-08:00] INFO: file[/etc/ssl/chef.sacredsf.org.key] group changed to 0
[2015-02-25T13:59:46-08:00] INFO: file[/etc/ssl/chef.sacredsf.org.key] mode changed to 600
[2015-02-25T13:59:46-08:00] INFO: Processing file[/etc/ssl/chef_ca.cert] action create (/var/chef/cache/cookbooks/x509/providers/certificate.rb line 30)
[2015-02-25T13:59:46-08:00] INFO: file[/etc/ssl/chef_ca.cert] created file /etc/ssl/chef_ca.cert
[2015-02-25T13:59:46-08:00] INFO: file[/etc/ssl/chef_ca.cert] updated file contents /etc/ssl/chef_ca.cert
[2015-02-25T13:59:46-08:00] INFO: file[/etc/ssl/chef_ca.cert] owner changed to 0
[2015-02-25T13:59:46-08:00] INFO: file[/etc/ssl/chef_ca.cert] group changed to 0
[2015-02-25T13:59:46-08:00] INFO: file[/etc/ssl/chef_ca.cert] mode changed to 644
[2015-02-25T13:59:46-08:00] INFO: Chef Run complete in 4.990408 seconds

Back on the Chef server / workstation, you can check the CSR in the node’s data. If you don’t know the name of the node, run this command to get a list:
knife node list
In my case, my node’s name is TestVM.local.

Check the CSR:
knife node show TestVM.local -a csr_outbox

TestVM.local:
  csr_outbox:
    chef.sacredsf.org:
      ca:   ChefCA
      csr:  -----BEGIN CERTIFICATE REQUEST-----
      [snip]
      -----END CERTIFICATE REQUEST-----
      
      date: 2015-02-25 13:59:46 -0800
      days: 365
      id:   0f68afcc64e13af040bdf9f6b53d4c701c3614d5c6fb1cb7df5751dbbb92d0aa
      key:  
      type: server

Sign It:

Now we can sign the CSR on the server:
chef-ssl autosign --ca-name="ChefCA" --ca-path=/home/nmcspadden/chefCA
Enter the passphrase you used earlier to generate the CA.

This command will search for all outstanding CSRs and ask you to sign them (which is horribly inefficient and requires lots of manual effort for production use, but that’s a future topic for discussion).

Once you have signed all outstanding CSRs (in this example, only one) by entering “yes” when prompted, the run completes:
Saved OK
All CSRs processed.

Signed certificates are saved into the ‘certificates’ data bag:
knife search certificates "host:TestVM.local" -a dn

1 items found

certificates:
  dn: /C=GB/ST=London/L=London/O=Example Ltd/OU=Certificate Automation/CN=chef.sacredsf.org/emailAddress=x509-auto@example.com

To search for all certificates:
knife search certificates "host:*" -a dn

Finally, return to the client and run chef-client again to complete the certificate generation:
sudo chef-client --force-logger --runlist "recipe[x509::chef-ca]"

[2015-02-25T15:02:11-08:00] INFO: Processing x509_certificate[chef.sacredsf.org] action create (x509::chef-ca line 3)
[2015-02-25T15:02:11-08:00] INFO: installing certificate chef.sacredsf.org (id 0f68afcc64e13af040bdf9f6b53d4c701c3614d5c6fb1cb7df5751dbbb92d0aa)
[2015-02-25T15:02:11-08:00] INFO: Processing directory[/etc/ssl] action create (/var/chef/cache/cookbooks/x509/providers/certificate.rb line 11)
[2015-02-25T15:02:11-08:00] INFO: Processing file[/etc/ssl/chef.sacredsf.org.cert] action create (/var/chef/cache/cookbooks/x509/providers/certificate.rb line 17)
[2015-02-25T15:02:11-08:00] INFO: file[/etc/ssl/chef.sacredsf.org.cert] backed up to /var/chef/backup/etc/ssl/chef.sacredsf.org.cert.chef-20150225150211.867169
[2015-02-25T15:02:11-08:00] INFO: file[/etc/ssl/chef.sacredsf.org.cert] updated file contents /etc/ssl/chef.sacredsf.org.cert
[2015-02-25T15:02:11-08:00] INFO: Processing file[/etc/ssl/chef.sacredsf.org.key] action nothing (/var/chef/cache/cookbooks/x509/providers/certificate.rb line 23)
[2015-02-25T15:02:11-08:00] INFO: Processing file[/etc/ssl/chef_ca.cert] action create (/var/chef/cache/cookbooks/x509/providers/certificate.rb line 30)
[2015-02-25T15:02:11-08:00] INFO: file[/etc/ssl/chef_ca.cert] backed up to /var/chef/backup/etc/ssl/chef_ca.cert.chef-20150225150211.873229
[2015-02-25T15:02:11-08:00] INFO: file[/etc/ssl/chef_ca.cert] updated file contents /etc/ssl/chef_ca.cert
[2015-02-25T15:02:11-08:00] INFO: Chef Run complete in 2.903235 seconds

You can use openssl to verify your certificate:
openssl x509 -in /etc/ssl/chef.sacredsf.org.cert -issuer | head -1

issuer= /CN=ChefCA

Conclusions

Chef now provides a simpler way of generating client certificates than creating your own CA using openssl. However, there’s still some major pitfalls here that get in the way of a strong SSL client certificate solution.

For one thing, signing the CSRs is currently done manually. Ideally, I’d like to set up some kind of automatic script to sign the CSRs based on some policy, so clients can be approved without manual intervention.

Another issue is that because the CSRs require manual approval, it currently takes two chef-client runs to get an SSL cert. The next goal is to get this step down to being more automatic and streamlined.

Leave a comment