Cryptocracy

A blog

(Very) Simple x509 PKI with Chef

Last year, Venda released a project to create and manage a simple x509 PKI using Chef and Chris Andrews introduced it with his blog post, “Deploying a PKI With Chef”.

A few people tried out it out after the initial release (and submitted patches or bug reports – thankyou!), and it has since been renamed to become the x509 cookbook, which you can find on the community site or on github.

I’ve found it useful of late, so let’s take another look.

What’s The Problem?

You’ve decided to SSL-enable one of your internal services, and that means you need an x509 certificate. The cheapest and easiest option is to generate a self-signed certificate, but this option is not without drawbacks.

When you connect to a service using a self-signed certificate, you can be confident that your communication is encrypted, but you can’t be sure who you’re communicating with. You are protected from attackers “sniffing” data from an insecure network, but not from attackers creating a fake service in front of the one you expect to connect to (a man-in-the-middle attack).

It’s also annoying to users, as most software will (rightly!) warn you that self-signed certificates are not to be trusted.

A better option is to run an internal Certificate Authority, and use that to sign the certificates for your SSL-enabled services. You can import your CA’s certificate into your browser (or OS), which will then trust services using certificates that it has signed.

It’s not hard to make your own CA, but getting a signed certificate for your service necessarily involves a number of steps:

  1. On the host, generate a secret key and a certificate signing request (CSR)
  2. Get the CSR to the internal CA
  3. Create a signed certificate using the CSR and the internal CA
  4. Get the signed certificate to the host
  5. Install the signed certificate

Venda wanted to automate this process and the result is the x509 cookbook, and the chef-ssl-client gem.

1. Installation

Note: these tools work with chef-client and the Chef server – chef-solo is not supported.

Certificate Authority

To create and manage a CA, we will be using the chef-ssl utility. It loads your knife configuration to determine how to connect to the Chef server, so you’ll have to install it somewhere you can already use knife.

The chef-ssl utility is provided by the chef-ssl-client gem.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ gem install chef-ssl-client --no-rdoc --no-ri
Fetching: chef-ssl-client-1.0.5.gem (100%)
Successfully installed chef-ssl-client-1.0.5

$ chef-ssl --help
  chef-ssl

  Chef-automated SSL certificate signing tool

  Commands:
    autosign             Search for CSRs and sign them with the given CA
    help                 Display global or [command] help documentation.
    issue                Issue an ad hoc certificate
    makeca               Creates a new CA
    search               Searches for outstanding CSRs
    sign                 Search for the given CSR by name and provide a signed certificate

  Global Options:
    -h, --help           Display help documentation
    -v, --version        Display version information
    -t, --trace          Display backtrace when an error occurs

Chef

To create certificates, we’ll need the x509 cookbook on our Chef server. Some of this cookbook’s functionality relies on the vt-gpg cookbook, which is listed as a dependency. We won’t be using that functionality today, but Chef will insist that we upload both.

Both cookbooks are available on the community site, so downloading and installing them using Knife might look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ knife cookbook site install x509
Installing x509 to /Users/zts/code/chef-repo/cookbooks
Checking out the master branch.
Creating pristine copy branch chef-vendor-x509
...
Cookbook x509 version 1.0.3 successfully installed
Installing vt-gpg to /Users/zts/code/chef-repo/cookbooks
Checking out the master branch.
Creating pristine copy branch chef-vendor-vt-gpg
...
Cookbook vt-gpg version 1.0.0 successfully installed
$ knife cookbook upload x509 vt-gpg
Uploading vt-gpg         [1.0.0]
Uploading x509           [1.0.3]
Uploaded 2 cookbooks.

2. Usage

Creating a Certificate Authority

To sign certificates, we’re going to need a CA. This is accomplished using the chef-ssl makeca command on the host where you installed chef-ssl-client. The required arguments are --dn (the Distinguished Name of the CA), and --ca-path, the place where the CA’s files will be stored. The DN will be visible in the CA certificate, and any certificates it signs.

1
2
3
4
5
6
$ chef-ssl makeca --dn '/CN=My Test CA' --ca-path ./testCA
New CA DN: /CN=My Test CA
Enter new CA passphrase:
Re-enter new CA passphrase:

Creating new CA: done

Defining a certificate

To create a certificate on one of our Chef nodes, we use the x509_certificate LWRP provided by the x509 cookbook. This LWRP has some dependencies, which are installed by the default recipe – but this means that recipe[x509::default] needs to be applied. It doesn’t matter whether you put it on the run_list or load it using include_recipe, but listing it as a dependency in another cookbook’s metadata.rb is not enough.

In this example, we’re creating a server certificate for “www.example.com”, and indicating that we’d like it to be signed by “testCA”.

1
2
3
4
5
6
7
8
9
10
include_recipe "x509::default"

x509_certificate "www.example.com" do
  certificate  "/etc/ssl/www.example.com.cert"
  key          "/etc/ssl/www.example.com.key"
  ca           "testCA"
  type         "server"
  bits         2048
  days         365
end

The first time we run chef-client with this recipe, we’ll see logs similar to this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[zts@test-centos6]~% sudo chef-client --force-logger
[2013-04-20T19:14:35+00:00] INFO: *** Chef 11.4.0 ***
[2013-04-20T19:14:36+00:00] INFO: Run List is [recipe[x509::example]]
[2013-04-20T19:14:36+00:00] INFO: Run List expands to [x509::example]
[2013-04-20T19:14:36+00:00] INFO: Starting Chef Run for test-centos6.nat0.cryptocracy.com
[2013-04-20T19:14:36+00:00] INFO: Running start handlers
[2013-04-20T19:14:36+00:00] INFO: Start handlers complete.
[2013-04-20T19:14:36+00:00] INFO: Loading cookbooks [vt-gpg, x509]
[2013-04-20T19:14:36+00:00] WARN: X509 library dependency 'eassl' not loaded: cannot load such file -- eassl
[2013-04-20T19:14:36+00:00] INFO: Processing chef_gem[eassl2] action nothing (x509::default line 25)
[2013-04-20T19:14:36+00:00] INFO: Processing chef_gem[eassl2] action upgrade (x509::default line 25)
[2013-04-20T19:16:21+00:00] INFO: chef_gem[eassl2] upgraded from uninstalled to 2.0.1
[2013-04-20T19:16:21+00:00] INFO: Processing chef_gem[eassl2] action nothing (x509::default line 25)
[2013-04-20T19:16:21+00:00] INFO: Processing x509_certificate[www.example.com] action create (x509::example line 3)
[2013-04-20T19:16:21+00:00] INFO: Processing file[/etc/ssl/www.example.com.cert] action create (/var/cache/chef/cookbooks/x509/providers/certificate.rb line 5)
[2013-04-20T19:16:21+00:00] INFO: entered create
[2013-04-20T19:16:21+00:00] INFO: file[/etc/ssl/www.example.com.cert] owner changed to 0
[2013-04-20T19:16:21+00:00] INFO: file[/etc/ssl/www.example.com.cert] group changed to 0
[2013-04-20T19:16:21+00:00] INFO: file[/etc/ssl/www.example.com.cert] mode changed to 644
[2013-04-20T19:16:21+00:00] INFO: file[/etc/ssl/www.example.com.cert] created file /etc/ssl/www.example.com.cert
[2013-04-20T19:16:21+00:00] INFO: Processing file[/etc/ssl/www.example.com.key] action create (/var/cache/chef/cookbooks/x509/providers/certificate.rb line 11)
[2013-04-20T19:16:21+00:00] INFO: entered create
[2013-04-20T19:16:21+00:00] INFO: file[/etc/ssl/www.example.com.key] owner changed to 0
[2013-04-20T19:16:21+00:00] INFO: file[/etc/ssl/www.example.com.key] group changed to 0
[2013-04-20T19:16:21+00:00] INFO: file[/etc/ssl/www.example.com.key] mode changed to 600
[2013-04-20T19:16:21+00:00] INFO: file[/etc/ssl/www.example.com.key] created file /etc/ssl/www.example.com.key
[2013-04-20T19:16:22+00:00] INFO: Chef Run complete in 106.180117863 seconds
[2013-04-20T19:16:22+00:00] INFO: Running report handlers
[2013-04-20T19:16:22+00:00] INFO: Report handlers complete

Three things have happened during this run.

  1. The eassl gem is installed, required by the x509_certificate LWRP.
  2. A self-signed certificate is generated and installed.
  3. A Certificate Signing Request (CSR) is created and saved in the csr_outbox attribute on the node.

We can check the last action using knife node show:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$ knife node show test-centos6.nat0.cryptocracy.com -a csr_outbox
test-centos6.nat0.cryptocracy.com:
  csr_outbox:
    www.example.com:
      ca:   testCA
      csr:  -----BEGIN CERTIFICATE REQUEST-----
      MIIC7DCCAdQCAQAwgaYxCzAJBgNVBAYTAkdCMQ8wDQYDVQQIEwZMb25kb24xDzAN
      BgNVBAcTBkxvbmRvbjEUMBIGA1UEChMLRXhhbXBsZSBMdGQxHzAdBgNVBAsTFkNl
      cnRpZmljYXRlIEF1dG9tYXRpb24xGDAWBgNVBAMTD3d3dy5leGFtcGxlLmNvbTEk
      MCIGCSqGSIb3DQEJARMVeDUwOS1hdXRvQGV4YW1wbGUuY29tMIIBIjANBgkqhkiG
      9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv6N6WZIiwwuOcFIni9b7lEcbd/Djpe00TZ7B
      mB8dJa3pFZM3Le5MsUtswwIMe9GpFyDNBImYb0ebTsOwGykqWg4qjG0/tNu0DARt
      9JX3SKeC31h9ShUt2XOL5ZpGqMlDWQY2qdKZnA0ge+vmV1UW2fyb1e0Wwyi+CgUK
      fZd6A3JWMd1TykVHZQJwt3xuTYiu/6wZbOj9OLlNqOFjEtnIstXgKTBhI3qs3Zuj
      lQU6FEWdqghzwaPSXG1/tNQn6Ql1wKsnbflONlzQbjLdaItDRnYzj5KlYroXChjH
      tE4Y4f6KC/BhQlENfytSgGOC9W+KWtaAfIIg/E+YLHFa8QmqLwIDAQABoAAwDQYJ
      KoZIhvcNAQEFBQADggEBAIW3og6ARIJFs4Ipj7AEebN0E8YYMsgBhqtAlg7DkLZt
      myQeCpL8Gn04tkCYYkvh+BoaE0PZ7wvFRkdphQ6Fvxji4CaDoeItSppM+C79nqo/
      gfi1xmufb1d0RKwDua8nLN428SddWeTMGw65X7GfXkl9JQcEpmA4ecKmVMDUUgNo
      dNRRJpSKT+a+OPT+9PFXY9ApSKENOSkJn/XtF8Dnx1xI5WBL7eYzRyM1es44RcuN
      OtKdA1u/qtFAX1gRrvTvlqmVbevC1kI5Wx7tAmvNCkQ2RrhI9vdwFS3pGpiNpKRx
      eENUk/N/rhOPXO9/v0JHZX455UZh5EqINoZ7eGrY4hE=
      -----END CERTIFICATE REQUEST-----

      date: 2013-04-20 19:16:21 +0000
      days: 365
      id:   80fc0fb9266db7b83f85850fa0e6548b6d70ee68c8b5b412f1deea6ebdef0404
      key:
      type: server

Signing a CSR (chef-ssl)

After our node has created a signing request, we return to the chef-ssl utility to process it. The chef-ssl autosign command finds pending CSRs for a given CA by matching the --ca-name argument against the ca parameter used in the x509_certificate resource. You’ll be asked whether you’d like to sign each request it finds.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
$ chef-ssl autosign --ca-name="testCA" --ca-path=./testCA
Enter CA passphrase:
Search CA: testCA
     Node Hostname: test-centos6.nat0.cryptocracy.com
  Certificate Type: server
    Certificate DN: /C=GB/ST=London/L=London/O=Example Ltd/OU=Certificate Automation/CN=www.example.com/emailAddress=x509-auto@example.com
      Requested CA: testCA
Requested Validity: 365 days
-----BEGIN CERTIFICATE REQUEST-----
MIIC7DCCAdQCAQAwgaYxCzAJBgNVBAYTAkdCMQ8wDQYDVQQIEwZMb25kb24xDzAN
BgNVBAcTBkxvbmRvbjEUMBIGA1UEChMLRXhhbXBsZSBMdGQxHzAdBgNVBAsTFkNl
cnRpZmljYXRlIEF1dG9tYXRpb24xGDAWBgNVBAMTD3d3dy5leGFtcGxlLmNvbTEk
MCIGCSqGSIb3DQEJARMVeDUwOS1hdXRvQGV4YW1wbGUuY29tMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv6N6WZIiwwuOcFIni9b7lEcbd/Djpe00TZ7B
mB8dJa3pFZM3Le5MsUtswwIMe9GpFyDNBImYb0ebTsOwGykqWg4qjG0/tNu0DARt
9JX3SKeC31h9ShUt2XOL5ZpGqMlDWQY2qdKZnA0ge+vmV1UW2fyb1e0Wwyi+CgUK
fZd6A3JWMd1TykVHZQJwt3xuTYiu/6wZbOj9OLlNqOFjEtnIstXgKTBhI3qs3Zuj
lQU6FEWdqghzwaPSXG1/tNQn6Ql1wKsnbflONlzQbjLdaItDRnYzj5KlYroXChjH
tE4Y4f6KC/BhQlENfytSgGOC9W+KWtaAfIIg/E+YLHFa8QmqLwIDAQABoAAwDQYJ
KoZIhvcNAQEFBQADggEBAIW3og6ARIJFs4Ipj7AEebN0E8YYMsgBhqtAlg7DkLZt
myQeCpL8Gn04tkCYYkvh+BoaE0PZ7wvFRkdphQ6Fvxji4CaDoeItSppM+C79nqo/
gfi1xmufb1d0RKwDua8nLN428SddWeTMGw65X7GfXkl9JQcEpmA4ecKmVMDUUgNo
dNRRJpSKT+a+OPT+9PFXY9ApSKENOSkJn/XtF8Dnx1xI5WBL7eYzRyM1es44RcuN
OtKdA1u/qtFAX1gRrvTvlqmVbevC1kI5Wx7tAmvNCkQ2RrhI9vdwFS3pGpiNpKRx
eENUk/N/rhOPXO9/v0JHZX455UZh5EqINoZ7eGrY4hE=
-----END CERTIFICATE REQUEST-----

Sign with: /CN=My Test CA
Sign this? (yes or no)
yes
Signed: SHA1 Fingerprint=67:39:44:79:31:74:50:E5:D0:54:4F:4E:6B:E5:E5:DB:2A:E8:7F:10
-----BEGIN CERTIFICATE-----
MIIDfjCCAuegAwIBAgIBBDANBgkqhkiG9w0BAQUFADAVMRMwEQYDVQQDDApNeSBU
ZXN0IENBMB4XDTEzMDQyMDE5MjAxMFoXDTE0MDQyMDE5MjAxMFowgaYxCzAJBgNV
BAYTAkdCMQ8wDQYDVQQIEwZMb25kb24xDzANBgNVBAcTBkxvbmRvbjEUMBIGA1UE
ChMLRXhhbXBsZSBMdGQxHzAdBgNVBAsTFkNlcnRpZmljYXRlIEF1dG9tYXRpb24x
GDAWBgNVBAMTD3d3dy5leGFtcGxlLmNvbTEkMCIGCSqGSIb3DQEJARMVeDUwOS1h
dXRvQGV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
v6N6WZIiwwuOcFIni9b7lEcbd/Djpe00TZ7BmB8dJa3pFZM3Le5MsUtswwIMe9Gp
FyDNBImYb0ebTsOwGykqWg4qjG0/tNu0DARt9JX3SKeC31h9ShUt2XOL5ZpGqMlD
WQY2qdKZnA0ge+vmV1UW2fyb1e0Wwyi+CgUKfZd6A3JWMd1TykVHZQJwt3xuTYiu
/6wZbOj9OLlNqOFjEtnIstXgKTBhI3qs3ZujlQU6FEWdqghzwaPSXG1/tNQn6Ql1
wKsnbflONlzQbjLdaItDRnYzj5KlYroXChjHtE4Y4f6KC/BhQlENfytSgGOC9W+K
WtaAfIIg/E+YLHFa8QmqLwIDAQABo4HHMIHEMAkGA1UdEwQCMAAwHQYDVR0OBBYE
FOuq7ztithiBI1Al05/OrAliIVV7MDcGCWCGSAGG+EIBDQQqFihSdWJ5L09wZW5T
U0wvRWFTU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMD0GA1UdIwQ2MDSAFA6/VF54
PUt7pkF5z9ZYUOOS7iDtoRmkFzAVMRMwEQYDVQQDDApNeSBUZXN0IENBggEAMAsG
A1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQUFAAOB
gQA73eaeESHnYeWjk8uOg5qq4VL+rLk+M/MB3rON4g6lUzBVBUzTEULU+ziGEnWw
1f8EO99GVPf1hAywcfycj2GskkPbg4JgBHFQ3EeQVYEBzQcKCLqboRaUsgXqpDlg
OGd5BpT33Jh2E8u/5G81WGhCScIVHpH1JwGyAkwvsnyokw==
-----END CERTIFICATE-----

Saved OK
All CSRs processed.

Signed certificates are saved to the “certificates” data bag on the Chef server.

1
2
3
4
5
$ knife search certificates "host:test-centos6.nat0.cryptocracy.com" -a dn
1 items found

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

Installing the signed certificate

To update the node with our newly-issued certificate, we run chef-client once again.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[zts@test-centos6]~% sudo chef-client --force-logger
[2013-04-20T19:36:45+00:00] INFO: *** Chef 11.4.0 ***
[2013-04-20T19:36:45+00:00] INFO: Run List is [recipe[x509::example]]
[2013-04-20T19:36:45+00:00] INFO: Run List expands to [x509::example]
[2013-04-20T19:36:45+00:00] INFO: Starting Chef Run for test-centos6.nat0.cryptocracy.com
[2013-04-20T19:36:45+00:00] INFO: Running start handlers
[2013-04-20T19:36:45+00:00] INFO: Start handlers complete.
[2013-04-20T19:36:45+00:00] INFO: Loading cookbooks [vt-gpg, x509]
[2013-04-20T19:36:45+00:00] INFO: Processing chef_gem[eassl2] action nothing (x509::default line 25)
[2013-04-20T19:36:45+00:00] INFO: Processing chef_gem[eassl2] action upgrade (x509::default line 25)
[2013-04-20T19:36:45+00:00] INFO: Processing chef_gem[eassl2] action nothing (x509::default line 25)
[2013-04-20T19:36:45+00:00] INFO: Processing x509_certificate[www.example.com] action create (x509::example line 3)
[2013-04-20T19:36:45+00:00] INFO: installing certificate www.example.com (id 80fc0fb9266db7b83f85850fa0e6548b6d70ee68c8b5b412f1deea6ebdef0404)
[2013-04-20T19:36:45+00:00] INFO: Processing file[/etc/ssl/www.example.com.cert] action create (/var/cache/chef/cookbooks/x509/providers/certificate.rb line 5)
[2013-04-20T19:36:45+00:00] INFO: file[/etc/ssl/www.example.com.cert] backed up to /var/lib/chef/etc/ssl/www.example.com.cert.chef-20130420193645
[2013-04-20T19:36:45+00:00] INFO: file[/etc/ssl/www.example.com.cert] contents updated
v[2013-04-20T19:36:45+00:00] INFO: Processing file[/etc/ssl/www.example.com.key] action nothing (/var/cache/chef/cookbooks/x509/providers/certificate.rb line 11)
[2013-04-20T19:36:45+00:00] INFO: Chef Run complete in 0.351435563 seconds
[2013-04-20T19:36:45+00:00] INFO: Running report handlers
[2013-04-20T19:36:45+00:00] INFO: Report handlers complete

We can use openssl to confirm that the certicate was signed by the CA we requested:

1
2
[zts@test-centos6]~% openssl x509 -in /etc/ssl/www.example.com.cert -issuer | head -1
issuer= /CN=My Test CA

Conclusion

The x509 cookbook takes some of the pain out of generating SSL certificates, signing them with a private CA, and installing them on your nodes.

There’s room for improvement, but I already find it useful. Bug reports, feature requests, and PRs will all be warmly received.

Comments