Stein fixes

- Cookstyle fixes
- Refactor Berksfile to use groups so we can exclude integration testing
  cookbooks
- Update documentation
- Enable sensitive resources for template[/etc/keystone/keystone.conf]
  and execute[bootstrap_keystone] to improve security.
- Update delivery configuration to exclude integration cookbooks

[1] https://docs.openstack.org/keystone/stein/install/keystone-install-rdo.html#install-and-configure-components

Depends-On: https://review.opendev.org/701027
Depends-On: https://review.opendev.org/706101
Depends-On: https://review.opendev.org/706140
Depends-On: https://review.opendev.org/706147
Depends-On: https://review.opendev.org/706158
Change-Id: I6c5005b23ee209650911146e373c4cf082cbee9e
This commit is contained in:
Lance Albertson 2020-02-05 14:52:14 -08:00
parent 453ab3bb95
commit c49dedfbcd
16 changed files with 129 additions and 137 deletions

View File

@ -1 +1,9 @@
remote_file = "https://raw.githubusercontent.com/chef-cookbooks/community_cookbook_tools/master/delivery/project.toml" [local_phases]
unit = 'rspec spec/'
lint = 'cookstyle --display-cop-names --extra-details'
syntax = "berks install -e integration"
provision = "echo skipping"
deploy = "echo skipping"
smoke = "echo skipping"
functional = "echo skipping"
cleanup = "echo skipping"

View File

@ -1,5 +1,3 @@
inherit_from: .rubocop_todo.yml
AllCops: AllCops:
Include: Include:
- metadata.rb - metadata.rb
@ -14,24 +12,3 @@ AllCops:
- .cookbooks/**/* - .cookbooks/**/*
- berks-cookbooks/**/* - berks-cookbooks/**/*
- .bundle/**/* - .bundle/**/*
Encoding:
Exclude:
- metadata.rb
- Gemfile
NumericLiterals:
Enabled: false
LineLength:
Enabled: false
WordArray:
MinSize: 3
# TODO(galstom21)
# The rescue exception statements in providers/**.rb need to be modified,
# to rescue specific exceptions.
RescueException:
Exclude:
- providers/register.rb

View File

@ -1,19 +0,0 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2018-08-03 05:25:58 -0700 using RuboCop version 0.55.0.
# 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
# versions of RuboCop, may require this file to be generated again.
# Offense count: 2
# Cop supports --auto-correct.
Style/IfUnlessModifier:
Exclude:
- 'recipes/server-apache.rb'
# Offense count: 65
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Metrics/LineLength:
Max: 224

View File

@ -4,19 +4,19 @@ solver :ruby, :required
metadata metadata
%w( [
client %w(client dep),
-common %w(-common dep),
-dns %w(-dns integration),
-image %w(-image integration),
-integration-test %w(-integration-test integration),
-network %w(-network integration),
-ops-database %w(-ops-database integration),
-ops-messaging %w(-ops-messaging integration)
).each do |cookbook| ].each do |cookbook, group|
if Dir.exist?("../cookbook-openstack#{cookbook}") if Dir.exist?("../cookbook-openstack#{cookbook}")
cookbook "openstack#{cookbook}", path: "../cookbook-openstack#{cookbook}" cookbook "openstack#{cookbook}", path: "../cookbook-openstack#{cookbook}", group: group
else else
cookbook "openstack#{cookbook}", git: "https://opendev.org/openstack/cookbook-openstack#{cookbook}" cookbook "openstack#{cookbook}", git: "https://opendev.org/openstack/cookbook-openstack#{cookbook}", group: group
end end
end end

View File

@ -21,9 +21,9 @@ https://docs.openstack.org/keystone/latest/
Requirements Requirements
============ ============
- Chef 14 or higher - Chef 15 or higher
- ChefDK 3.2.30 for testing (also includes Berkshelf for cookbook - Chef Workstation 0.15.18 for testing (also includes Berkshelf for
dependency resolution) cookbook dependency resolution)
Platform Platform
======== ========
@ -38,7 +38,7 @@ Cookbooks
The following cookbooks are dependencies: The following cookbooks are dependencies:
- 'apache2', '~> 8.0' - 'apache2', '~> 8.0'
- 'openstack-common', '>= 18.0.0' - 'openstack-common', '>= 19.0.0'
- 'openstackclient' - 'openstackclient'
Attributes Attributes
@ -63,7 +63,17 @@ openstack-identity::cloud_config
openstack-identity::_credential_tokens openstack-identity::_credential_tokens
-------------------------------------- --------------------------------------
- Helper recipe to manage credential keys - Helper recipe to manage credential keys.
If you prefer, you can manually create the keys by doing the following:
.. code-block:: console
$ keystone-manage credential_setup \
--keystone-user keystone --keystone-group keystone
This should create a directory ``/etc/keystone/credential-keys`` with
the keys residing in it.
openstack-identity::_fernet_tokens openstack-identity::_fernet_tokens
---------------------------------- ----------------------------------
@ -141,7 +151,7 @@ License and Author
+---------------+----------------------------------------------+ +---------------+----------------------------------------------+
| **Copyright** | GmbH Copyright 2013-2014, IBM, Corp. | | **Copyright** | GmbH Copyright 2013-2014, IBM, Corp. |
+---------------+----------------------------------------------+ +---------------+----------------------------------------------+
| **Copyright** | Copyright 2016-2019, Oregon State University | | **Copyright** | Copyright 2016-2020, Oregon State University |
+---------------+----------------------------------------------+ +---------------+----------------------------------------------+
Licensed under the Apache License, Version 2.0 (the "License"); you may Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@ -1,14 +1,15 @@
# encoding: UTF-8 # encoding: UTF-8
# #
# Cookbook Name:: openstack-identity # Cookbook:: openstack-identity
# Recipe:: default # Recipe:: default
# #
# Copyright 2012-2013, AT&T Services, Inc. # Copyright:: 2012-2013, AT&T Services, Inc.
# Copyright 2013, Opscode, Inc. # Copyright:: 2013, Opscode, Inc.
# Copyright 2013, IBM Corp. # Copyright:: 2013, IBM Corp.
# Copyright 2017, x-ion GmbH # Copyright:: 2017, x-ion GmbH
# Copyright 2018, Workday, Inc. # Copyright:: 2018, Workday, Inc.
# Copyright 2019, x-ion GmbH # Copyright:: 2019, x-ion GmbH
# Copyright:: 2016-2020, Oregon State University
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.

View File

@ -3,14 +3,7 @@ maintainer 'openstack-chef'
maintainer_email 'openstack-discuss@lists.openstack.org' maintainer_email 'openstack-discuss@lists.openstack.org'
license 'Apache-2.0' license 'Apache-2.0'
description 'The OpenStack Identity service Keystone.' description 'The OpenStack Identity service Keystone.'
version '18.0.0' version '19.0.0'
recipe 'cloud_config', 'Manage the cloud config file located at /root/clouds.yaml'
recipe '_credential_tokens', 'Helper recipe to manage credential keys'
recipe '_fernet_tokens', 'Helper recipe to manage fernet tokens'
recipe 'openrc', 'Creates a fully usable openrc file'
recipe 'registration', 'Registers the initial keystone endpoint as well as users, tenants and roles'
recipe 'server-apache', 'Installs and configures the OpenStack Identity Service running inside of an apache webserver.'
%w(ubuntu redhat centos).each do |os| %w(ubuntu redhat centos).each do |os|
supports os supports os
@ -18,8 +11,8 @@ end
depends 'apache2', '~> 8.0' depends 'apache2', '~> 8.0'
depends 'openstackclient' depends 'openstackclient'
depends 'openstack-common', '>= 18.0.0' depends 'openstack-common', '>= 19.0.0'
issues_url 'https://launchpad.net/openstack-chef' issues_url 'https://launchpad.net/openstack-chef'
source_url 'https://opendev.org/openstack/cookbook-openstack-identity' source_url 'https://opendev.org/openstack/cookbook-openstack-identity'
chef_version '>= 14.0' chef_version '>= 15.0'

View File

@ -1,8 +1,10 @@
# encoding: UTF-8 # encoding: UTF-8
# #
# Cookbook Name:: openstack-identity # Cookbook:: openstack-identity
# Recipe:: _credential_tokens # Recipe:: _credential_tokens
# #
# Copyright:: 2020, Oregon State University
#
# Licensed under the Apache License, Version 2.0 (the 'License'); # Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at # You may obtain a copy of the License at
@ -24,23 +26,23 @@ class ::Chef::Recipe
include ::Openstack include ::Openstack
end end
key_repository = key_repository = node['openstack']['identity']['conf']['credential']['key_repository']
node['openstack']['identity']['conf']['credential']['key_repository'] keystone_user = node['openstack']['identity']['user']
keystone_group = node['openstack']['identity']['group']
directory key_repository do directory key_repository do
owner node['openstack']['identity']['user'] owner keystone_user
group node['openstack']['identity']['group'] group keystone_group
mode 0o0700 mode '700'
end end
node['openstack']['identity']['credential']['keys'].each do |key_index| node['openstack']['identity']['credential']['keys'].each do |key_index|
key = secret(node['openstack']['secret']['secrets_data_bag'], key = secret(node['openstack']['secret']['secrets_data_bag'], "credential_key#{key_index}")
"credential_key#{key_index}")
file File.join(key_repository, key_index.to_s) do file File.join(key_repository, key_index.to_s) do
content key content key
owner node['openstack']['identity']['user'] owner keystone_user
group node['openstack']['identity']['group'] group keystone_group
mode 0o0400 mode '400'
sensitive true sensitive true
end end
end end

View File

@ -1,8 +1,10 @@
# encoding: UTF-8 # encoding: UTF-8
# #
# Cookbook Name:: openstack-identity # Cookbook:: openstack-identity
# Recipe:: _fernet_tokens # Recipe:: _fernet_tokens
# #
# Copyright:: 2020, Oregon State University
#
# Licensed under the Apache License, Version 2.0 (the 'License'); # Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at # You may obtain a copy of the License at
@ -23,23 +25,28 @@ class ::Chef::Recipe
include ::Openstack include ::Openstack
end end
key_repository = key_repository = node['openstack']['identity']['conf']['fernet_tokens']['key_repository']
node['openstack']['identity']['conf']['fernet_tokens']['key_repository'] keystone_user = node['openstack']['identity']['user']
keystone_group = node['openstack']['identity']['group']
directory key_repository do directory key_repository do
owner node['openstack']['identity']['user'] owner keystone_user
group node['openstack']['identity']['group'] group keystone_group
mode 0o0700 mode '700'
end end
node['openstack']['identity']['fernet']['keys'].each do |key_index| node['openstack']['identity']['fernet']['keys'].each do |key_index|
key = secret(node['openstack']['secret']['secrets_data_bag'], key = secret(node['openstack']['secret']['secrets_data_bag'], "fernet_key#{key_index}")
"fernet_key#{key_index}")
file File.join(key_repository, key_index.to_s) do file File.join(key_repository, key_index.to_s) do
content key content key
owner node['openstack']['identity']['user'] owner keystone_user
group node['openstack']['identity']['group'] group keystone_group
mode 0o0400 mode '400'
sensitive true sensitive true
end end
end end
execute 'keystone-manage fernet_setup' do
command "keystone-manage fernet_setup --keystone-user #{keystone_user} --keystone-group #{keystone_group}"
creates '/etc/keystone/fernet-keys'
end

View File

@ -1,9 +1,9 @@
# encoding: UTF-8 # encoding: UTF-8
# #
# Cookbook Name:: openstack-identity # Cookbook:: openstack-identity
# recipe:: cloud_config # recipe:: cloud_config
# #
# Copyright 2019 x-ion GmbH # Copyright:: 2019 x-ion GmbH
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.

View File

@ -1,9 +1,9 @@
# encoding: UTF-8 # encoding: UTF-8
# #
# Cookbook Name:: openstack-identity # Cookbook:: openstack-identity
# recipe:: openrc # recipe:: openrc
# #
# Copyright 2014 IBM Corp. # Copyright:: 2014 IBM Corp.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.

View File

@ -1,10 +1,11 @@
# encoding: UTF-8 # encoding: UTF-8
# #
# Cookbook Name:: openstack-identity # Cookbook:: openstack-identity
# Recipe:: setup # Recipe:: setup
# #
# Copyright 2012, Rackspace US, Inc. # Copyright:: 2012, Rackspace US, Inc.
# Copyright 2012-2013, Opscode, Inc. # Copyright:: 2012-2013, Opscode, Inc.
# Copyright:: 2020, Oregon State University
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -48,11 +49,11 @@ admin_domain = node['openstack']['identity']['admin_domain_name']
# endpoint_type = node['openstack']['identity']['endpoint_type'] # endpoint_type = node['openstack']['identity']['endpoint_type']
connection_params = { connection_params = {
openstack_auth_url: auth_url, openstack_auth_url: auth_url,
openstack_username: admin_user, openstack_username: admin_user,
openstack_api_key: admin_pass, openstack_api_key: admin_pass,
openstack_project_name: admin_project, openstack_project_name: admin_project,
openstack_domain_id: admin_domain, openstack_domain_id: admin_domain,
# openstack_endpoint_type: endpoint_type, # openstack_endpoint_type: endpoint_type,
} }
@ -77,8 +78,8 @@ openstack_role 'service' do
connection_params connection_params connection_params connection_params
end end
node.normal['openstack']['identity']['internalURL'] = identity_internal_endpoint.to_s node.default['openstack']['identity']['internalURL'] = identity_internal_endpoint.to_s
node.normal['openstack']['identity']['publicURL'] = identity_endpoint.to_s node.default['openstack']['identity']['publicURL'] = identity_endpoint.to_s
Chef::Log.info "Keystone InternalURL: #{identity_internal_endpoint}" Chef::Log.info "Keystone InternalURL: #{identity_internal_endpoint}"
Chef::Log.info "Keystone PublicURL: #{identity_endpoint}" Chef::Log.info "Keystone PublicURL: #{identity_endpoint}"

View File

@ -1,9 +1,10 @@
# encoding: UTF-8 # encoding: UTF-8
# #
# Cookbook Name:: openstack-identity # Cookbook:: openstack-identity
# Recipe:: server-apache # Recipe:: server-apache
# #
# Copyright 2015, IBM Corp. Inc. # Copyright:: 2015, IBM Corp. Inc.
# Copyright:: 2016-2020, Oregon State University
# #
# Licensed under the Apache License, Version 2.0 (the 'License'); # Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -50,8 +51,8 @@ admin_user = node['openstack']['identity']['admin_user']
admin_pass = get_password 'user', node['openstack']['identity']['admin_user'] admin_pass = get_password 'user', node['openstack']['identity']['admin_user']
admin_role = node['openstack']['identity']['admin_role'] admin_role = node['openstack']['identity']['admin_role']
region = node['openstack']['identity']['region'] region = node['openstack']['identity']['region']
keystone_user = node['openstack']['identity']['user'] keystone_user = node['openstack']['identity']['user']
keystone_group = node['openstack']['identity']['group'] keystone_group = node['openstack']['identity']['group']
# install the database python adapter packages for the selected database # install the database python adapter packages for the selected database
# service_type # service_type
@ -101,14 +102,14 @@ end
directory '/etc/keystone' do directory '/etc/keystone' do
owner keystone_user owner keystone_user
group keystone_group group keystone_group
mode 0o0700 mode '700'
end end
# create keystone domain config dir if needed # create keystone domain config dir if needed
directory node['openstack']['identity']['domain_config_dir'] do directory node['openstack']['identity']['domain_config_dir'] do
owner keystone_user owner keystone_user
group keystone_group group keystone_group
mode 0o0700 mode '700'
only_if { node['openstack']['identity']['domain_specific_drivers_enabled'] } only_if { node['openstack']['identity']['domain_specific_drivers_enabled'] }
end end
@ -119,8 +120,8 @@ file '/var/lib/keystone/keystone.db' do
end end
# include the recipes to setup tokens # include the recipes to setup tokens
include_recipe 'openstack-identity::_credential_tokens'
include_recipe 'openstack-identity::_fernet_tokens' include_recipe 'openstack-identity::_fernet_tokens'
include_recipe 'openstack-identity::_credential_tokens'
# define the address to bind the keystone apache public service to # define the address to bind the keystone apache public service to
bind_service = node['openstack']['bind_service']['public']['identity'] bind_service = node['openstack']['bind_service']['public']['identity']
@ -145,14 +146,14 @@ if node['openstack']['identity']['pastefile_url']
source node['openstack']['identity']['pastefile_url'] source node['openstack']['identity']['pastefile_url']
owner keystone_user owner keystone_user
group keystone_group group keystone_group
mode 0o0644 mode '644'
end end
else else
template '/etc/keystone/keystone-paste.ini' do template '/etc/keystone/keystone-paste.ini' do
source 'keystone-paste.ini.erb' source 'keystone-paste.ini.erb'
owner keystone_user owner keystone_user
group keystone_group group keystone_group
mode 0o0644 mode '644'
end end
end end
@ -176,7 +177,8 @@ template '/etc/keystone/keystone.conf' do
cookbook 'openstack-common' cookbook 'openstack-common'
owner keystone_user owner keystone_user
group keystone_group group keystone_group
mode 0o0640 mode '640'
sensitive true
variables( variables(
service_config: keystone_conf_options service_config: keystone_conf_options
) )
@ -210,6 +212,7 @@ execute 'bootstrap_keystone' do
--bootstrap-admin-url #{identity_internal_endpoint} \\ --bootstrap-admin-url #{identity_internal_endpoint} \\
--bootstrap-public-url #{identity_endpoint} \\ --bootstrap-public-url #{identity_endpoint} \\
--bootstrap-internal-url #{identity_internal_endpoint}" --bootstrap-internal-url #{identity_internal_endpoint}"
sensitive true
end end
#### Start of Apache specific work #### Start of Apache specific work
@ -236,7 +239,7 @@ keystone_apache_dir = "#{default_docroot_dir}/keystone"
directory keystone_apache_dir do directory keystone_apache_dir do
owner 'root' owner 'root'
group 'root' group 'root'
mode 0o0755 mode '755'
end end
# create the keystone apache config using template # create the keystone apache config using template

View File

@ -13,7 +13,7 @@ describe 'openstack-identity::_credential_tokens' do
it do it do
expect(chef_run).to create_directory('/etc/keystone/credential-tokens') expect(chef_run).to create_directory('/etc/keystone/credential-tokens')
.with(owner: 'keystone', user: 'keystone', mode: 0o0700) .with(owner: 'keystone', user: 'keystone', mode: '700')
end end
[0, 1].each do |key_index| [0, 1].each do |key_index|
@ -23,7 +23,7 @@ describe 'openstack-identity::_credential_tokens' do
content: "thisiscredentialkey#{key_index}", content: "thisiscredentialkey#{key_index}",
owner: 'keystone', owner: 'keystone',
group: 'keystone', group: 'keystone',
mode: 0o0400 mode: '400'
) )
end end
end end

View File

@ -13,7 +13,7 @@ describe 'openstack-identity::_fernet_tokens' do
it do it do
expect(chef_run).to create_directory('/etc/keystone/fernet-tokens') expect(chef_run).to create_directory('/etc/keystone/fernet-tokens')
.with(owner: 'keystone', user: 'keystone', mode: 0o0700) .with(owner: 'keystone', user: 'keystone', mode: '700')
end end
[0, 1].each do |key_index| [0, 1].each do |key_index|
@ -23,9 +23,14 @@ describe 'openstack-identity::_fernet_tokens' do
content: "thisisfernetkey#{key_index}", content: "thisisfernetkey#{key_index}",
owner: 'keystone', owner: 'keystone',
group: 'keystone', group: 'keystone',
mode: 0o0400 mode: '400'
) )
end end
end end
it do
expect(chef_run).to run_execute('keystone-manage fernet_setup').with(
command: 'keystone-manage fernet_setup --keystone-user keystone --keystone-group keystone'
)
end
end end
end end

View File

@ -54,7 +54,8 @@ describe 'openstack-identity::server-apache' do
end end
it 'bootstrap with keystone-manage' do it 'bootstrap with keystone-manage' do
expect(chef_run).to run_execute('bootstrap_keystone').with(command: "keystone-manage bootstrap \\ expect(chef_run).to run_execute('bootstrap_keystone').with(
command: "keystone-manage bootstrap \\
--bootstrap-password #{password} \\ --bootstrap-password #{password} \\
--bootstrap-username #{service_user} \\ --bootstrap-username #{service_user} \\
--bootstrap-project-name #{project_name} \\ --bootstrap-project-name #{project_name} \\
@ -63,7 +64,9 @@ describe 'openstack-identity::server-apache' do
--bootstrap-region-id #{region} \\ --bootstrap-region-id #{region} \\
--bootstrap-admin-url #{public_url} \\ --bootstrap-admin-url #{public_url} \\
--bootstrap-public-url #{public_url} \\ --bootstrap-public-url #{public_url} \\
--bootstrap-internal-url #{public_url}") --bootstrap-internal-url #{public_url}",
sensitive: true
)
end end
describe '/etc/keystone' do describe '/etc/keystone' do
@ -73,7 +76,7 @@ describe 'openstack-identity::server-apache' do
expect(chef_run).to create_directory(dir.name).with( expect(chef_run).to create_directory(dir.name).with(
user: 'keystone', user: 'keystone',
group: 'keystone', group: 'keystone',
mode: 0o0700 mode: '700'
) )
end end
end end
@ -94,7 +97,7 @@ describe 'openstack-identity::server-apache' do
expect(chef_run).to create_directory(dir).with( expect(chef_run).to create_directory(dir).with(
user: 'keystone', user: 'keystone',
group: 'keystone', group: 'keystone',
mode: 0o0700 mode: '700'
) )
end end
end end
@ -122,7 +125,8 @@ describe 'openstack-identity::server-apache' do
expect(chef_run).to create_template(resource.name).with( expect(chef_run).to create_template(resource.name).with(
user: 'keystone', user: 'keystone',
group: 'keystone', group: 'keystone',
mode: 0o0640 mode: '640',
sensitive: true
) )
end end
end end
@ -207,13 +211,13 @@ describe 'openstack-identity::server-apache' do
end end
end end
describe '[fernet_tokens] section' do describe '[fernet_tokens] section' do
it do it 'key_repository = /etc/keystone/fernet-tokens' do
r = %r{^key_repository = /etc/keystone/fernet-tokens$} r = %r{^key_repository = /etc/keystone/fernet-tokens$}
expect(chef_run).to render_config_file(path).with_section_content('fernet_tokens', r) expect(chef_run).to render_config_file(path).with_section_content('fernet_tokens', r)
end end
end end
describe '[credential] section' do describe '[credential] section' do
it do it 'key_repository = /etc/keystone/credential-tokens' do
r = %r{^key_repository = /etc/keystone/credential-tokens$} r = %r{^key_repository = /etc/keystone/credential-tokens$}
expect(chef_run).to render_config_file(path).with_section_content('credential', r) expect(chef_run).to render_config_file(path).with_section_content('credential', r)
end end
@ -301,7 +305,7 @@ describe 'openstack-identity::server-apache' do
source: 'http://server/mykeystone-paste.ini', source: 'http://server/mykeystone-paste.ini',
user: 'keystone', user: 'keystone',
group: 'keystone', group: 'keystone',
mode: 0o0644 mode: '644'
) )
end end
end end