The contents of a data bag can be encrypted.
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. |
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
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.
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"
}
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"
}
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
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.