diff --git a/actions.yaml b/actions.yaml index 297c041e..b260e6ff 100644 --- a/actions.yaml +++ b/actions.yaml @@ -6,3 +6,5 @@ resume: description: Resume the nova_compute unit. This action will start nova_compute services. hugepagereport: description: Report on hugepage configuration and usage +security-checklist: + description: Validate the running configuration against the OpenStack security guides checklist diff --git a/actions/security-checklist b/actions/security-checklist new file mode 120000 index 00000000..47464970 --- /dev/null +++ b/actions/security-checklist @@ -0,0 +1 @@ +security_checklist.py \ No newline at end of file diff --git a/actions/security_checklist.py b/actions/security_checklist.py new file mode 100755 index 00000000..630e745c --- /dev/null +++ b/actions/security_checklist.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +# +# Copyright 2019 Canonical Ltd +# +# 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. +import configparser +import os +import sys + +sys.path.append('hooks') + +import charmhelpers.contrib.openstack.audits as audits +from charmhelpers.contrib.openstack.audits import ( + openstack_security_guide, +) + + +# Via the openstack_security_guide above, we are running the following +# security assertions automatically: +# +# - Check-Compute-01 - validate-file-ownership +# - Check-Compute-02 - validate-file-permissions +# - Check-Compute-03 - validate-uses-keystone +# - Check-Compute-04 - validate-uses-tls-for-keystone +# - Check-Compute-05 - validates-uses-tls-for-glance + + +@audits.audit(audits.is_audit_type(audits.AuditType.OpenStackSecurityGuide),) +def is_volume_encryption_enabled(audit_options): + """Validate volume encryption is enabled in Cinder. + + Security Guide Check Name: Check-Block-09 + + :param audit_options: Dictionary of options for audit configuration + :type audit_options: Dict + :raises: AssertionError if the assertion fails. + """ + key_manager = audit_options['nova-conf']['key_manager'] + assert key_manager.get('backend') is not None, \ + "key_manager.backend should be set" + + +def _config_file(path): + """Read and parse config file at `path` as an ini file. + + :param path: Path of the file + :type path: List[str] + :returns: Parsed contents of the file at path + :rtype Dict: + """ + conf = configparser.ConfigParser() + conf.read(os.path.join(*path)) + return dict(conf) + + +def main(): + config = { + 'config_path': '/etc/nova', + 'config_file': 'nova.conf', + 'audit_type': audits.AuditType.OpenStackSecurityGuide, + 'files': openstack_security_guide.FILE_ASSERTIONS['nova-compute'], + } + config['nova-conf'] = _config_file( + [config['config_path'], config['config_file']]) + return audits.action_parse_results(audits.run(config)) + +if __name__ == "__main__": + sys.exit(main()) diff --git a/hooks/nova_compute_context.py b/hooks/nova_compute_context.py index 4386f0a1..f11f7241 100644 --- a/hooks/nova_compute_context.py +++ b/hooks/nova_compute_context.py @@ -79,6 +79,8 @@ def _save_flag_file(path, data): return with open(path, 'wt') as out: out.write(data) + os.chmod(path, 0o640) + shutil.chown(path, 'root', 'nova') # compatability functions to help with quantum -> neutron transition diff --git a/tests/basic_deployment.py b/tests/basic_deployment.py index 78e9cb59..adc648de 100644 --- a/tests/basic_deployment.py +++ b/tests/basic_deployment.py @@ -519,6 +519,17 @@ class NovaBasicDeployment(OpenStackAmuletDeployment): report = data.get(u"results").get(u"hugepagestats") assert report.find('free_hugepages') != -1 + def test_501_security_checklist_action(self): + """Verify expected result on a default install""" + u.log.debug("Testing security-checklist") + sentry_unit = self.nova_compute_sentry + + action_id = u.run_action(sentry_unit, "security-checklist") + u.wait_on_action(action_id) + data = amulet.actions.get_action_output(action_id, full_output=True) + assert data.get(u"status") == "failed", \ + "Security check is expected to not pass by default" + def test_900_restart_on_config_change(self): """Verify that the specified services are restarted when the config is changed."""