Securing Chef Resources Using Databags
Configuration management has become a prominent part of DevOps practices. Either be it a matured Chef and Puppet or new entrants Ansible and SaltStack, all of them are better than another in one or other way. However, playing without securing them is a big security risk and is not in the veins DevOps.
Chef is the most vastly used configuration management tool. In this blog I would be discussing chef use-cases which would help us to secure/encrypt sensitive data such as passwords, keys, etc. in data-bags and how can we access it in recipes, templates, attributes etc. We will also discuss a use-case related to environments.
Creating Data-bags:
We would need to create a data-bag to encrypt the sensitive data. The below command will create a data bag on the chef server and will use default secret key to encrypt the data-bag.
[js]knife data bag create my_databag my_databag_item [/js]
This would create a directory named “my_databag” which is our data-bag and this directory has created a file named “my_databag_item.json” which is a data-bag item.
Let’s say the “plain-text” content of that we want to encrypt is:
[js]{
"id": "my_databag",
"password1": “abc123”,
"password2": “123abc”
}[/js]
And after encryption this file would look like:
[js]{
"id": "my_databag",
"password1": {
"encrypted_data": "XXXXXXXXXXXXXXXXXXXXXXXXX",
"iv": "XYXYXYXYXYYXYXYXYXYXYXY",
"version": 1,
"cipher": "aes-256-cbc"
},
"password2": {
"encrypted_data": "YYYYYYYYYYYYYYYYYYYYYYYYYY",
"iv": "YXYXYXYXYXYXYXYXYXYXYXYX",
"version": 1,
"cipher": "aes-256-cbc"
}
}[/js]
Here are few variation which we generally use:
- We do not want to create data-bag on chef-server but want to create a data-bag locally:
[js]knife data bag create my_databag my_databag_item –local-mode flag[/js]
- We do not want to use a default secret key but instead want to use self-created secret key:
[js]#One of the many ways to create secret key:
openssl rand -base64 512 | tr -d ‘\r\n’ > /path/to/my_secret_key
#Create data-bag using my_secret_key
knife data bag create my_databag my_databag_item –local-mode –secret-file /path/to/my_secret_key[/js]
Now, let’s see how could we use the encrypted data in recipes, templates etc.
- Using data-bags in a recipe: We would first need to load the data-bag in the recipe. Before this, we should know the path of the secret file (say /path/to/my_secret_key) that we have created previously.
#Loading secret key inside the recipe:[js]my_secret_key = Chef::EncryptedDataBagItem.load_secret("/path/to/my_secret_key")[/js]
#Loading data-bag item inside the recipe:
[js]passwords = Chef::EncryptedDataBagItem.load("my_databag", "my_databag_item", my_secret_key)[/js]
In order to use “password1”, we can use:
Example 1: Below recipe is sourcing DB dump in the MySQL. While using chef variable in a big string as below, we use hash(“#”) followed by curly braces(“{}”) as highlighted below:
[js]bash ‘create readonly user’ do</pre>
user ‘mysql’
cwd ‘/tmp’
code <<-EOH
mysql -uroot -p<b>#{passwords[‘password1’]}</b> < /tmp/dummy.sql
EOH
end[/js]Example 2: We can use it without “#” and “{}” when using it outside a string as below:
[js]mysql_service ‘default’ do</pre>
version ‘5.6’
port ‘3306’
bind_address ‘0.0.0.0’
initial_root_password <b>password["password1"]
action [:create, :start]
not_if ‘ps -ef | grep [m]ysqld’
end[/js] - We have directly used the path of the secret key while loading the secret key in the above example. We can do a small improvement by storing the path of secret key inside attribute file as below (assuming we are working inside a cookbook directory).
[js]cat attributes/default.rb</pre>
default["secret_key_path"] = "/path/to/my_secret_key"[/js]We could use it inside the recipe to load secret key:
[js]my_secret_key = Chef::EncryptedDataBagItem.load_secret(#{node[:secret_key_path]}")[/js]
Note: that we have not done any encryption here. This is just an improvement over point
- Using data-bags in a template: We can not directly use data-bags as we have used in recipes. While using templates in a recipe(which is granting a mMySQLuser some privileges), we would have to specifically pass the data-bags as variables inside a template as below:
[js]my_secret_key = Chef::EncryptedDataBagItem.load_secret(#{node[:secret_key_path]}")
passwords = Chef::EncryptedDataBagItem.load("my_databag", "my_databag_item", my_secret_key)
template ‘/tmp/grantPrivileges.txt’ do
source "grantPrivileges.txt.erb"
owner "root"
group "root"
mode 0644
variables(
:passwords => passwords
)
end[/js]Now, inside the template file, we can use “password1” as:
[js]cat templates/grantPriviliges.txt.erb</pre>
grant all on dbname.* to ‘user1’@’%’ identified by ‘<%= @passwords["password"] %>’;[/js] - Playing with data-bags while working on environments: We usually work in multiple environments such as production, uat, qa, dev, etc. We have a set up where we have around 20 environment and all can be created/modified using chef. The problem was that all the passwords were present in plain text in the environment file.
The task was to secure all the passwords using databags. Let’s do it step-by-step as follows:
- Create a new data-bag “environment”.
- Under “environment” data-bag, create a data-bag item for every environment. This is how this will look:
Data-bag:
ls ../../data_bags/environment/
dev.json production.json qa.json uat.json
Environment:
ls ../../environments/
dev.json production.json qa.json uat.jsonLet’s say we have three passwords inside“../../environments/production.json” environment file. We would remove all the three passwords and put them inside “../../data_bags/environment/production.json” data-bag item of “environment” databag.
Later, we remove all their references from the recipes and templates.
Next time, when to set up a new environment (say staging), we would need to create a environment file and it’s corresponding data-bag item inside the environment data-bag.
Well, these are few use-cases which could most frequently be encountered while dealing with data-bags in chef.
Please share in comments section if anyone has a good use-case to share.