jtimberman's Code Blog

Chef, Ops, Ruby, Linux/Unix. Opinions are mine, not my employer's (Chef).

Encrypted Data Bag for Postfix SASL Authentication

I recently had a chance to sit down and implemented an encrypted data bag in my personal environment. This should translate nicely to anyone that wants to use encrypted data bags in their environment.

The use case

I send mail out through an SASL authenticated SMTP server. My local network has a postfix SMTP relay that connects to the SASL auth relay. I’m using the Opscode postfix cookbook with the sasl_auth recipe, since I wrote it originally for this use case.

The postfix::sasl_auth recipe is applied in an “operations master” role. The attributes for configuring the user and password for SASL are attributes. Relevant lines from the role:

name "ops_master"
run_list("recipe[postfix::sasl_auth]")
override_attributes(
  "postfix" => {
    "relayhost" => "[smtp.example.com]:587",
    "smtp_sasl_auth_enable" => "yes",
    "smtp_sasl_passwd" => "AWESOME!!",
    "smtp_sasl_user_name" => "MYUSER"
  }
)

Encrypted Secrets

Chef’s data bags are a great way to store infrastructure wide, but not role or node specific information. Encrypted data bags are a great way to store sensitive information, like passwords. Here are the steps I followed to get the encrypted data bag set up.

First, I created the secret key file that is used to encrypt the contents of the data bag item. This file will not be stored in source control, as it is highly sensitive, and only gets copied to the systems that need it.

openssl rand -base64 512 > ~/.chef/encrypted_data_bag_secret

Next, I created the actual data bag.

knife data bag create secrets

Next I created the data bag item using the secret key. This is created directly on the Chef Server, rather than a plain text file.

knife data bag create secrets postfix --secret-file ~/.chef/encrypted_data_bag_secret

I’m not saving the plaintext data bag item, but will store the encrypted item, so I’ll retrieve it from the Chef Server and redirect the output to a JSON file.

mkdir data_bags/secrets
knife data bag show secrets postfix -Fj > data_bags/secrets/postfix.json
cat data_bags/secrets/postfix.json
{
  "id": "postfix",
  "user": "encrypted string here",
  "passwd": "encrypted string here"
}

The current recipe doesn’t support using an encrypted data bag item, so I had to modify it. First, load the encrypted data bag item.

postfix_creds = Chef::EncryptedDataBagItem.load("secrets","postfix", 
  Chef::EncryptedDataBagItem::DEFAULT_SECRET_FILE)

This access the “secrets” data bag for the “postfix” item, and uses the default value for the secret key file (which is “/etc/chef/encrypted_data_bag_secret”.

Next, I update the sasl_passwd template to pass in the user and password from the data bag item.

template "/etc/postfix/sasl_passwd" do
  #...
  variables(:smtp_sasl_passwd => smtp_sasl['passwd'],
            :smtp_sasl_user_name => smtp_sasl['user'])
  #...
end

Finally, the template is updated to use the new values.

<%= node[:postfix][:relayhost] %> <%= @smtp_sasl_user_name %>:<%= @smtp_sasl_passwd %>

After uploading the cookbook and the role (I removed the username and password), I copied the secret key file over to the node I needed to run Chef on, then ran Chef Client.

sudo cat /etc/postfix/sasl_passwd
[smtp.example.com]:587 MYUSER:AWESOME!!

Yay!

The full recipe is:

The library file (cookbooks/helper_libs/libraries/encrypted_data_bag_item.rb) is: