Chef Knife script for encrypting a file into a data bag

Presents a custom Knife script that streamlines encrypting file contents into Chef data bags, avoiding manual JSON wrapper steps for multiline key material. Useful for securely distributing credentials in delivery pipelines.

Coveros Staff

July 29, 2014

In our current continuous delivery pipeline, we have to distribute a number of secure keys to various servers for access to different resources. It’s common to use encrypted data bags in Chef to store protected values such as passwords or, in my case, SSH keys. The typical process for doing this is:

1. Create the secure key (which is basically a string of characters)
2. Create a data bag item JSON file and copy the key text into it
3. Use knife command to upload and encrypt the data bag item (“knife data bag from file”)

This became slightly problematic for me because an RSA key file has line-breaks and other characters that Knife’s JSON parser doesn’t like. To make this easier, I wrote a simple Ruby Knife script to do combine steps 2 and 3. This is an interesting exercise for a couple reasons. First, it simplifies the process so I don’t have to create a semi-pointless JSON wrapper for the text of another file. More importantly, it shows how we can use the standard Chef API calls in a knife script, similar to the code we write in a recipe to access data bag items.

databag_encrypt_file.krb

#!/usr/bin/knife exec
# Knife exec script to put the contents of a file into a data bag, then encrypt it.
#
########### USAGE ############
this\_file = File.basename(\_\_FILE\_\_)
usage = <<-EOS

#{this\_file}: Encrypts and stores the contents of a file into a data bag item. This
is typically used to encrypt and store the contents of a PEM file.

usage:
  knife exec #{this\_file} {filename} {databag} {databag\_item} {secret\_key\_file}

example:
  knife exec #{this\_file} foo.pem foo\_bag foo\_item my\_secret.pem

Use 'knife data bag show foo\_bag foo\_item --secret-file my\_secret.pem' to verify.
EOS
############ USAGE ############

filename = ARGV\[2\]
data\_bag\_name = ARGV\[3\]
data\_bag\_item\_name = ARGV\[4\]
encryption\_key\_file = ARGV\[5\]

abort usage if (encryption\_key\_file.nil? || (encryption\_key\_file == ""))

# See if the data bag exists yet
begin
	data\_bag = data\_bag(data\_bag\_name)
	puts "Data bag #{data\_bag\_name} already exists."
rescue
    puts "Creating new data bag #{data\_bag\_name}"
	bag = Chef::DataBag.new
	bag.name(data\_bag\_name)
	bag.create
end

puts "Storing contents of #{filename} in item #{data\_bag\_item\_name}"

content = File.read(filename)

# Set up the un-encrypted contents of the data bag
bag\_item = Chef::DataBagItem.new
bag\_item.data\_bag(data\_bag\_name)
bag\_item\[:comment\] = "Data bag automatically generated from file #{filename} by databag\_encrypt\_file.krb"
bag\_item\[:filename\] = File.basename(filename)
bag\_item\[:content\] = content
bag\_item\[:id\] = data\_bag\_item\_name

puts "Encrypting with key #{encryption\_key\_file}"

# Now, encrypt the data bag contents into a new data bag
bag\_hash = bag\_item.to\_hash
secret = Chef::EncryptedDataBagItem.load\_secret(encryption\_key\_file)
enc\_hash = Chef::EncryptedDataBagItem.encrypt\_data\_bag\_item(bag\_hash, secret)
ebag\_item = Chef::DataBagItem.from\_hash(enc\_hash)
ebag\_item.data\_bag(data\_bag\_name)
ebag\_item.save

puts "Success. Use command to verify contents:"
puts "  knife data bag show #{data\_bag\_name} #{data\_bag\_item\_name} --secret-file #{encryption\_key\_file}"

# Need this, or knife exec attempts to execute your parameters as new scripts
exit 0

This allows me to use a simple command to upload my PEM file:

knife exec databag_encrypt_file.krb my_key.pem dev_keys key1 ~/keys/databag_secret.key

I can verify that the key was uploaded properly with the following command:

knife data bag show dev_keys key1 --secret-file ~/keys/databag_secret.key

The key file is now available for all recipes using standard Chef::DataBagItem API calls. Of course, it requires that the “databag_secret.key” file be available for both Knife and Chef-Client nodes. I call that the “root key distribution” problem and that’s another topic entirely.

Coveros Staff

Coveros Staff

This post represents the collective insights of the Coveros team. Our staff consists of software experts who bring deep experience in secure agile development, DevOps, testing, and software quality. Over the past 20 years, Coveros has trained more than 30,000 professionals and worked with half of the Fortune 100 companies on mission-critical software development challenges. We draw on this extensive experience to share practical insights, proven strategies, and real-world solutions that help organizations build better software faster and more securely.