allow for attribute storage of secrets as an alternative

In addition to storing secrets in data bags (the default) we would like
to be able to store them in attributes.

This commit doesn't change the existing passwords API, get_password will
now read from attributes when the node['openstack']['use_databags']
attribute is falsy.

This commit deprecates the development_mode which was only used as a
hack for getting passwords easier without data bags. The attribute
storage of passwords should now be sufficient for all those use cases.

Increased rubocop's methodlength maximum to 15 because there are some
temporary deprecation warnings which add up, but should go away soon.

Change-Id: I1f23878dd3fa83fb40f7b4b56960d57a7b9b89cc
This commit is contained in:
Ionuț Arțăriși 2014-06-18 13:48:19 +02:00
parent 17cee2d238
commit dbe0bd6818
6 changed files with 118 additions and 40 deletions

View File

@ -22,3 +22,6 @@ LineLength:
WordArray:
MinSize: 3
MethodLength:
Max: 15

View File

@ -2,6 +2,9 @@
This file is used to list changes made in each version of cookbook-openstack-common.
## 9.6.0
* Add an option to store passwords in attributes instead of data bags. Deprecates the get_secret method and the development_mode.
## 9.5.2
* Adds new python_packages attributes for database client packages

View File

@ -34,8 +34,24 @@ default['openstack']['common']['custom_template_banner'] = '
# pass = secret "passwords", "nova"
#
# The value of pass will be "nova"
#
# This attribute is now DEPRECATED and will be removed. Use the default
# attributes below instead.
default['openstack']['developer_mode'] = false
# Use data bags for storing passwords
# Set this to false in order to get the passwords from attributes like:
# node['openstack']['secret'][key][type]
default['openstack']['use_databags'] = true
# Default attributes when not using data bags (use_databags = false)
%w{block-storage object-storage compute database dashboard image identity
telemetry network object-storage orchestration}.each do |service|
%w{user service db token}.each do |type|
default['openstack']['secret'][service][type] = "#{service}-#{type}"
end
end
# The type of token signing to use (uuid or pki)
default['openstack']['auth']['strategy'] = 'pki'

View File

@ -5,6 +5,7 @@
# library:: passwords
#
# Copyright 2012-2013, AT&T Services, Inc.
# Copyright 2014, SUSE Linux, GmbH.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -47,7 +48,14 @@ module ::Openstack # rubocop:disable Documentation
#
# The nova_password will == 'nova_password'
def secret(bag_name, index)
return (node['openstack']['secret'][index] || index) if node['openstack']['developer_mode']
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
key_path = node['openstack']['secret']['key_path']
::Chef::Log.info "Loading encrypted databag #{bag_name}.#{index} using key at #{key_path}"
secret = ::Chef::EncryptedDataBagItem.load_secret key_path
@ -57,17 +65,38 @@ module ::Openstack # rubocop:disable Documentation
# Ease-of-use/standarization routine that returns a secret from the
# attribute-specified openstack secrets databag.
def get_secret(key)
secret node['openstack']['secret']['secrets_data_bag'], key
::Chef::Log.warn(
"The get_secret method is DEPRECATED. "\
"Use get_password(key, 'token') instead")
if node['openstack']['use_databags']
secret node['openstack']['secret']['secrets_data_bag'], key
else
node['openstack']['secret'][key]['token']
end
end
# Ease-of-use/standarization routine that returns a service/database/user
# password for a named OpenStack service/database/user. Accepts 'user',
# 'service' or 'db' as the type.
# Return a password using either data bags or attributes for
# storage. The storage mechanism used is determined by the
# node['openstack']['use_databags'] attribute.
# @param [String] type of password, one of 'user', 'service', 'db' or 'token'
# @param [String] the identifier of the password (usually the
# component name, but can also be a token name
# e.g. openstack_identity_bootstrap_token
def get_password(type, key)
if ['db', 'user', 'service'].include?(type)
secret node['openstack']['secret']["#{type}_passwords_data_bag"], key
else
unless %w{db user service token}.include?(type)
::Chef::Log.error("Unsupported type for get_password: #{type}")
return
end
if node['openstack']['use_databags']
if type == 'token'
secret node['openstack']['secret']['secrets_data_bag'], key
else
secret node['openstack']['secret']["#{type}_passwords_data_bag"], key
end
else
node['openstack']['secret'][key][type]
end
end
end

View File

@ -4,7 +4,7 @@ maintainer_email 'cookbooks@lists.tfoundry.com'
license 'Apache 2.0'
description 'Common OpenStack attributes, libraries and recipes.'
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
version '9.5.2'
version '9.6.0'
recipe 'openstack-common', 'Installs/Configures common recipes'
recipe 'openstack-common::set_endpoints_by_interface', 'Set endpoints by interface'

View File

@ -11,39 +11,66 @@ describe 'openstack-common::default' do
include_context 'library-stubs'
describe '#secret' do
it 'returns databag' do
value = { 'nova' => 'this' }
::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret')
::Chef::EncryptedDataBagItem.stub(:load).with('passwords', 'nova', 'secret').and_return(value)
expect(subject.secret('passwords', 'nova')).to eq('this')
end
end
describe '#get_secret' do
it 'returns databag' do
value = { 'nova' => 'this' }
::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret')
::Chef::EncryptedDataBagItem.stub(:load).with('secrets', 'nova', 'secret').and_return(value)
expect(subject.get_secret('nova')).to eq('this')
end
it 'returns secret from an alternate databag when secrets_data_bag set' do
node.set['openstack']['secret']['secrets_data_bag'] = 'myothersecrets'
value = { 'nova' => 'this' }
::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret')
::Chef::EncryptedDataBagItem.stub(:load).with('myothersecrets', 'nova', 'secret').and_return(value)
expect(subject.get_secret('nova')).to eq('this')
end
end
describe '#get_password' do
['service', 'db', 'user'].each do |type|
it "returns databag for #{type}" do
context 'stored in data bags by default' do
describe '#secret' do
it 'returns databag' do
value = { 'nova' => 'this' }
::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret')
::Chef::EncryptedDataBagItem.stub(:load).with("#{type}_passwords", 'nova', 'secret').and_return(value)
expect(subject.get_password(type, 'nova')).to eq('this')
::Chef::EncryptedDataBagItem.stub(:load).with('passwords', 'nova', 'secret').and_return(value)
expect(subject.secret('passwords', 'nova')).to eq('this')
end
end
describe '#get_secret' do
it 'returns databag value' do
value = { 'nova' => 'this' }
::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret')
::Chef::EncryptedDataBagItem.stub(:load).with('secrets', 'nova', 'secret').and_return(value)
expect(subject.get_secret('nova')).to eq('this')
end
it 'returns secret from an alternate databag when secrets_data_bag set' do
node.set['openstack']['secret']['secrets_data_bag'] = 'myothersecrets'
value = { 'nova' => 'this' }
::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret')
::Chef::EncryptedDataBagItem.stub(:load).with('myothersecrets', 'nova', 'secret').and_return(value)
expect(subject.get_secret('nova')).to eq('this')
end
end
describe '#get_password' do
['service', 'db', 'user'].each do |type|
it "returns databag value for #{type}" do
value = { 'nova' => 'this' }
::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret')
::Chef::EncryptedDataBagItem.stub(:load).with("#{type}_passwords", 'nova', 'secret').and_return(value)
expect(subject.get_password(type, 'nova')).to eq('this')
end
end
it 'returns nil for an invalid type' do
expect(subject.get_password('invalid_type', 'nova')).to be_nil
end
it 'returns tokens from the secrets_data_bag' do
bag_content = { 'nova' => 'mysecret' }
::Chef::EncryptedDataBagItem.stub(:load_secret).with(
'/etc/chef/openstack_data_bag_secret').and_return('secret')
::Chef::EncryptedDataBagItem.stub(:load).with(
'secrets', 'nova', 'secret').and_return(bag_content)
expect(subject.get_password('token', 'nova')).to eq('mysecret')
end
end
end
context 'stored in attributes as an alternative' do
before { node.set['openstack']['use_databags'] = false }
describe '#get_password' do
%w{service db user token}.each do |type|
it "returns the set attribute for #{type}" do
expect(subject.get_password(type, 'compute')).to eq("compute-#{type}")
end
end
end
end