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:
- Install the chef-ssl-client gem:
gem install chef-ssl-client
- Install the x509 cookbook:
knife cookbook site install x509
- 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
- Create the ‘certificates’ data bag:
knife data bag create certificates
- 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
- 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
- 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.