From e75626392b9d228d06e64ed1cfe37da9d2101838 Mon Sep 17 00:00:00 2001 From: Julia Kreger Date: Wed, 17 Aug 2022 08:28:29 -0700 Subject: [PATCH] CI: anaconda: permit tls certificate validation bypass The stock anaconda template previously lacked any ability to indicate "don't validate the tls certificate". The capability for the installation to operate *without* requiring this to be the case is necessary for efficient and simple CI testing as injecting CA certificates is an overly complex interaction for CI testing. Also updates the overall anaconda documentation to indicate the constraint exists, but does not indicate explicitly how to disable the setting via ironic.conf. Change-Id: Ia8e4320cbedb205ab183af121da53562792a8faa --- .../admin/anaconda-deploy-interface.rst | 5 +++++ ironic/common/pxe_utils.py | 2 ++ ironic/conf/anaconda.py | 11 ++++++++++ ironic/drivers/modules/ks.cfg.template | 6 +++--- ironic/tests/unit/common/test_pxe_utils.py | 20 +++++++++++++++++++ ...t-validation-disable-6611d3cb9401031d.yaml | 8 ++++++++ 6 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 releasenotes/notes/anaconda-permit-cert-validation-disable-6611d3cb9401031d.yaml diff --git a/doc/source/admin/anaconda-deploy-interface.rst b/doc/source/admin/anaconda-deploy-interface.rst index 2c686506a8..f489266688 100644 --- a/doc/source/admin/anaconda-deploy-interface.rst +++ b/doc/source/admin/anaconda-deploy-interface.rst @@ -277,5 +277,10 @@ Limitations This deploy interface has only been tested with Red Hat based operating systems that use anaconda. Other systems are not supported. +Runtime TLS certifiate injection into ramdisks is not supported. Assets such +as ``ramdisk`` or a ``stage2`` ramdisk image need to have trusted Certificate +Authority certificates present within the images *or* the Ironic API endpoint +utilized should utilize a known trusted Certificate Authority. + .. _`anaconda`: https://fedoraproject.org/wiki/Anaconda .. _`ks.cfg.template`: https://opendev.org/openstack/ironic/src/branch/master/ironic/drivers/modules/ks.cfg.template diff --git a/ironic/common/pxe_utils.py b/ironic/common/pxe_utils.py index 1849aaa7d6..ec0719b75c 100644 --- a/ironic/common/pxe_utils.py +++ b/ironic/common/pxe_utils.py @@ -1004,6 +1004,8 @@ def build_kickstart_config_options(task): if node.driver_internal_info.get('is_source_a_path', False): # Record a value so it matches as the template opts in. params['is_source_a_path'] = 'true' + if CONF.anaconda.insecure_heartbeat: + params['insecure_heartbeat'] = 'true' params['agent_token'] = node.driver_internal_info['agent_secret_token'] heartbeat_url = '%s/v1/heartbeat/%s' % ( deploy_utils.get_ironic_api_url().rstrip('/'), diff --git a/ironic/conf/anaconda.py b/ironic/conf/anaconda.py index 8ae3ab5330..4f230ecdcd 100644 --- a/ironic/conf/anaconda.py +++ b/ironic/conf/anaconda.py @@ -28,6 +28,17 @@ opts = [ help=_('kickstart template to use when no kickstart template ' 'is specified in the instance_info or the glance OS ' 'image.')), + cfg.BoolOpt('insecure_heartbeat', + default=False, + mutable=True, + help=_('Option to allow the kickstart configuration to be ' + 'informed if SSL/TLS certificate verificaiton should ' + 'be enforced, or not. This option exists largely to ' + 'facilitate easy testing and use of the ``anaconda`` ' + 'deployment interface. When this option is set, ' + 'heartbeat operations, depending on the contents of ' + 'the utilized kickstart template, may not enfore TLS ' + 'certificate verification.')), ] diff --git a/ironic/drivers/modules/ks.cfg.template b/ironic/drivers/modules/ks.cfg.template index ca799953a4..93788fdb8c 100644 --- a/ironic/drivers/modules/ks.cfg.template +++ b/ironic/drivers/modules/ks.cfg.template @@ -36,11 +36,11 @@ liveimg --url {{ ks_options.liveimg_url }} # Following %pre and %onerror sections are mandatory %pre -/usr/bin/curl -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'X-OpenStack-Ironic-API-Version: 1.72' -d '{"callback_url": "", "agent_token": "{{ ks_options.agent_token }}", "agent_status": "start", "agent_status_message": "Deployment starting. Running pre-installation scripts."}' {{ ks_options.heartbeat_url }} +/usr/bin/curl {% if 'insecure_heartbeat' in ks_options %}--insecure{% endif %} -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'X-OpenStack-Ironic-API-Version: 1.72' -d '{"callback_url": "", "agent_token": "{{ ks_options.agent_token }}", "agent_status": "start", "agent_status_message": "Deployment starting. Running pre-installation scripts."}' {{ ks_options.heartbeat_url }} %end %onerror -/usr/bin/curl -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'X-OpenStack-Ironic-API-Version: 1.72' -d '{"callback_url": "", "agent_token": "{{ ks_options.agent_token }}", "agent_status": "error", "agent_status_message": "Error: Deploying using anaconda. Check console for more information."}' {{ ks_options.heartbeat_url }} +/usr/bin/curl {% if 'insecure_heartbeat' in ks_options %}--insecure{% endif %} -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'X-OpenStack-Ironic-API-Version: 1.72' -d '{"callback_url": "", "agent_token": "{{ ks_options.agent_token }}", "agent_status": "error", "agent_status_message": "Error: Deploying using anaconda. Check console for more information."}' {{ ks_options.heartbeat_url }} %end # Config-drive information, if any. @@ -54,5 +54,5 @@ liveimg --url {{ ks_options.liveimg_url }} # before rebooting. %post sync -/usr/bin/curl -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'X-OpenStack-Ironic-API-Version: 1.72' -d '{"callback_url": "", "agent_token": "{{ ks_options.agent_token }}", "agent_status": "end", "agent_status_message": "Deployment completed successfully."}' {{ ks_options.heartbeat_url }} +/usr/bin/curl {% if 'insecure_heartbeat' in ks_options %}--insecure{% endif %} -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'X-OpenStack-Ironic-API-Version: 1.72' -d '{"callback_url": "", "agent_token": "{{ ks_options.agent_token }}", "agent_status": "end", "agent_status_message": "Deployment completed successfully."}' {{ ks_options.heartbeat_url }} %end diff --git a/ironic/tests/unit/common/test_pxe_utils.py b/ironic/tests/unit/common/test_pxe_utils.py index f9d7818305..3e57b83ed7 100644 --- a/ironic/tests/unit/common/test_pxe_utils.py +++ b/ironic/tests/unit/common/test_pxe_utils.py @@ -1628,6 +1628,26 @@ class PXEBuildKickstartConfigOptionsTestCase(db_base.DbTestCase): params = pxe_utils.build_kickstart_config_options(task) self.assertTrue(params['ks_options'].pop('agent_token')) self.assertEqual(expected, params['ks_options']) + self.assertNotIn('insecure_heartbeat', params) + + @mock.patch.object(deploy_utils, 'get_ironic_api_url', autospec=True) + def test_build_kickstart_config_options_pxe_insecure_heartbeat( + self, api_url_mock): + api_url_mock.return_value = 'http://ironic-api' + self.assertFalse(CONF.anaconda.insecure_heartbeat) + CONF.set_override('insecure_heartbeat', True, 'anaconda') + with task_manager.acquire(self.context, self.node.uuid, + shared=True) as task: + expected = {} + expected['liveimg_url'] = task.node.instance_info['image_url'] + expected['config_drive'] = '' + expected['heartbeat_url'] = ( + 'http://ironic-api/v1/heartbeat/%s' % task.node.uuid + ) + expected['insecure_heartbeat'] = 'true' + params = pxe_utils.build_kickstart_config_options(task) + self.assertTrue(params['ks_options'].pop('agent_token')) + self.assertEqual(expected, params['ks_options']) @mock.patch('ironic.common.utils.render_template', autospec=True) def test_prepare_instance_kickstart_config_not_anaconda_boot(self, diff --git a/releasenotes/notes/anaconda-permit-cert-validation-disable-6611d3cb9401031d.yaml b/releasenotes/notes/anaconda-permit-cert-validation-disable-6611d3cb9401031d.yaml new file mode 100644 index 0000000000..59d306c5dc --- /dev/null +++ b/releasenotes/notes/anaconda-permit-cert-validation-disable-6611d3cb9401031d.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + Adds a configuration option, ``[anaconda]insecure_heartbeat`` to allow + for TLS certificate validation to be disabled in the ``anaconda`` + deployment interface, which is needed for continious integration to + be able to be performed without substantial substrate image customization. + This option is *not* advised for any production usage.