Opscode

Table Of Contents

Encrypt a Data Bag

The contents of a data bag can be encrypted.

  • An encrypted data bag is always written using JSON as the serialization format. Chef and Knife support reading encrypted data bag items in both JSON (default) and YAML (backup).
  • Data is encrypted using a combination of JSON, AES-256-CBC (as defined by the openssl package in the Ruby Standard Library), and Base64 encoding. This allows a data bag to store confidential information (such as a database password) or to be managed in a source control system (without plain-text data appearing in revision history).
  • The system uses shared-key encryption. An encrypted file can only be decrypted by a node or a user with the same shared-key. Only designated nodes are able to decrypt values stored in an encrypted data bag.
  • A recipe can load encrypted data as long as the shared secret is present in a file on the node or is accessible from a URI path.
  • Only the values of a data bag item are decrypted; keys are still searchable. The values associated with the id key of a data bag item are not encrypted (because they are needed by Chef when tracking the data bag item).

Knife Options for Encryption

Knife can encrypt and decrypt data when the knife data bag sub-command is run with the create, edit, from file, or show arguments and the following options:

Option Description
--secret SECRET The encryption key that is used for values contained within a data bag.
--secret-file FILE The path to the file that contains the encryption key.

Secret Keys

An encrypted data bag requires a secret key. OpenSSL can be used to generate a secret key with the following syntax:

$ openssl rand -base64 512 > encrypted_data_bag_secret

where encrypted_data_bag_secret is the name of the file which contains the secret key. For example, to create a secret key named “secret_key”:

$ openssl rand -base64 512 > secret_key

and then to use it to encrypt a data bag item named “passwords” located in a data bag named “production”:

$ knife data bag create --secret-file ./secret_key production passwords

This will open the JSON editor:

{
  "id": "passwords",
  "mysql": "open-sesame-123",
  "rabbitmq": "open-queue-123"
}

To view the data bag item without decryption, enter the following:

$ knife data bag show production passwords

to return something similar to:

id:        passwords
mysql:     xtSxLvqHqPP1gHsqP5SlytFtDIfpWMJebJ2aZPd0mGU=
rabbitmq:  wVcK/OboqpRcfF5fOKlEHKz2ev7CxSrBsoCwWo9Jcko=

and then to view the same data bag item, but with decryption:

$ knife data bag show --secret-file=./secret_key production passwords

to return something similar to:

id:        passwords
mysql:     open-sesame-123
rabbitmq:  open-queue-123

Create an encrypted data bag item

An encrypted data bag item can be created with a Knife command similar to:

$ knife data bag create passwords mysql --secret-file /tmp/my_data_bag_key

where “passwords” is the name of the data bag, “mysql” is the name of the data bag item, and “/tmp/my_data_bag_key” is the path to the location in which the file that contains the secret-key is located. Knife will ask for user credentials before the encrypted data bag item is saved.

Verify that a data bag item is encrypted

When the contents of a data bag are encrypted, they will not be readable until they are decrypted. Encryption can be verified with a Knife command similar to:

$ knife data bag create passwords mysql

where “passwords” is the name of the data bag and “mysql” is the name of the data bag item. This will return something similar to:

{
  "id": "mysql",
  "pass": "trywgFA6R70NO28PNhMpGhEvKBZuxouemnbnAUQsUyo=\n",
  "user": "e/p+8WJYVHY9fHcEgAAReg==\n"
}

Decrypt an encrypted data bag item

An encrypted data bag item can be decrypted with a Knife command similar to:

$ knife data bag show --secret-file /tmp/my_data_bag_key passwords mysql

that will return JSON output similar to:

{
  "id": "mysql",
  "pass": "thesecret123",
   "user": "fred"
}

Store encryption keys on nodes

An encryption key can also be stored in an alternate file on the nodes that need it and specify the path location to the file inside an attribute; however, EncryptedDataBagItem.load expects to see the actual secret as the third argument, rather than a path to the secret file. In this case, you can use EncryptedDataBagItem.load_secret to slurp the secret file contents and then pass them:

# inside your attribute file:
# default[:mysql][:secretpath] = "C:\\chef\\any_secret_filename"
#
# inside your recipe:
# look for secret in file pointed to by mysql attribute :secretpath
mysql_secret = Chef::EncryptedDataBagItem.load_secret("#{node[:mysql][:secretpath]}")
mysql_creds = Chef::EncryptedDataBagItem.load("passwords", "mysql", mysql_secret)
mysql_creds["pass"] # will be decrypted

Example

To demonstrate the use of encrypted data bags on a node, we’ll start by copying the secret_key file to an example node using scp and moving it to /etc/chef/encrypted_data_bag_secret:

scp ./secret_key $MY_NODE_IP:~/
ssh $MY_NODE_IP
sudo mv ./secret_key /etc/chef/encrypted_data_bag_secret

The knife bootstrap sub-command supports the encrypted_data_bag_secret setting in knife.rb. You will want to add this line:

encrypted_data_bag_secret '/path/to/your/data_bag_key'

And change /path/to/your/data_bag_key to the location of where the data bag key is located. When you run knife bootstrap afterwards it automatically adds this line to the client.rb for the node you are bootstrapping and copies the key over.

Next, we’ll create a recipe that will log the decrypted values for demonstration purposes (if these were real secrets, you would want to avoid logging them). Use Knife and run the following:

$ knife cookbook create edb_demo

Then, edit cookbooks/edb_demo/recipes/default.rb so that it contains the following:

# cookbooks/edb_demo/recipes/default.rb
passwords = Chef::EncryptedDataBagItem.load("prod", "passwords")
mysql = passwords["mysql"]
Chef::Log.info("The mysql password is: '#{mysql}'")

Finally, upload the cookbook and run chef-client on the node. You should see something like this:

$ knife cookbook upload edb_demo
# output clipped
knife ssh name:i-8a436fe5 -a ec2.public_hostname 'sudo chef-client'
INFO: *** Chef 0.10.0 ***
INFO: Run List is [recipe[edb_demo]]
INFO: Run List expands to [edb_demo]
INFO: Starting Chef Run for i-8a436fe5
INFO: Loading cookbooks [edb_demo]
INFO: The mysql password is: 'open-sesame-123'
INFO: Chef Run complete in 3.122228 seconds
INFO: Running report handlers
INFO: Report handlers complete

As you can see, the recipe was able to decrypt the values in the encrypted data bag. It did so by using the shared secret located in the default location of /etc/chef/encrypted_data_bag_secret.