diff --git a/Gemfile b/Gemfile index ede1a848..b5bd929a 100644 --- a/Gemfile +++ b/Gemfile @@ -10,3 +10,4 @@ gem 'foodcritic', '~> 4.0' gem 'strainer' gem 'rubocop', '~> 0.18.1' gem 'rake', '~> 10.0' +gem 'chef-vault', '~> 2.3' diff --git a/README.md b/README.md index d5336e49..329ecc51 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ the entries to `/etc/sysctl.d/60-openstack.conf`. Data Bags ========= -This cookbook containes Libraries to work with passwords and secrets in databags. Databags can be unencrypted ( for dev ) or encrypted ( for prod ). +This cookbook containes Libraries to work with passwords and secrets in databags. Databags can be unencrypted ( for dev ) or encrypted ( for prod ). In addition to traditionally encrypted data bags they can also be created as chef-vault items. To read more about chef-vault and how to use it, go to https://docs.getchef.com/chef_vault.html. Documentation for Attributes for selecting databag format can be found in the attributes section of this cookbook. diff --git a/attributes/default.rb b/attributes/default.rb index d314cd87..014f4f9a 100644 --- a/attributes/default.rb +++ b/attributes/default.rb @@ -45,11 +45,15 @@ default['openstack']['developer_mode'] = false default['openstack']['use_databags'] = true # Set databag type -# acceptable values 'encrypted', 'standard' +# acceptable values 'encrypted', 'standard', 'vault' # Set this to 'standard' in order to use regular databags. # this is not recommended for anything other than dev/CI # type environments. Storing real secrets in plaintext = craycray. +# In addition to the encrypted data_bags which are an included +# feature of the official chef project, you can use 'vault' to +# encrypt your secrets with the method provided in the chef-vault gem. default['openstack']['databag_type'] = 'encrypted' +default['openstack']['vault_gem_version'] = '~> 2.3' # Default attributes when not using data bags (use_databags = false) %w{block-storage object-storage compute database dashboard image identity diff --git a/libraries/passwords.rb b/libraries/passwords.rb index 2b97bf79..7ab7aed7 100644 --- a/libraries/passwords.rb +++ b/libraries/passwords.rb @@ -47,22 +47,21 @@ module ::Openstack # rubocop:disable Documentation # nova_password = secret 'passwords', 'nova' # # The nova_password will == 'nova_password' + def secret(bag_name, index) if node['openstack']['developer_mode'] - ::Chef::Log.warn( - "Developer mode for reading passwords is DEPRECATED and will "\ - "be removed. Please use attributes (and the get_password method) "\ - "instead.") - - return (node['openstack']['secret'][index] || index) - end - case node['openstack']['databag_type'] - when 'encrypted' - encrypted_secret(bag_name, index) - when 'standard' - standard_secret(bag_name, index) + dev_secret(index) else - ::Chef::Log.error("Unsupported value for node['openstack']['databag_type']") + case node['openstack']['databag_type'] + when 'encrypted' + encrypted_secret(bag_name, index) + when 'standard' + standard_secret(bag_name, index) + when 'vault' # chef-vault, by convention use "vault_" as bag_name + vault_secret('vault_' + bag_name, index) + else + ::Chef::Log.error("Unsupported value for node['openstack']['databag_type']") + end end end @@ -78,6 +77,16 @@ module ::Openstack # rubocop:disable Documentation ::Chef::DataBagItem.load(bag_name, index)[index] end + def vault_secret(bag_name, index) + begin + require 'chef-vault' + rescue LoadError + Chef::Log.warn("Missing gem 'chef-vault'") + end + ::Chef::Log.info "Loading vault secret #{index} from #{bag_name}" + ::ChefVault::Item.load(bag_name, index)[index] + end + # Ease-of-use/standarization routine that returns a secret from the # attribute-specified openstack secrets databag. def get_secret(key) @@ -115,4 +124,14 @@ module ::Openstack # rubocop:disable Documentation node['openstack']['secret'][key][type] end end + + private + + def dev_secret(index) + ::Chef::Log.warn( + 'Developer mode for reading passwords is DEPRECATED and will '\ + 'be removed. Please use attributes (and the get_password method) '\ + 'instead.') + (node['openstack']['secret'][index] || index) + end end diff --git a/recipes/default.rb b/recipes/default.rb index 5ea8923a..3f4e3ac2 100644 --- a/recipes/default.rb +++ b/recipes/default.rb @@ -103,3 +103,9 @@ when 'suse' not_if { Mixlib::ShellOut.new('zypper repos --export -').run_command.stdout.include? repo_uri } end end + +if node['openstack']['databag_type'] == 'vault' + chef_gem 'chef-vault' do + version node['openstack']['vault_gem_version'] + end +end diff --git a/spec/default_spec.rb b/spec/default_spec.rb index 4fc7b957..81067354 100644 --- a/spec/default_spec.rb +++ b/spec/default_spec.rb @@ -30,5 +30,16 @@ describe 'openstack-common::default' do uri: 'http://ubuntu-cloud.archive.canonical.com/ubuntu', components: ['precise-updates/juno', 'main']) end + + it 'does not install the gem chef-vault by default' do + expect(chef_run).to_not install_chef_gem('chef-vault') + end + + it 'installs the gem chef-vault if databag_type is vault' do + node.set['openstack']['databag_type'] = 'vault' + expect(chef_run).to install_chef_gem('chef-vault') + .with(version: '~> 2.3') + end + end end diff --git a/spec/password_spec.rb b/spec/password_spec.rb index f3f69e78..d85cbcb0 100644 --- a/spec/password_spec.rb +++ b/spec/password_spec.rb @@ -1,6 +1,7 @@ # encoding: UTF-8 require_relative 'spec_helper' require ::File.join ::File.dirname(__FILE__), '..', 'libraries', 'passwords' +require 'chef-vault' describe 'openstack-common::default' do describe 'Passwords' do @@ -21,6 +22,17 @@ describe 'openstack-common::default' do end end + context 'using chef-vault' do + before do + node.set['openstack']['databag_type'] = 'vault' + end + it 'returns the data from a chef vault item' do + allow(ChefVault::Item).to receive(:load).with('vault_passwords', 'nova') + .and_return('nova' => 'novapassword') + expect(subject.secret('passwords', 'nova')).to eq('novapassword') + end + end + describe '#get_secret' do it 'returns databag value' do value = { 'nova' => 'this' }