diff --git a/ansible/certificates.yml b/ansible/certificates.yml
index 4b6d2528d96eb96769d8b7542d183c2a0d4be263..28a1a5f8ee1cd400e3ea63c16be394781751efa3 100644
--- a/ansible/certificates.yml
+++ b/ansible/certificates.yml
@@ -1,6 +1,8 @@
 ---
 - import_playbook: gather-facts.yml
-  when: kolla_enable_tls_backend | default(false) | bool
+  when: >-
+    kolla_enable_tls_backend | default(false) | bool or
+    rabbitmq_enable_tls | default(false) | bool
 
 - name: Apply role certificates
   hosts: localhost
diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml
index aa4938b08045d43528fa468a8c3477c86e327766..fe30d155eab3add98c642d5478db7d02256e40be 100644
--- a/ansible/group_vars/all.yml
+++ b/ansible/group_vars/all.yml
@@ -192,6 +192,11 @@ om_notify_vhost: "/"
 
 notify_transport_url: "{{ om_notify_transport }}://{% for host in groups[om_notify_group] %}{{ om_notify_user }}:{{ om_notify_password }}@{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ om_notify_port }}{% if not loop.last %},{% endif %}{% endfor %}/{{ om_notify_vhost }}"
 
+# Whether to enable TLS for oslo.messaging communication with RabbitMQ.
+om_enable_rabbitmq_tls: "{{ rabbitmq_enable_tls | bool }}"
+# CA certificate bundle in containers using oslo.messaging with RabbitMQ TLS.
+om_rabbitmq_cacert: "{{ rabbitmq_cacert }}"
+
 ####################
 # Networking options
 ####################
@@ -426,7 +431,7 @@ qdrouterd_port: "31459"
 
 qinling_api_port: "7070"
 
-rabbitmq_port: "5672"
+rabbitmq_port: "{{ '5671' if rabbitmq_enable_tls | bool else '5672' }}"
 rabbitmq_management_port: "15672"
 rabbitmq_cluster_port: "25672"
 rabbitmq_epmd_port: "4369"
@@ -748,6 +753,10 @@ osprofiler_backend_connection_string: "{{ redis_connection_string if osprofiler_
 rabbitmq_user: "openstack"
 rabbitmq_monitoring_user: ""
 outward_rabbitmq_user: "openstack"
+# Whether to enable TLS encryption for RabbitMQ client-server communication.
+rabbitmq_enable_tls: "no"
+# CA certificate bundle in RabbitMQ container.
+rabbitmq_cacert: "/etc/ssl/certs/{{ 'ca-certificates.crt' if kolla_base_distro in ['debian', 'ubuntu'] else 'ca-bundle.trust.crt' }}"
 
 ####################
 # Qdrouterd options
diff --git a/ansible/roles/aodh/templates/aodh.conf.j2 b/ansible/roles/aodh/templates/aodh.conf.j2
index 6745323ca695fbb5d75a6ed328409b4180abd71c..b9c3b08475d1a3b19b1401c277f5137681101e1f 100644
--- a/ansible/roles/aodh/templates/aodh.conf.j2
+++ b/ansible/roles/aodh/templates/aodh.conf.j2
@@ -58,3 +58,8 @@ topics = {{ aodh_enabled_notification_topics | map(attribute='name') | join(',')
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
diff --git a/ansible/roles/barbican/templates/barbican.conf.j2 b/ansible/roles/barbican/templates/barbican.conf.j2
index d7b698579f6ee6bcd2db26fd1750daeafed69b65..1c858fa6ddac9e2e00916c5af2704f770747928e 100644
--- a/ansible/roles/barbican/templates/barbican.conf.j2
+++ b/ansible/roles/barbican/templates/barbican.conf.j2
@@ -75,6 +75,12 @@ topics = {{ barbican_enabled_notification_topics | map(attribute='name') | join(
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 [oslo_middleware]
 enable_proxy_headers_parsing = True
 
diff --git a/ansible/roles/blazar/templates/blazar.conf.j2 b/ansible/roles/blazar/templates/blazar.conf.j2
index 734b9c326d5054baecfaf4b22f00c3c0f4c4ec34..2fdc6adfa6a5254f64ac5541576f0ad1fce203f2 100644
--- a/ansible/roles/blazar/templates/blazar.conf.j2
+++ b/ansible/roles/blazar/templates/blazar.conf.j2
@@ -60,6 +60,11 @@ topics = {{ blazar_enabled_notification_topics | map(attribute='name') | join(',
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
 
 {% if blazar_policy_file is defined %}
 [oslo_policy]
diff --git a/ansible/roles/ceilometer/templates/ceilometer.conf.j2 b/ansible/roles/ceilometer/templates/ceilometer.conf.j2
index 1f15481fa8ba8b4c4a3d42253b4c09a25997bc15..e87fdb494a07c99ba0dd3426f751b1fcb0e0f945 100644
--- a/ansible/roles/ceilometer/templates/ceilometer.conf.j2
+++ b/ansible/roles/ceilometer/templates/ceilometer.conf.j2
@@ -35,6 +35,12 @@ ca_file = /etc/ceilometer/vmware_ca
 [oslo_messaging_notifications]
 transport_url = {{ notify_transport_url }}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 {% if ceilometer_policy_file is defined %}
 [oslo_policy]
 policy_file = {{ ceilometer_policy_file }}
diff --git a/ansible/roles/certificates/tasks/generate-backend.yml b/ansible/roles/certificates/tasks/generate-backend.yml
index 8eab9e48b3c8abca51323e95de8dc9a9b30e5678..341f5dcdb73c6d90fbbbd3e602553bf0ac668876 100644
--- a/ansible/roles/certificates/tasks/generate-backend.yml
+++ b/ansible/roles/certificates/tasks/generate-backend.yml
@@ -62,3 +62,16 @@
     src: "{{ backend_dir }}/backend.key"
     dest: "{{ kolla_certificates_dir }}/backend-key.pem"
     mode: "0660"
+
+- name: Copy backend TLS certificate and key for RabbitMQ
+  copy:
+    src: "{{ item.src }}"
+    dest: "{{ item.dest }}"
+    remote_src: true
+  with_items:
+    - src: "{{ kolla_tls_backend_cert }}"
+      dest: "{{ kolla_certificates_dir }}/rabbitmq-cert.pem"
+    - src: "{{ kolla_tls_backend_key }}"
+      dest: "{{ kolla_certificates_dir }}/rabbitmq-key.pem"
+  when:
+    - rabbitmq_enable_tls | bool
diff --git a/ansible/roles/certificates/tasks/main.yml b/ansible/roles/certificates/tasks/main.yml
index 21b253bebb619cd91e0278ff59e431593c54680b..f8d80dd9b98845c76c4b82cfd37a798a3be332b8 100644
--- a/ansible/roles/certificates/tasks/main.yml
+++ b/ansible/roles/certificates/tasks/main.yml
@@ -3,4 +3,4 @@
 - include_tasks: generate.yml
 - include_tasks: generate-backend.yml
   when:
-    - kolla_enable_tls_backend | bool
+    - kolla_enable_tls_backend | bool or rabbitmq_enable_tls | bool
diff --git a/ansible/roles/cinder/templates/cinder.conf.j2 b/ansible/roles/cinder/templates/cinder.conf.j2
index 2ab967de6f34842723081e674b6c8b41527bd420..c80ae265de8bc4fdb65fa4f319c7da376297b20c 100644
--- a/ansible/roles/cinder/templates/cinder.conf.j2
+++ b/ansible/roles/cinder/templates/cinder.conf.j2
@@ -69,6 +69,12 @@ topics = {{ cinder_enabled_notification_topics | map(attribute='name') | join(',
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 [oslo_middleware]
 enable_proxy_headers_parsing = True
 
diff --git a/ansible/roles/cloudkitty/templates/cloudkitty.conf.j2 b/ansible/roles/cloudkitty/templates/cloudkitty.conf.j2
index cc1ebaf5aa531165275deecae47ffe7d26b937bb..16293a79a671b567d235ba610431cf598df91504 100644
--- a/ansible/roles/cloudkitty/templates/cloudkitty.conf.j2
+++ b/ansible/roles/cloudkitty/templates/cloudkitty.conf.j2
@@ -43,6 +43,12 @@ lock_path = /var/lib/cloudkitty/tmp
 policy_file = {{ cloudkitty_policy_file }}
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 [collect]
 collector = {{ cloudkitty_collector_backend }}
 {% if cloudkitty_custom_metrics_used %}
diff --git a/ansible/roles/cyborg/templates/cyborg.conf.j2 b/ansible/roles/cyborg/templates/cyborg.conf.j2
index 61a5673b1df99a87bde99d0c03f89c284f53b556..89a3191090c6296a2b4796e0688cd43277246233 100644
--- a/ansible/roles/cyborg/templates/cyborg.conf.j2
+++ b/ansible/roles/cyborg/templates/cyborg.conf.j2
@@ -54,3 +54,9 @@ topics = {{ cyborg_enabled_notification_topics | map(attribute='name') | join(',
 {% else %}
 driver = noop
 {% endif %}
+
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
diff --git a/ansible/roles/designate/templates/designate.conf.j2 b/ansible/roles/designate/templates/designate.conf.j2
index 2d1701aa56412a3b88bd887e479ae90529f99c3c..67101ef742823f7fd4391669f77e0500d47e2553 100644
--- a/ansible/roles/designate/templates/designate.conf.j2
+++ b/ansible/roles/designate/templates/designate.conf.j2
@@ -89,6 +89,12 @@ topics = {{ designate_enabled_notification_topics | map(attribute='name') | join
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 [oslo_concurrency]
 lock_path = /var/lib/designate/tmp
 
diff --git a/ansible/roles/glance/templates/glance-api.conf.j2 b/ansible/roles/glance/templates/glance-api.conf.j2
index 5ad3a5c00cf26324550315449ab523d17c63c7b4..d784df3ca8861be816161343cec976bf9f25e10e 100644
--- a/ansible/roles/glance/templates/glance-api.conf.j2
+++ b/ansible/roles/glance/templates/glance-api.conf.j2
@@ -120,6 +120,12 @@ topics = {{ glance_enabled_notification_topics | map(attribute='name') | join(',
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 {% if glance_policy_file is defined %}
 [oslo_policy]
 policy_file = {{ glance_policy_file }}
diff --git a/ansible/roles/heat/templates/heat.conf.j2 b/ansible/roles/heat/templates/heat.conf.j2
index c7245e0a26c80fe9fbc33e37e138b59f9c422385..08f32be9abe4a491837991b68861fe47983f800e 100644
--- a/ansible/roles/heat/templates/heat.conf.j2
+++ b/ansible/roles/heat/templates/heat.conf.j2
@@ -84,6 +84,12 @@ topics = {{ heat_enabled_notification_topics | map(attribute='name') | join(',')
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 {% if heat_policy_file is defined %}
 [oslo_policy]
 policy_file = {{ heat_policy_file }}
diff --git a/ansible/roles/ironic/templates/ironic-inspector.conf.j2 b/ansible/roles/ironic/templates/ironic-inspector.conf.j2
index 3a3d4a8933898754e5f140deff64d17face946e9..ac7f62211e5975da31cd521321c9a9a4a2d81ab6 100644
--- a/ansible/roles/ironic/templates/ironic-inspector.conf.j2
+++ b/ansible/roles/ironic/templates/ironic-inspector.conf.j2
@@ -12,6 +12,12 @@ transport_url = {{ rpc_transport_url }}
 [oslo_messaging_notifications]
 transport_url = {{ notify_transport_url }}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 [ironic]
 {% if enable_keystone | bool %}
 auth_url = {{ keystone_admin_url }}
diff --git a/ansible/roles/ironic/templates/ironic.conf.j2 b/ansible/roles/ironic/templates/ironic.conf.j2
index 79d387cf8a5e480b7ce7721feb3c226705693e9e..7854d0eccc5e06448b0c5ee0f4cd26e2dafb3d73 100644
--- a/ansible/roles/ironic/templates/ironic.conf.j2
+++ b/ansible/roles/ironic/templates/ironic.conf.j2
@@ -30,6 +30,12 @@ topics = {{ ironic_enabled_notification_topics | map(attribute='name') | join(',
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 {% if ironic_policy_file is defined %}
 [oslo_policy]
 policy_file = {{ ironic_policy_file }}
diff --git a/ansible/roles/karbor/templates/karbor.conf.j2 b/ansible/roles/karbor/templates/karbor.conf.j2
index d31985b12c7815302c882af38528a6da38444bc8..936f204f27673f2bb231be6c9bd9c8060baea811 100644
--- a/ansible/roles/karbor/templates/karbor.conf.j2
+++ b/ansible/roles/karbor/templates/karbor.conf.j2
@@ -57,5 +57,11 @@ topics = {{ karbor_enabled_notification_topics | map(attribute='name') | join(',
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 [oslo_middleware]
 enable_proxy_headers_parsing = True
diff --git a/ansible/roles/keystone/templates/keystone.conf.j2 b/ansible/roles/keystone/templates/keystone.conf.j2
index 06d57eb3b2f6312f84c0224758fe7d55ea7c56ba..730107eaca1bfa8ea7b1df6047abea161fe61369 100644
--- a/ansible/roles/keystone/templates/keystone.conf.j2
+++ b/ansible/roles/keystone/templates/keystone.conf.j2
@@ -59,6 +59,11 @@ topics = {{ keystone_enabled_notification_topics | map(attribute='name') | join(
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
 
 {% if enable_osprofiler | bool %}
 [profiler]
diff --git a/ansible/roles/magnum/templates/magnum.conf.j2 b/ansible/roles/magnum/templates/magnum.conf.j2
index c0cb531aa9c86cea563295e027ca1af79a1629f8..f1d9742562f68121cc865f94c1673aed458cefcf 100644
--- a/ansible/roles/magnum/templates/magnum.conf.j2
+++ b/ansible/roles/magnum/templates/magnum.conf.j2
@@ -112,6 +112,12 @@ topics = {{ magnum_enabled_notification_topics | map(attribute='name') | join(',
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 {% if magnum_policy_file is defined %}
 [oslo_policy]
 policy_file = {{ magnum_policy_file }}
diff --git a/ansible/roles/manila/templates/manila.conf.j2 b/ansible/roles/manila/templates/manila.conf.j2
index 171033f732accc77accbf3b008543aa2c4903460..9bf62cadf7ee42865e9867b56cb7dd66919bac84 100644
--- a/ansible/roles/manila/templates/manila.conf.j2
+++ b/ansible/roles/manila/templates/manila.conf.j2
@@ -54,6 +54,11 @@ topics = {{ manila_enabled_notification_topics | map(attribute='name') | join(',
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
 
 [oslo_middleware]
 enable_proxy_headers_parsing = True
diff --git a/ansible/roles/masakari/templates/masakari.conf.j2 b/ansible/roles/masakari/templates/masakari.conf.j2
index 5a0bab0fc945759ac9444af7bd0d8fdae13763d0..da0ef60ad4c9bab06c917e4eab2d7e3dd8554ab3 100644
--- a/ansible/roles/masakari/templates/masakari.conf.j2
+++ b/ansible/roles/masakari/templates/masakari.conf.j2
@@ -48,6 +48,12 @@ topics = notifications
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 [oslo_middleware]
 enable_proxy_headers_parsing = True
 
diff --git a/ansible/roles/mistral/templates/mistral.conf.j2 b/ansible/roles/mistral/templates/mistral.conf.j2
index de3540ad5c393bd57d4631a0303013781353a63e..c784c1105eee290d2d58f5ccac2d940dc9a7cf72 100644
--- a/ansible/roles/mistral/templates/mistral.conf.j2
+++ b/ansible/roles/mistral/templates/mistral.conf.j2
@@ -70,6 +70,12 @@ topics = {{ mistral_enabled_notification_topics | map(attribute='name') | join('
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 {% if mistral_policy_file is defined %}
 [oslo_policy]
 policy_file = {{ mistral_policy_file }}
diff --git a/ansible/roles/murano/templates/murano.conf.j2 b/ansible/roles/murano/templates/murano.conf.j2
index ce67b458f7e043806622fa03a603261d6fd8416a..5b9194a7743138108edabb1cd380bb14bc823768 100644
--- a/ansible/roles/murano/templates/murano.conf.j2
+++ b/ansible/roles/murano/templates/murano.conf.j2
@@ -59,6 +59,12 @@ topics = {{ murano_enabled_notification_topics | map(attribute='name') | join(',
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 [oslo_middleware]
 enable_proxy_headers_parsing = True
 
diff --git a/ansible/roles/neutron/templates/neutron.conf.j2 b/ansible/roles/neutron/templates/neutron.conf.j2
index 4b8510dea5a77feba7f29f5bc199b870ef5c7021..ee7ed148c8b0a5e7f3facc29bac64d71fd84c2ee 100644
--- a/ansible/roles/neutron/templates/neutron.conf.j2
+++ b/ansible/roles/neutron/templates/neutron.conf.j2
@@ -129,6 +129,12 @@ topics = {{ neutron_enabled_notification_topics | map(attribute='name') | join('
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 {% if neutron_policy_file is defined %}
 [oslo_policy]
 policy_file = {{ neutron_policy_file }}
diff --git a/ansible/roles/nova-cell/templates/nova.conf.j2 b/ansible/roles/nova-cell/templates/nova.conf.j2
index d54ab23be5563a9b59e01700032684df1b5de20d..81852d5e700b1ea40bafedc204e8540bdc4f8be5 100644
--- a/ansible/roles/nova-cell/templates/nova.conf.j2
+++ b/ansible/roles/nova-cell/templates/nova.conf.j2
@@ -183,6 +183,12 @@ topics = {{ nova_enabled_notification_topics | map(attribute='name') | join(',')
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 {% if service_name in nova_cell_services_require_policy_json and nova_policy_file is defined %}
 [oslo_policy]
 policy_file = {{ nova_policy_file }}
diff --git a/ansible/roles/nova/templates/nova.conf.j2 b/ansible/roles/nova/templates/nova.conf.j2
index 5a7f88dbbda2cc10ae9c90516cf21f6ae997181a..2795c1475945d217c3d61dc6f4d0c0cdc4e3f2d5 100644
--- a/ansible/roles/nova/templates/nova.conf.j2
+++ b/ansible/roles/nova/templates/nova.conf.j2
@@ -132,6 +132,12 @@ topics = {{ nova_enabled_notification_topics | map(attribute='name') | join(',')
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 {% if service_name in nova_services_require_policy_json and nova_policy_file is defined %}
 [oslo_policy]
 policy_file = {{ nova_policy_file }}
diff --git a/ansible/roles/octavia/templates/octavia.conf.j2 b/ansible/roles/octavia/templates/octavia.conf.j2
index 33289a9a91763b9d3f193ab0819b51e5e8c18ab9..3d42d7aa77f8268ad6da497d9c0bbcf9c4655f65 100644
--- a/ansible/roles/octavia/templates/octavia.conf.j2
+++ b/ansible/roles/octavia/templates/octavia.conf.j2
@@ -88,6 +88,12 @@ rpc_thread_pool_size = 2
 [oslo_messaging_notifications]
 transport_url = {{ notify_transport_url }}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 {% if octavia_policy_file is defined %}
 [oslo_policy]
 policy_file = {{ octavia_policy_file }}
diff --git a/ansible/roles/qinling/templates/qinling.conf.j2 b/ansible/roles/qinling/templates/qinling.conf.j2
index 030edd9354574377630206695fe2b2d2fd07542f..ee9f8702c96ff36d9608649f279dd1288b6430d7 100644
--- a/ansible/roles/qinling/templates/qinling.conf.j2
+++ b/ansible/roles/qinling/templates/qinling.conf.j2
@@ -55,6 +55,12 @@ topics = notifications
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 {% if qinling_policy_file is defined %}
 [oslo_policy]
 policy_file = {{ qinling_policy_file }}
diff --git a/ansible/roles/rabbitmq/defaults/main.yml b/ansible/roles/rabbitmq/defaults/main.yml
index 8c3553a040c5f5969be48b35482d296f283546aa..1d93ca5491440b0e87901586cb382b405e650c14 100644
--- a/ansible/roles/rabbitmq/defaults/main.yml
+++ b/ansible/roles/rabbitmq/defaults/main.yml
@@ -72,3 +72,5 @@ rabbitmq_cluster_name: "openstack"
 rabbitmq_hostname: "{{ ansible_hostname }}"
 rabbitmq_pid_file: "/var/lib/rabbitmq/mnesia/rabbitmq.pid"
 rabbitmq_server_additional_erl_args: ""
+# Dict of TLS options for RabbitMQ. Keys will be prefixed with 'ssl_options.'.
+rabbitmq_tls_options: {}
diff --git a/ansible/roles/rabbitmq/handlers/main.yml b/ansible/roles/rabbitmq/handlers/main.yml
index 515126e0bf5be3583fbf766878237a2efdef12c8..d3cb4d717df9b0ef1e05d5347dba8ab8cebaade4 100644
--- a/ansible/roles/rabbitmq/handlers/main.yml
+++ b/ansible/roles/rabbitmq/handlers/main.yml
@@ -17,6 +17,7 @@
     - inventory_hostname == groups[service.group]|first
   notify:
     - Waiting for rabbitmq to start on first node
+  listen: Restart rabbitmq container
 
 - name: Waiting for rabbitmq to start on first node
   vars:
@@ -43,3 +44,4 @@
   when:
     - kolla_action != "config"
     - inventory_hostname != groups[service.group]|first
+  listen: Restart rabbitmq container
diff --git a/ansible/roles/rabbitmq/tasks/check-containers.yml b/ansible/roles/rabbitmq/tasks/check-containers.yml
index 7cb590b5b96be086399bc00bc6bdf062e0ccfbf3..0ff847b49f700a3d0f9b42b26d9be2c82a5c0789 100644
--- a/ansible/roles/rabbitmq/tasks/check-containers.yml
+++ b/ansible/roles/rabbitmq/tasks/check-containers.yml
@@ -14,5 +14,4 @@
     - item.value.enabled | bool
   with_dict: "{{ rabbitmq_services }}"
   notify:
-    - Restart rabbitmq container (first node)
-    - Restart rabbitmq container (rest of nodes)
+    - Restart rabbitmq container
diff --git a/ansible/roles/rabbitmq/tasks/config.yml b/ansible/roles/rabbitmq/tasks/config.yml
index 25a977b6c51f9532cefc4f67cc5876230c0401c8..9e9f4f221303e2e58be030b98fc5bff9c5c5f7d6 100644
--- a/ansible/roles/rabbitmq/tasks/config.yml
+++ b/ansible/roles/rabbitmq/tasks/config.yml
@@ -23,8 +23,7 @@
     - item.value.enabled | bool
   with_dict: "{{ rabbitmq_services }}"
   notify:
-    - Restart rabbitmq container (first node)
-    - Restart rabbitmq container (rest of nodes)
+    - Restart rabbitmq container
 
 - name: Copying over rabbitmq-env.conf
   become: true
@@ -42,9 +41,7 @@
     - inventory_hostname in groups[service.group]
     - service.enabled | bool
   notify:
-    - Restart rabbitmq container (first node)
-    - Restart rabbitmq container (rest of nodes)
-
+    - Restart rabbitmq container
 
 - name: Copying over rabbitmq.conf
   become: true
@@ -62,8 +59,7 @@
     - inventory_hostname in groups[service.group]
     - service.enabled | bool
   notify:
-    - Restart rabbitmq container (first node)
-    - Restart rabbitmq container (rest of nodes)
+    - Restart rabbitmq container
 
 - name: Copying over erl_inetrc
   become: true
@@ -81,8 +77,7 @@
     - inventory_hostname in groups[service.group]
     - service.enabled | bool
   notify:
-    - Restart rabbitmq container (first node)
-    - Restart rabbitmq container (rest of nodes)
+    - Restart rabbitmq container
 
 - name: Copying over definitions.json
   become: true
@@ -100,8 +95,10 @@
     - inventory_hostname in groups[service.group]
     - service.enabled | bool
   notify:
-    - Restart rabbitmq container (first node)
-    - Restart rabbitmq container (rest of nodes)
+    - Restart rabbitmq container
+
+- include_tasks: copy-certs.yml
+  when: rabbitmq_enable_tls | bool
 
 - import_tasks: check-containers.yml
   when: kolla_action != "config"
diff --git a/ansible/roles/rabbitmq/tasks/copy-certs.yml b/ansible/roles/rabbitmq/tasks/copy-certs.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f3c84a49ababc45877913894fbd568d0d24112e9
--- /dev/null
+++ b/ansible/roles/rabbitmq/tasks/copy-certs.yml
@@ -0,0 +1,52 @@
+---
+- name: Copying over extra CA certificates
+  become: true
+  vars:
+    service: "{{ rabbitmq_services['rabbitmq'] }}"
+  copy:
+    src: "{{ kolla_certificates_dir }}/ca/"
+    dest: "{{ node_config_directory }}/{{ project_name }}/ca-certificates"
+    mode: "0644"
+  when:
+    - kolla_copy_ca_into_containers | bool
+    - service | service_enabled_and_mapped_to_host
+  notify:
+    - Restart rabbitmq container
+
+- name: Copying over TLS certificate
+  become: true
+  vars:
+    service: "{{ rabbitmq_services['rabbitmq'] }}"
+  copy:
+    src: "{{ item }}"
+    dest: "{{ node_config_directory }}/{{ project_name }}/{{ project_name }}-cert.pem"
+    mode: "0644"
+  with_first_found:
+    - files:
+        - "{{ kolla_certificates_dir }}/{{ inventory_hostname }}/{{ project_name }}-cert.pem"
+        - "{{ kolla_certificates_dir }}/{{ inventory_hostname }}-cert.pem"
+        - "{{ kolla_certificates_dir }}/{{ project_name }}-cert.pem"
+      skip: true
+  when:
+    - service | service_enabled_and_mapped_to_host
+  notify:
+    - Restart rabbitmq container
+
+- name: Copying over TLS key
+  become: true
+  vars:
+    service: "{{ rabbitmq_services['rabbitmq'] }}"
+  copy:
+    src: "{{ item }}"
+    dest: "{{ node_config_directory }}/{{ project_name }}/{{ project_name }}-key.pem"
+    mode: "0600"
+  with_first_found:
+    - files:
+        - "{{ kolla_certificates_dir }}/{{ inventory_hostname }}/{{ project_name }}-key.pem"
+        - "{{ kolla_certificates_dir }}/{{ inventory_hostname }}-key.pem"
+        - "{{ kolla_certificates_dir }}/{{ project_name }}-key.pem"
+      skip: true
+  when:
+    - service | service_enabled_and_mapped_to_host
+  notify:
+    - Restart rabbitmq container
diff --git a/ansible/roles/rabbitmq/tasks/precheck.yml b/ansible/roles/rabbitmq/tasks/precheck.yml
index a7baf2afa422c9871ccac5293f1c3985585dbf86..3dc37bc88f5e8fc1f982d4f9937403205dbeafe2 100644
--- a/ansible/roles/rabbitmq/tasks/precheck.yml
+++ b/ansible/roles/rabbitmq/tasks/precheck.yml
@@ -74,6 +74,32 @@
   when:
     - not item.1 is match('^'+('api' | kolla_address(item.0.item))+'\\b')
 
+- name: Check if TLS certificate exists for RabbitMQ
+  vars:
+    cert: "{{ query('first_found', paths, errors='ignore') }}"
+    paths:
+      - "{{ kolla_certificates_dir }}/{{ inventory_hostname }}/rabbitmq-cert.pem"
+      - "{{ kolla_certificates_dir }}/{{ inventory_hostname }}-cert.pem"
+      - "{{ kolla_certificates_dir }}/rabbitmq-cert.pem"
+  fail:
+    msg: No TLS certificate provided for RabbitMQ.
+  when:
+    - rabbitmq_enable_tls | bool
+    - cert | length == 0
+
+- name: Check if TLS key exists for RabbitMQ
+  vars:
+    key: "{{ query('first_found', paths, errors='ignore') }}"
+    paths:
+      - "{{ kolla_certificates_dir }}/{{ inventory_hostname }}/rabbitmq-key.pem"
+      - "{{ kolla_certificates_dir }}/{{ inventory_hostname }}-key.pem"
+      - "{{ kolla_certificates_dir }}/rabbitmq-key.pem"
+  fail:
+    msg: No TLS key provided for RabbitMQ.
+  when:
+    - rabbitmq_enable_tls | bool
+    - key | length == 0
+
 - name: Checking free port for outward RabbitMQ
   wait_for:
     host: "{{ api_interface_address }}"
@@ -137,3 +163,31 @@
   when:
     - enable_outward_rabbitmq | bool
     - not item.1 is match('^'+('api' | kolla_address(item.0.item))+'\\b')
+
+- name: Check if TLS certificate exists for outward RabbitMQ
+  vars:
+    cert: "{{ query('first_found', paths, errors='ignore') }}"
+    paths:
+      - "{{ kolla_certificates_dir }}/{{ inventory_hostname }}/outward_rabbitmq-cert.pem"
+      - "{{ kolla_certificates_dir }}/{{ inventory_hostname }}-cert.pem"
+      - "{{ kolla_certificates_dir }}/outward_rabbitmq-cert.pem"
+  fail:
+    msg: No TLS certificate provided for outward RabbitMQ.
+  when:
+    - enable_outward_rabbitmq | bool
+    - rabbitmq_enable_tls | bool
+    - cert | length == 0
+
+- name: Check if TLS key exists for outward RabbitMQ
+  vars:
+    key: "{{ query('first_found', paths, errors='ignore') }}"
+    paths:
+      - "{{ kolla_certificates_dir }}/{{ inventory_hostname }}/outward_rabbitmq-key.pem"
+      - "{{ kolla_certificates_dir }}/{{ inventory_hostname }}-key.pem"
+      - "{{ kolla_certificates_dir }}/outward_rabbitmq-key.pem"
+  fail:
+    msg: No TLS key provided for outward RabbitMQ.
+  when:
+    - enable_outward_rabbitmq | bool
+    - rabbitmq_enable_tls | bool
+    - key | length == 0
diff --git a/ansible/roles/rabbitmq/templates/rabbitmq.conf.j2 b/ansible/roles/rabbitmq/templates/rabbitmq.conf.j2
index f897499dac2ec345824f9dfe49eac988add76956..25ec6b46f354e2f117435739e65b367d4cb3a22e 100644
--- a/ansible/roles/rabbitmq/templates/rabbitmq.conf.j2
+++ b/ansible/roles/rabbitmq/templates/rabbitmq.conf.j2
@@ -1,6 +1,11 @@
 # NOTE(yoctozepto): rabbitmq uses the raw format (e.g. fd::) of IPv6 address;
 # despite specifying port via colon, the url format (e.g. [fd::]) is not accepted
+{% if rabbitmq_enable_tls | bool %}
+listeners.tcp = none
+listeners.ssl.1 = {{ api_interface_address }}:{{ role_rabbitmq_port }}
+{% else %}
 listeners.tcp.1 = {{ api_interface_address }}:{{ role_rabbitmq_port }}
+{% endif %}
 {# NOTE: to avoid split-brain #}
 cluster_partition_handling = pause_minority
 
@@ -12,3 +17,12 @@ cluster_formation.peer_discovery_backend = rabbit_peer_discovery_classic_config
 {% for host in groups[role_rabbitmq_groups] %}
 cluster_formation.classic_config.nodes.{{ loop.index0 }} = rabbit@{{ hostvars[host]['ansible_hostname'] }}
 {% endfor %}
+
+{% if rabbitmq_enable_tls | bool %}
+# https://www.rabbitmq.com/ssl.html
+ssl_options.certfile = /etc/rabbitmq/certs/{{ project_name }}-cert.pem
+ssl_options.keyfile = /etc/rabbitmq/certs/{{ project_name }}-key.pem
+{% for key, value in rabbitmq_tls_options.items() %}
+ssl_options.{{ key }} = {{ value }}
+{% endfor %}
+{% endif %}
diff --git a/ansible/roles/rabbitmq/templates/rabbitmq.json.j2 b/ansible/roles/rabbitmq/templates/rabbitmq.json.j2
index 91e67e15b44b8903c6fbbac722880b96f3f0cee2..d93df3ad18b5402cac1d22db4dc797fa34fae30f 100644
--- a/ansible/roles/rabbitmq/templates/rabbitmq.json.j2
+++ b/ansible/roles/rabbitmq/templates/rabbitmq.json.j2
@@ -24,7 +24,19 @@
             "dest": "/etc/rabbitmq/definitions.json",
             "owner": "rabbitmq",
             "perm": "0600"
-        }
+        }{% if rabbitmq_enable_tls | bool %},
+        {
+            "source": "{{ container_config_directory }}/{{ project_name }}-cert.pem",
+            "dest": "/etc/rabbitmq/certs/{{ project_name }}-cert.pem",
+            "owner": "rabbitmq",
+            "perm": "0600"
+        },
+        {
+            "source": "{{ container_config_directory }}/{{ project_name }}-key.pem",
+            "dest": "/etc/rabbitmq/certs/{{ project_name }}-key.pem",
+            "owner": "rabbitmq",
+            "perm": "0600"
+        }{% endif %}
     ],
     "permissions": [
         {
diff --git a/ansible/roles/sahara/templates/sahara.conf.j2 b/ansible/roles/sahara/templates/sahara.conf.j2
index 9b4c15661fcd61307b30f09e4c5c87c24ce52524..c982e375e8ea7a4c583dda29d70b5618719cb0a7 100644
--- a/ansible/roles/sahara/templates/sahara.conf.j2
+++ b/ansible/roles/sahara/templates/sahara.conf.j2
@@ -38,6 +38,12 @@ topics = {{ sahara_enabled_notification_topics | map(attribute='name') | join(',
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 {% if sahara_policy_file is defined %}
 [oslo_policy]
 policy_file = {{ sahara_policy_file }}
diff --git a/ansible/roles/searchlight/templates/searchlight.conf.j2 b/ansible/roles/searchlight/templates/searchlight.conf.j2
index e1ea68cfed0c1e0af1c0ce5a009c942ff2838fef..1c51fbcc619fa1e5831d98ad657f3232d9e69ed3 100644
--- a/ansible/roles/searchlight/templates/searchlight.conf.j2
+++ b/ansible/roles/searchlight/templates/searchlight.conf.j2
@@ -44,6 +44,11 @@ topics = {{ searchlight_enabled_notification_topics | map(attribute='name') | jo
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
 
 {% if searchlight_policy_file is defined %}
 [oslo_policy]
diff --git a/ansible/roles/senlin/templates/senlin.conf.j2 b/ansible/roles/senlin/templates/senlin.conf.j2
index 2764bf07d31934a4165d1fdf0287a9436b52a3c0..5879a8f2225da459d981424612deee28f42154e5 100644
--- a/ansible/roles/senlin/templates/senlin.conf.j2
+++ b/ansible/roles/senlin/templates/senlin.conf.j2
@@ -66,6 +66,12 @@ topics = {{ senlin_enabled_notification_topics | map(attribute='name') | join(',
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 {% if senlin_policy_file is defined %}
 [oslo_policy]
 policy_file = {{ senlin_policy_file }}
diff --git a/ansible/roles/solum/templates/solum.conf.j2 b/ansible/roles/solum/templates/solum.conf.j2
index 02d46ecc9e684c3d80dbdd7f43df9c3055b5190b..e4d3c4647ad92263692c670e0212627c94babca3 100644
--- a/ansible/roles/solum/templates/solum.conf.j2
+++ b/ansible/roles/solum/templates/solum.conf.j2
@@ -61,3 +61,9 @@ memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_addres
 
 [oslo_messaging_notifications]
 transport_url = {{ notify_transport_url }}
+
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
diff --git a/ansible/roles/tacker/templates/tacker.conf.j2 b/ansible/roles/tacker/templates/tacker.conf.j2
index c76b83bd201009c8d4484d151413df508a467867..4f8928755512cb1e58252ce2b107d0ed53fab52f 100644
--- a/ansible/roles/tacker/templates/tacker.conf.j2
+++ b/ansible/roles/tacker/templates/tacker.conf.j2
@@ -66,6 +66,12 @@ topics = {{ tacker_enabled_notification_topics | map(attribute='name') | join(',
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 {% if tacker_policy_file is defined %}
 [oslo_policy]
 policy_file = {{ tacker_policy_file }}
diff --git a/ansible/roles/trove/templates/trove-conductor.conf.j2 b/ansible/roles/trove/templates/trove-conductor.conf.j2
index 42247a3b03d112ab23fb4d4447339eeac435d99c..6aaf468563ca38ef1204d194b1c39d308718132f 100644
--- a/ansible/roles/trove/templates/trove-conductor.conf.j2
+++ b/ansible/roles/trove/templates/trove-conductor.conf.j2
@@ -25,6 +25,12 @@ topics = {{ trove_enabled_notification_topics | map(attribute='name') | join(','
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 [database]
 connection = mysql+pymysql://{{ trove_database_user }}:{{ trove_database_password }}@{{ trove_database_address }}/{{ trove_database_name }}
 connection_recycle_time = {{ database_connection_recycle_time }}
diff --git a/ansible/roles/trove/templates/trove-taskmanager.conf.j2 b/ansible/roles/trove/templates/trove-taskmanager.conf.j2
index 9c4d4ac49824eb957ec6fb88e0876663073b6908..c578bbf8ec6433d1f9df4bd7a0d2d01353b4b6e1 100644
--- a/ansible/roles/trove/templates/trove-taskmanager.conf.j2
+++ b/ansible/roles/trove/templates/trove-taskmanager.conf.j2
@@ -48,6 +48,11 @@ topics = {{ trove_enabled_notification_topics | map(attribute='name') | join(','
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
 
 {% if enable_osprofiler | bool %}
 [profiler]
diff --git a/ansible/roles/trove/templates/trove.conf.j2 b/ansible/roles/trove/templates/trove.conf.j2
index 3bb9199aa6b357661fa33eb02d1a7e81c9553809..e642ed2687ce1df5461fa64846e1542d6ad8614f 100644
--- a/ansible/roles/trove/templates/trove.conf.j2
+++ b/ansible/roles/trove/templates/trove.conf.j2
@@ -55,6 +55,12 @@ topics = {{ trove_enabled_notification_topics | map(attribute='name') | join(','
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 {% if enable_osprofiler | bool %}
 [profiler]
 enabled = true
diff --git a/ansible/roles/vitrage/templates/vitrage.conf.j2 b/ansible/roles/vitrage/templates/vitrage.conf.j2
index 12cf804956574433f788719ba97840d4db479ce5..e007c03cd0c6f4880b9114fe0bde8eb7be99477d 100644
--- a/ansible/roles/vitrage/templates/vitrage.conf.j2
+++ b/ansible/roles/vitrage/templates/vitrage.conf.j2
@@ -72,6 +72,12 @@ topics = {{ vitrage_enabled_notification_topics | map(attribute='name') | join('
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 [oslo_concurrency]
 lock_path = /var/lib/vitrage/tmp
 
diff --git a/ansible/roles/watcher/templates/watcher.conf.j2 b/ansible/roles/watcher/templates/watcher.conf.j2
index 33178edd47ac567042809c5d5df21e2d4cdf8aa7..3f655da1dc4694d95007ab0a85cd92979ab63fc0 100644
--- a/ansible/roles/watcher/templates/watcher.conf.j2
+++ b/ansible/roles/watcher/templates/watcher.conf.j2
@@ -57,6 +57,12 @@ topics = {{ watcher_enabled_notification_topics | map(attribute='name') | join('
 driver = noop
 {% endif %}
 
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
+
 {% if watcher_policy_file is defined %}
 [oslo_policy]
 policy_file = {{ watcher_policy_file }}
diff --git a/ansible/roles/zun/templates/zun.conf.j2 b/ansible/roles/zun/templates/zun.conf.j2
index a3358c51c59148b55e6783cf45d7b53fe6dfae5b..4600a48229236476e9cf5f3886d24e246877887d 100644
--- a/ansible/roles/zun/templates/zun.conf.j2
+++ b/ansible/roles/zun/templates/zun.conf.j2
@@ -127,3 +127,9 @@ docker_remote_api_port = 2375
 
 [cni_daemon]
 cni_daemon_port = {{ zun_cni_daemon_port }}
+
+{% if om_enable_rabbitmq_tls | bool %}
+[oslo_messaging_rabbit]
+ssl = true
+ssl_ca_file = {{ om_rabbitmq_cacert }}
+{% endif %}
diff --git a/doc/source/admin/advanced-configuration.rst b/doc/source/admin/advanced-configuration.rst
index d228c833453ca33dca11be8663f7b557fabc449d..82f28654a39ac004631003b49c97356179f51615 100644
--- a/doc/source/admin/advanced-configuration.rst
+++ b/doc/source/admin/advanced-configuration.rst
@@ -69,6 +69,8 @@ RabbitMQ doesn't work with IP address, hence the IP address of
 ``api_interface`` should be resolvable by hostnames to make sure that
 all RabbitMQ Cluster hosts can resolve each others hostname beforehand.
 
+.. _tls-configuration:
+
 TLS Configuration
 ~~~~~~~~~~~~~~~~~
 
diff --git a/doc/source/conf.py b/doc/source/conf.py
index f10c47e994903df60a06a45ce58d9dae7cc13e38..b7b15c4b7d7d16b561df7380d2315b1b8518dc36 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -98,6 +98,7 @@ openstack_projects = [
     'neutron',
     'nova',
     'octavia',
+    'oslo.messaging',
     'oslotest',
     'swift',
 ]
diff --git a/doc/source/reference/message-queues/rabbitmq.rst b/doc/source/reference/message-queues/rabbitmq.rst
index 1457327ce046d918f128401895b678c203f66196..916df309c819fd83b49e679055879221895f8f10 100644
--- a/doc/source/reference/message-queues/rabbitmq.rst
+++ b/doc/source/reference/message-queues/rabbitmq.rst
@@ -8,6 +8,81 @@ RabbitMQ is a message broker written in Erlang.
 It is currently the default provider of message queues in Kolla Ansible
 deployments.
 
+TLS encryption
+~~~~~~~~~~~~~~
+
+There are a number of channels to consider when securing RabbitMQ
+communication. Kolla Ansible currently supports TLS encryption of the
+following:
+
+* client-server traffic, typically between OpenStack services using the
+  :oslo.messaging-doc:`oslo.messaging </>` library and RabbitMQ
+* RabbitMQ Management API and UI (frontend connection to HAProxy only)
+
+Encryption of the following channels is not currently supported:
+
+* RabbitMQ cluster traffic between RabbitMQ server nodes
+* RabbitMQ CLI communication with RabbitMQ server nodes
+* RabbitMQ Management API and UI (backend connection from HAProxy to RabbitMQ)
+
+Client-server
+-------------
+
+Encryption of client-server traffic is enabled by setting
+``rabbitmq_enable_tls`` to ``true``. Additionally, certificates and keys must
+be available in the following paths (in priority order):
+
+Certificates:
+
+* ``"{{ kolla_certificates_dir }}/{{ inventory_hostname }}/rabbitmq-cert.pem"``
+* ``"{{ kolla_certificates_dir }}/{{ inventory_hostname }}-cert.pem"``
+* ``"{{ kolla_certificates_dir }}/rabbitmq-cert.pem"``
+
+Keys:
+
+* ``"{{ kolla_certificates_dir }}/{{ inventory_hostname }}/rabbitmq-key.pem"``
+* ``"{{ kolla_certificates_dir }}/{{ inventory_hostname }}-key.pem"``
+* ``"{{ kolla_certificates_dir }}/rabbitmq-key.pem"``
+
+The default for ``kolla_certificates_dir`` is ``/etc/kolla/certificates``.
+
+The certificates must be valid for the IP address of the host running RabbitMQ
+on the API network.
+
+Additional TLS configuration options may be passed to RabbitMQ via
+``rabbitmq_tls_options``. This should be a dict, and the keys will be prefixed
+with ``ssl_options.``. For example:
+
+.. code-block:: yaml
+
+   rabbitmq_tls_options:
+     ciphers.1: ECDHE-ECDSA-AES256-GCM-SHA384
+     ciphers.2: ECDHE-RSA-AES256-GCM-SHA384
+     ciphers.3: ECDHE-ECDSA-AES256-SHA384
+     honor_cipher_order: true
+     honor_ecc_order: true
+
+Details on configuration of RabbitMQ for TLS can be found in the `RabbitMQ
+documentation <https://www.rabbitmq.com/ssl.html>`__.
+
+When ``om_rabbitmq_enable_tls`` is ``true`` (it defaults to the value of
+``rabbitmq_enable_tls``), applicable OpenStack services will be configured to
+use oslo.messaging with TLS enabled. The CA certificate is configured via
+``om_rabbitmq_cacert`` (it defaults to ``rabbitmq_cacert``, which points to the
+system's trusted CA certificate bundle for TLS). Note that there is currently
+no support for using client certificates.
+
+For testing purposes, Kolla Ansible provides the ``kolla-ansible certificates``
+command, which will generate self-signed certificates for RabbitMQ if
+``rabbitmq_enable_tls`` is ``true``.
+
+Management API and UI
+---------------------
+
+The management API and UI are accessed via HAProxy, exposed only on the
+internal VIP. As such, traffic to this endpoint is encrypted when
+``kolla_enable_tls_internal`` is ``true``. See :ref:`tls-configuration`.
+
 Passing arguments to RabbitMQ server's Erlang VM
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/etc/kolla/globals.yml b/etc/kolla/globals.yml
index 34c2ae69eb709112f8265be1fa2006e703ff6115..f1b5336b3e0db50a6c3f234aa37210e269e0af8d 100644
--- a/etc/kolla/globals.yml
+++ b/etc/kolla/globals.yml
@@ -84,6 +84,10 @@
 #om_rpc_port: "{{ qdrouterd_port }}"
 #om_rpc_group: "qdrouterd"
 
+# Whether to enable TLS for oslo.messaging communication with RabbitMQ.
+#om_enable_rabbitmq_tls: "{{ rabbitmq_enable_tls | bool }}"
+# CA certificate bundle in containers using oslo.messaging with RabbitMQ TLS.
+#om_rabbitmq_cacert: "{{ rabbitmq_cacert }}"
 
 ##############################
 # Neutron - Networking Options
@@ -373,6 +377,10 @@
 # These are appended to args already provided by Kolla Ansible
 # to configure IPv6 in RabbitMQ server.
 #rabbitmq_server_additional_erl_args: ""
+# Whether to enable TLS encryption for RabbitMQ client-server communication.
+#rabbitmq_enable_tls: "no"
+# CA certificate bundle in RabbitMQ container.
+#rabbitmq_cacert: "/etc/ssl/certs/{{ 'ca-certificates.crt' if kolla_base_distro in ['debian', 'ubuntu'] else 'ca-bundle.trust.crt' }}"
 
 #################
 # MariaDB options
diff --git a/releasenotes/notes/rabbitmq-tls-78ea3fddf67267f2.yaml b/releasenotes/notes/rabbitmq-tls-78ea3fddf67267f2.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..f8bced3bac35994a295c5f5f2d08f1424f20fb5b
--- /dev/null
+++ b/releasenotes/notes/rabbitmq-tls-78ea3fddf67267f2.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    Adds support for TLS encryption of RabbitMQ client-server communication.
+    See `blueprint
+    <https://blueprints.launchpad.net/kolla-ansible/+spec/message-queue-ssl-support>`__
+    for details.
diff --git a/tests/templates/globals-default.j2 b/tests/templates/globals-default.j2
index 2740ab1ef5ae6287f2e74aa91cb7e3733a55fbf1..bf4364d913bac9c73fcce118a51a65d4cf59883d 100644
--- a/tests/templates/globals-default.j2
+++ b/tests/templates/globals-default.j2
@@ -134,6 +134,7 @@ openstack_cacert: "/etc/ssl/certs/ca-certificates.crt"
 openstack_cacert: "/etc/pki/tls/certs/ca-bundle.crt"
 {% endif %}
 kolla_admin_openrc_cacert: "{% raw %}{{ kolla_certificates_dir }}{% endraw %}/ca/root.crt"
+rabbitmq_enable_tls: "yes"
 {% endif %}
 
 {% if scenario == 'linuxbridge' %}