diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index e173d32..d2643f6 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2016-01-12 15:11:37 +0100 using RuboCop version 0.34.2. +# on 2016-02-16 10:26:21 +0100 using RuboCop version 0.34.2. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -11,19 +11,21 @@ Metrics/ParameterLists: Max: 6 -# Offense count: 4 +# Offense count: 5 # Configuration parameters: EnforcedStyle, SupportedStyles. Style/ClassAndModuleChildren: Exclude: + - 'recipes/_fernet_tokens.rb' - 'recipes/client.rb' - 'recipes/openrc.rb' - 'recipes/registration.rb' - 'recipes/server-apache.rb' -# Offense count: 3 +# Offense count: 4 # Configuration parameters: Exclude. Style/Documentation: Exclude: + - 'recipes/_fernet_tokens.rb' - 'recipes/client.rb' - 'recipes/registration.rb' - 'recipes/server-apache.rb' diff --git a/attributes/default.rb b/attributes/default.rb index 15456d6..27909b9 100644 --- a/attributes/default.rb +++ b/attributes/default.rb @@ -122,6 +122,16 @@ default['openstack']['identity']['ssl']['ciphers'] = nil # Note this section is only written if node['openstack']['auth']['strategy'] == 'pki' default['openstack']['identity']['signing']['basedir'] = '/etc/keystone/ssl' +# Fernet keys. Note this section is only written if +# node['openstack']['auth']['strategy'] == 'fernet' +# Fernet keys to read from databags/vaults. This should be changed in the +# environment when rotating keys (with the defaults below, the items +# 'fernet_key0' and 'fernet_key1' will be read from the databag/vault +# 'keystone). +# For more information please read: +# http://docs.openstack.org/admin-guide-cloud/keystone_fernet_token_faq.html +default['openstack']['identity']['fernet']['keys'] = [0, 1] + # The authorization configuration options # The external (REMOTE_USER) auth plugin module. (String value) default['openstack']['identity']['auth']['external'] = 'keystone.auth.plugins.external.DefaultDomain' diff --git a/recipes/_fernet_tokens.rb b/recipes/_fernet_tokens.rb new file mode 100644 index 0000000..024a0c5 --- /dev/null +++ b/recipes/_fernet_tokens.rb @@ -0,0 +1,43 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-identity +# Recipe:: _fernet_tokens +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class ::Chef::Recipe + include ::Openstack +end + +node.default['openstack']['identity']['conf']['fernet_tokens']['key_repository'] = + '/etc/keystone/fernet-tokens' +node.default['openstack']['identity']['conf']['token']['provider'] = 'fernet' + +key_repository = node['openstack']['identity']['conf']['fernet_tokens']['key_repository'] + +directory key_repository do + owner node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] + mode 00700 +end + +node['openstack']['identity']['fernet']['keys'].each do |key_index| + key = secret('keystone', "fernet_key#{key_index}") + file File.join(key_repository, key_index.to_s) do + content key + owner node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] + mode 00600 + end +end diff --git a/recipes/_pki_tokens.rb b/recipes/_pki_tokens.rb new file mode 100644 index 0000000..2239eea --- /dev/null +++ b/recipes/_pki_tokens.rb @@ -0,0 +1,70 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-identity +# Recipe:: _pki_tokens +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +certfile_url = node['openstack']['identity']['signing']['certfile_url'] +keyfile_url = node['openstack']['identity']['signing']['keyfile_url'] +ca_certs_url = node['openstack']['identity']['signing']['ca_certs_url'] +signing_basedir = node['openstack']['identity']['signing']['basedir'] + +directory signing_basedir do + owner node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] + mode 00700 +end + +directory "#{signing_basedir}/certs" do + owner node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] + mode 00755 +end + +directory "#{signing_basedir}/private" do + owner node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] + mode 00750 +end + +if certfile_url.nil? || keyfile_url.nil? || ca_certs_url.nil? + execute 'keystone-manage pki_setup' do + user node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] + + not_if { ::FileTest.exists? "#{node['openstack']['identity']['signing']['basedir']}/private/signing_key.pem" } + end +else + remote_file node['openstack']['identity']['signing']['certfile'] do + source certfile_url + owner node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] + mode 00640 + end + + remote_file node['openstack']['identity']['signing']['keyfile'] do + source keyfile_url + owner node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] + mode 00640 + end + + remote_file node['openstack']['identity']['signing']['ca_certs'] do + source ca_certs_url + owner node['openstack']['identity']['user'] + group node['openstack']['identity']['group'] + mode 00640 + end +end diff --git a/recipes/server-apache.rb b/recipes/server-apache.rb index 03765fa..81ec46b 100644 --- a/recipes/server-apache.rb +++ b/recipes/server-apache.rb @@ -80,59 +80,11 @@ file '/var/lib/keystone/keystone.db' do not_if { node['openstack']['db']['identity']['service_type'] == 'sqlite' } end -if node['openstack']['auth']['strategy'] == 'pki' - certfile_url = node['openstack']['identity']['signing']['certfile_url'] - keyfile_url = node['openstack']['identity']['signing']['keyfile_url'] - ca_certs_url = node['openstack']['identity']['signing']['ca_certs_url'] - signing_basedir = node['openstack']['identity']['signing']['basedir'] - - directory signing_basedir do - owner node['openstack']['identity']['user'] - group node['openstack']['identity']['group'] - mode 00700 - end - - directory "#{signing_basedir}/certs" do - owner node['openstack']['identity']['user'] - group node['openstack']['identity']['group'] - mode 00755 - end - - directory "#{signing_basedir}/private" do - owner node['openstack']['identity']['user'] - group node['openstack']['identity']['group'] - mode 00750 - end - - if certfile_url.nil? || keyfile_url.nil? || ca_certs_url.nil? - execute 'keystone-manage pki_setup' do - user node['openstack']['identity']['user'] - group node['openstack']['identity']['group'] - - not_if { ::FileTest.exists? "#{node['openstack']['identity']['signing']['basedir']}/private/signing_key.pem" } - end - else - remote_file node['openstack']['identity']['signing']['certfile'] do - source certfile_url - owner node['openstack']['identity']['user'] - group node['openstack']['identity']['group'] - mode 00640 - end - - remote_file node['openstack']['identity']['signing']['keyfile'] do - source keyfile_url - owner node['openstack']['identity']['user'] - group node['openstack']['identity']['group'] - mode 00640 - end - - remote_file node['openstack']['identity']['signing']['ca_certs'] do - source ca_certs_url - owner node['openstack']['identity']['user'] - group node['openstack']['identity']['group'] - mode 00640 - end - end +case node['openstack']['auth']['strategy'] +when 'pki' + include_recipe 'openstack-identity::_pki_tokens' +when 'fernet' + include_recipe 'openstack-identity::_fernet_tokens' end public_bind_service = node['openstack']['bind_service']['public']['identity'] diff --git a/spec/fernet_tokens_spec.rb b/spec/fernet_tokens_spec.rb new file mode 100644 index 0000000..3d4b335 --- /dev/null +++ b/spec/fernet_tokens_spec.rb @@ -0,0 +1,40 @@ +# encoding: UTF-8 +# + +require_relative 'spec_helper' + +describe 'openstack-identity::_fernet_tokens' do + describe 'ubuntu' do + let(:runner) { ChefSpec::SoloRunner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + before do + allow_any_instance_of(Chef::Recipe).to receive(:secret) + .with('keystone', 'fernet_key0') + .and_return('thisisfernetkey0') + allow_any_instance_of(Chef::Recipe).to receive(:secret) + .with('keystone', 'fernet_key1') + .and_return('thisisfernetkey1') + end + + it do + expect(chef_run).to create_directory('/etc/keystone/fernet-tokens') + .with(owner: 'keystone', user: 'keystone', mode: 00700) + end + + [0, 1].each do |key_index| + it do + expect(chef_run).to create_file("/etc/keystone/fernet-tokens/#{key_index}") + .with( + content: "thisisfernetkey#{key_index}", + owner: 'keystone', + group: 'keystone', + mode: 00600 + ) + end + end + end +end diff --git a/spec/pki_tokens_spec.rb b/spec/pki_tokens_spec.rb new file mode 100644 index 0000000..d13fd04 --- /dev/null +++ b/spec/pki_tokens_spec.rb @@ -0,0 +1,90 @@ +# encoding: UTF-8 +# + +require_relative 'spec_helper' + +describe 'openstack-identity::_pki_tokens' do + describe 'ubuntu' do + let(:runner) { ChefSpec::SoloRunner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) do + runner.converge(described_recipe) + end + + include Helpers + include_context 'identity_stubs' + + describe 'ssl directories' do + let(:ssl_dir) { '/etc/keystone/ssl' } + let(:certs_dir) { "#{ssl_dir}/certs" } + let(:private_dir) { "#{ssl_dir}/private" } + + describe '/etc/keystone/ssl' do + let(:dir_resource) { chef_run.directory(ssl_dir) } + + it 'creates /etc/keystone/ssl' do + expect(chef_run).to create_directory(ssl_dir).with( + owner: 'keystone', + group: 'keystone', + mode: 0700 + ) + end + end + + describe '/etc/keystone/ssl/certs' do + let(:dir_resource) { chef_run.directory(certs_dir) } + + it 'creates /etc/keystone/ssl/certs' do + expect(chef_run).to create_directory(certs_dir).with( + user: 'keystone', + group: 'keystone', + mode: 0755 + ) + end + end + + describe '/etc/keystone/ssl/private' do + let(:dir_resource) { chef_run.directory(private_dir) } + + it 'creates /etc/keystone/ssl/private' do + expect(chef_run).to create_directory(private_dir) + .with( + user: 'keystone', + group: 'keystone', + mode: 0750 + ) + end + end + end + + describe 'pki setup' do + let(:cmd) { 'keystone-manage pki_setup' } + + describe 'without {certfile,keyfile,ca_certs}_url attributes set' do + it 'executes' do + expect(FileTest).to receive(:exists?) + .with('/etc/keystone/ssl/private/signing_key.pem') + .and_return(false) + + expect(chef_run).to run_execute(cmd) + .with( + user: 'keystone', + group: 'keystone' + ) + end + end + + it 'does not execute when dir exists' do + expect(FileTest).to receive(:exists?) + .with('/etc/keystone/ssl/private/signing_key.pem') + .and_return(true) + + expect(chef_run).not_to run_execute(cmd) + .with( + user: 'keystone', + group: 'keystone' + ) + end + end + end +end diff --git a/spec/server-apache_spec.rb b/spec/server-apache_spec.rb index 799d854..02efe93 100644 --- a/spec/server-apache_spec.rb +++ b/spec/server-apache_spec.rb @@ -102,44 +102,6 @@ describe 'openstack-identity::server-apache' do expect(chef_run).not_to create_directory(private_dir) end end - - describe 'with pki' do - describe '/etc/keystone/ssl' do - let(:dir_resource) { chef_run.directory(ssl_dir) } - - it 'creates /etc/keystone/ssl' do - expect(chef_run).to create_directory(ssl_dir).with( - owner: 'keystone', - group: 'keystone', - mode: 0700 - ) - end - end - - describe '/etc/keystone/ssl/certs' do - let(:dir_resource) { chef_run.directory(certs_dir) } - - it 'creates /etc/keystone/ssl/certs' do - expect(chef_run).to create_directory(certs_dir).with( - user: 'keystone', - group: 'keystone', - mode: 0755 - ) - end - end - - describe '/etc/keystone/ssl/private' do - let(:dir_resource) { chef_run.directory(private_dir) } - - it 'creates /etc/keystone/ssl/private' do - expect(chef_run).to create_directory(private_dir).with( - user: 'keystone', - group: 'keystone', - mode: 0750 - ) - end - end - end end it 'deletes keystone.db' do @@ -151,46 +113,6 @@ describe 'openstack-identity::server-apache' do expect(chef_run).not_to delete_file('/var/lib/keystone/keystone.db') end - describe 'pki setup' do - let(:cmd) { 'keystone-manage pki_setup' } - - describe 'without pki' do - before { node.set['openstack']['auth']['strategy'] = 'uuid' } - it 'does not execute' do - expect(chef_run).to_not run_execute(cmd).with( - user: 'keystone', - group: 'keystone' - ) - end - end - - describe 'with pki' do - describe 'without {certfile,keyfile,ca_certs}_url attributes set' do - it 'executes' do - expect(FileTest).to receive(:exists?) - .with('/etc/keystone/ssl/private/signing_key.pem') - .and_return(false) - - expect(chef_run).to run_execute(cmd).with( - user: 'keystone', - group: 'keystone' - ) - end - end - - it 'does not execute when dir exists' do - expect(FileTest).to receive(:exists?) - .with('/etc/keystone/ssl/private/signing_key.pem') - .and_return(true) - - expect(chef_run).not_to run_execute(cmd).with( - user: 'keystone', - group: 'keystone' - ) - end - end - end - describe 'keystone.conf' do let(:path) { '/etc/keystone/keystone.conf' } let(:resource) { chef_run.template(path) }