9/5/2014
5 min read
Chef has so many ways of working with data, some people call it "getting Chefed in the face". Well, I got "Chefed" in the ass last week with encrypted data bags. Here's the story and the solution so that you don't have to lose as much hair as I did over the whole thing.
What is a Chef Data Bag? Its one (of many) ways to handle data specific to a node or set of nodes. In its simplist form its a JSON object or a collection of JSON objects.
This is an example of a Data Bag (for my user in production):
{
"id": "mikeheijmans",
"groups": [
"awesome"
],
"shell": "/bin/bash",
"comment": "Mike Heijmans",
"ssh_keys": [
"1231231313.key1",
"2213112413.key2"
]
}
The only required field here is id
and is used to identify the Data Bag inside the code and on the chef server.
Everything else in the data bag can be accessed and used within your chef recipes and is most commonly used for setting variables in file templates or setting attributes for a node.
An encrypted data bag is exactly what it sounds like. Instead of the json being in plain text, you can encrypt the data on the chef server and decrypt it inside your recipes using the secret-file
(usually a .pem) or secret
(passphrase).
This is what a similar data bag looks like when its encrypted (similar because I don't want you trying to figure out the key you nefarious little hacker :smile:):
{
"id": "my_encrypted_dbag",
"data_item1": {
"encrypted_data": "bV/6HaMMw0lX85Bmxpci6wtTgoIgufL6mwTp\nmdERiv8EcIh40+k5VPMNkSoeeQ==\n",
"iv": "kffXFaiYhWxQh1npHsbYw==\n",
"version": 1,
"cipher": "aes-256-cbc"
},
"data_item2": {
"encrypted_data": "BkifX++NPJZiRnEU76FYOYGy1HS4J0Wb9EXU36xAUaAAs0QgR1f3w9Iv0e\nPsrrSyQotTgk2niMNyHdfDguTqFlVsefm1W2tA9DV/BrI17T+xjPnN50M+xv\nu7i5o5Ik\n",
"iv": "Ecjf4Y6KZVEFOGDTHJDw==\n",
"version": 1,
"cipher": "aes-256-cbc"
}
}
This is decrypted in the recipes like this:
secret = Chef::EncryptedDataBagItem.load_secret(Chef::Config[:encrypted_data_bag_secret])
my_encrypted_dbag = Chef::EncryptedDataBagItem.load("my_data_bag", "my_encrypted_dbag", secret)
You can encrypt a Data Bag on upload by running:
knife data bag from file my_data_bag my_encrypted_dbag.json --secret-file .chef/my_secret.pem
If you save the encrypted version in the JSON file in your source... you would upload like this:
knife data bag from file my_data_bag my_encrypted_dbag.json
note: this doesn't have the secret-file because its encrypted locally..
We have a setting in our our .chef/knife.rb
file like this:
knife[:secret_file] = "#{current_dir}/my_secret.pem"
What this little one-line setting does is cause all data bag commands with knife to automatically have the
--secret-file ./chef/my_secret.pem
appended.
At my previous job, we would use the repo as the source of truth and always upload all when making a change:
knife data bag from file -a
This uploads all the Data Bags in source.
... so do you see the problem here?
The Data Bag files in source where saved in the encrypted format... when I ran the "upload all" command, it encrypted the encrypted Data Bag and uploaded it. Double encrytped! dun Dun DUN! (yo dawg, I heard you like encryption..)
So now I have a few problems...
good news... chef failed fast and didn't break anything except itself
When I run:
knife data bag show my_data_bag my_encrypted_dbag --secret-file ./chef/my_secret.pem
I get:
{
"id": "my_encrypted_dbag",
"data_item1": {
"encrypted_data": "bV/6HaMMw0lX85Bmxpci6wtTgoIgufL6mwTp\nmdERiv8EcIh40+k5VPMNkSoeeQ==\n",
"iv": "kffXFaiYhWxQh1npHsbYw==\n",
"version": 1,
"cipher": "aes-256-cbc"
---snip---
This is exactly what the client is getting back when it decrypts the Data Bag and why everything is breaking.
The question here is: How do I decrypt the data and then upload the returned (single-encrypted) version so Chef works?
Here is what I did:
First, I commented out the auto encryption line from .chef/knife.rb
:
# knife[:secret_file] = "#{current_dir}/my_secret.pem"
this will prevent any confusion with what's getting encrypted and what's not
Then I simply re-upload the single encrypted version in the Repo (remember the json is already saved encrypted):
knife data bag from file -a
This will overwrite the data on the Chef server with the single-encrypted version and fix everything. (and it did)
After doing a ton of research and even talking to a Chef consultant about this, I have come up with a few solutions to prevent this from happening in the future. (in order of my liking)
I am a fan of number 1 because it provides a simple way to maintain Data Bags, allows for CI/CD to handle your Data Bag changes, and it makes Data Bag changes review-able by your team, but every case is different and you need to find your own way through the weeds here.
I hope this helps you learn through my mistakes and prevents a bit of headache and hair loss in the future.