By default, the connections between the chef-client and the chef-server are not secured. This is a short post on howto encrypt and verify your connections.

As of chef-11 (unlike chef-10), SSL is enabled by default. But (naturally, as Opscode cannot create trusted certificates for your domain) the certificates are not verified. This essentially means that the connection is not secure at all.

Unless you only use chef in a trusted network, you should invest some time in securing your clients connections.

Installing a certificate on your server

First, grab yourself a certificate for your chef server. This post will use a certificate from cacerg.org.

Installing the certificate to the chef-servers nginx is a little annoying. chef-11 (at least when installed using the official packages from opscode) uses a bundled nginx, instead of a system-wide one. Therefore, you need to change the configuration in /var/opt/chef-server/nginx/etc which feels a little unsafe, as it might get overwritten automatically by upgrades.

Fortunately, as Jeff Blaine pointed out in an email to me, that there’s a chef-server.rb config file which takes care of these problems.

First, set up a resource to reconfigure chef-server when something is changed:

execute 'chef-server-ctl reconfigure' do
  action :nothing
end

Deploy your certificate using the certificate cookbook. After that, notify the reconfigure resource, so it reconfigures in case we renew the certificate later.

# deploy certificate to chef-appliance directory
certificate_manage node['hostname'] do
  cert_file         "#{node['fqdn']}.crt"
  cert_path         '/var/opt/chef-server/nginx/ca'
  data_bag          'certificates'
  data_bag_secret   '/etc/chef/certificate_data_bag_secret'
  create_subfolders false
  notifies          :run, 'execute[chef-server-ctl reconfigure]'
end

Configure the /etc/chef-server/chef-server.rb using a template. Feel free to add additional settings, I chose to disable the webgui. Again, we notify the reconfigure resource to automatically reconfigure chef in case the configuration is changed.

#
# Generated by Chef for <%= node['fqdn'] %>.
# Local modifications will be overwritten.
#
chef_server_webui['enable']  = <%= @webgui.to_s %>
nginx['ssl_certificate']     = '<%= @ssl_certificate %>'
nginx['ssl_certificate_key'] = '<%= @ssl_certificate_key %>'
template '/etc/chef-server/chef-server.rb' do
  mode      00644
  source    'chef-server.rb.erb'
  variables ssl_certificate:     '/var/opt/chef-server/nginx/ca/chef.yourcompany.com.crt',
            ssl_certificate_key: '/var/opt/chef-server/nginx/ca/chef.yourcompany.com.key',
            webgui:              false
  notifies  :run, 'execute[chef-server-ctl reconfigure]'
end

Tell your chef-clients to verify peers

If you use the chef-client cookbook you can tell your clients to verify the certificate by overriding the following attributes in your wrapper cookbook (support for verify_mode was added in chef-client-0.3.0). Note, that you need to specify ssl_ca_path manually, as it is not set by default.

override['chef_client']['config']['chef_server_url'] = 'https://chef.yourcompany.com'
override['chef_client']['config']['ssl_verify_mode'] = 'verify_peer'
override['chef_client']['config']['ssl_ca_path'] = '/etc/ssl/certs'

Or do it manually in your client.rb

chef_server_url 'https://chef.yourcompany.com'
ssl_verify_mode :verify_peer
ssl_ca_path     '/etc/ssl/certs'

If you are using cacert, you may need to fix the cacert root certificate (see this gist. You can use the cacert cookbook to do so automatically.

include_recipe 'cacert:cacert.org'

Be careful with the remote_file resource!

What I also stumbled across is, that the remote_file resource doesn’t check certificates for https addresses either. So you might be very cautious if you use it for sensitive data, or to download scripts from the internet that will be executed later. I’m sure you do not want a script run by root modified on the way.