diff --git a/devstack/lib/ironic b/devstack/lib/ironic
index a53cc6824a..d1c98f5979 100644
--- a/devstack/lib/ironic
+++ b/devstack/lib/ironic
@@ -1703,12 +1703,14 @@ function configure_ironic_conductor {
 
     local kernel_append_params="nofb nomodeset console=${IRONIC_TTY_DEV}"
     kernel_append_params+=" systemd.journald.forward_to_console=yes"
-    if is_service_enabled tls-proxy; then
-        kernel_append_params+=" ipa-insecure=1"
-    fi
 
-    if [[ -n "kernel_append_params" ]]; then
-        iniset $IRONIC_CONF_FILE redfish kernel_append_params "$kernel_append_params"
+    # NOTE(dtantsur): avoid setting ipa-insecure for redfish, we have a way to
+    # pass the TLS certificate.
+    iniset $IRONIC_CONF_FILE redfish kernel_append_params "$kernel_append_params"
+
+    if is_service_enabled tls-proxy; then
+        deploy_int_CA "$IRONIC_STATE_PATH/ironic-ca.pem"
+        iniset $IRONIC_CONF_FILE agent api_ca_file "$IRONIC_STATE_PATH/ironic-ca.pem"
     fi
 
     # Set these options for scenarios in which the agent fetches the image
diff --git a/ironic/common/images.py b/ironic/common/images.py
index 3f9fb88379..778f9a12df 100644
--- a/ironic/common/images.py
+++ b/ironic/common/images.py
@@ -58,6 +58,8 @@ def _create_root_fs(root_directory, files_info):
     :raises: IOError, if copying any of the files failed.
     """
     for src_file, path in files_info.items():
+        LOG.debug('Injecting %(path)s into an ISO from %(source)r',
+                  {'path': path, 'source': src_file})
         target_file = os.path.join(root_directory, path)
         dirname = os.path.dirname(target_file)
         if dirname:
diff --git a/ironic/conf/agent.py b/ironic/conf/agent.py
index 55e9aea1c5..33b8b65548 100644
--- a/ironic/conf/agent.py
+++ b/ironic/conf/agent.py
@@ -159,6 +159,10 @@ opts = [
                       'ramdisk. Set to True to use the system default CA '
                       'storage. Set to False to disable validation. Ignored '
                       'when automatic TLS setup is used.')),
+    cfg.StrOpt('api_ca_file',
+               help=_('Path to the TLS CA that is used to start the bare '
+                      'metal API. In some boot methods this file can be '
+                      'passed to the ramdisk.')),
 ]
 
 
diff --git a/ironic/drivers/modules/image_utils.py b/ironic/drivers/modules/image_utils.py
index b50ffe867f..b8d2f0c161 100644
--- a/ironic/drivers/modules/image_utils.py
+++ b/ironic/drivers/modules/image_utils.py
@@ -388,6 +388,12 @@ def _find_param(param_str, param_dict):
     return val
 
 
+_TLS_REMOTE_FILE = 'etc/ironic-python-agent/ironic.crt'
+_TLS_CONFIG_TEMPLATE = """[DEFAULT]
+cafile = /%s
+""" % _TLS_REMOTE_FILE
+
+
 def prepare_deploy_iso(task, params, mode, d_info):
     """Prepare deploy or rescue ISO image
 
@@ -430,6 +436,11 @@ def prepare_deploy_iso(task, params, mode, d_info):
         bootloader_href=bootloader_href, params=params)
 
     inject_files = {}
+    if CONF.agent.api_ca_file:
+        inject_files[CONF.agent.api_ca_file] = _TLS_REMOTE_FILE
+        inject_files[_TLS_CONFIG_TEMPLATE.encode('utf-8')] = \
+            'etc/ironic-python-agent.d/ironic-tls.conf'
+
     network_data = task.driver.network.get_node_network_data(task)
     if network_data:
         LOG.debug('Injecting custom network data for node %s',
diff --git a/ironic/tests/unit/drivers/modules/test_image_utils.py b/ironic/tests/unit/drivers/modules/test_image_utils.py
index 7c177a5522..d555ab3f3e 100644
--- a/ironic/tests/unit/drivers/modules/test_image_utils.py
+++ b/ironic/tests/unit/drivers/modules/test_image_utils.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 import os
+import tempfile
 from unittest import mock
 
 from oslo_utils import importutils
@@ -430,6 +431,52 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase):
 
             find_mock.assert_has_calls(find_call_list)
 
+    @mock.patch.object(image_utils, '_find_param', autospec=True)
+    @mock.patch.object(image_utils, '_prepare_iso_image', autospec=True)
+    def test_prepare_deploy_iso_tls(self, mock__prepare_iso_image,
+                                    find_mock):
+        with tempfile.NamedTemporaryFile(delete=False) as tf:
+            temp_name = tf.name
+            self.addCleanup(lambda: os.unlink(temp_name))
+            self.config(api_ca_file=temp_name, group='agent')
+            tf.write(b'I can haz SSLz')
+
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=True) as task:
+
+            d_info = {
+                'ilo_deploy_kernel': 'kernel',
+                'ilo_deploy_ramdisk': 'ramdisk',
+                'ilo_bootloader': 'bootloader'
+            }
+            task.node.driver_info.update(d_info)
+
+            find_call_list = [
+                mock.call('deploy_kernel', d_info),
+                mock.call('deploy_ramdisk', d_info),
+                mock.call('bootloader', d_info)
+            ]
+            find_mock.side_effect = [
+                'kernel', 'ramdisk', 'bootloader'
+            ]
+
+            task.node.instance_info.update(deploy_boot_mode='uefi')
+
+            image_utils.prepare_deploy_iso(task, {}, 'deploy', d_info)
+
+            expected_files = {
+                b"""[DEFAULT]
+cafile = /etc/ironic-python-agent/ironic.crt
+""": 'etc/ironic-python-agent.d/ironic-tls.conf',
+                temp_name: 'etc/ironic-python-agent/ironic.crt'
+            }
+
+            mock__prepare_iso_image.assert_called_once_with(
+                task, 'kernel', 'ramdisk', 'bootloader', params={},
+                inject_files=expected_files)
+
+            find_mock.assert_has_calls(find_call_list)
+
     @mock.patch.object(image_utils, '_find_param', autospec=True)
     @mock.patch.object(image_utils, '_prepare_iso_image', autospec=True)
     @mock.patch.object(images, 'create_boot_iso', autospec=True)
diff --git a/releasenotes/notes/vmedia-tls-117daa5ae0a9e30d.yaml b/releasenotes/notes/vmedia-tls-117daa5ae0a9e30d.yaml
new file mode 100644
index 0000000000..627f6e8f1f
--- /dev/null
+++ b/releasenotes/notes/vmedia-tls-117daa5ae0a9e30d.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    A new option ``[agent]api_ca_file`` allows passing a CA file to the
+    ramdisk when ``redfish-virtual-media`` boot is used. Requires
+    ironic-python-agent from the Wallaby cycle.