RabbitMQ TLS support
OSCORE-387
Change-Id: I93ead9105820fe7462b7bd9b76d51f89ce5950c6
Releases: Mitaka, Newton, Ocata
Usage: see README.rst
diff --git a/README.rst b/README.rst
index 8c882e6..a2f4dbe 100644
--- a/README.rst
+++ b/README.rst
@@ -117,7 +117,7 @@
revision: master
-Heat system definition of several stacks/systems
+Heat system definition of several stacks/systems
.. code-block:: yaml
@@ -176,6 +176,46 @@
virtual_host: '/openstack'
....
+Client-side RabbitMQ TLS configuration:
+
+|
+
+To enable TLS for oslo.messaging you need to provide the CA certificate.
+
+By default system-wide CA certs are used. Nothing should be specified except `ssl.enabled`.
+
+.. code-block:: yaml
+
+ ....
+ message_queue:
+ ssl:
+ enabled: True
+
+Use `cacert_file` param to specify the CA-cert file location explicitly:
+
+.. code-block:: yaml
+
+ ....
+ message_queue:
+ ssl:
+ enabled: True
+ cacert_file: /etc/ssl/rabbitmq-ca.pem
+
+To manage content of the `cacert_file` use the `cacert` param:
+
+.. code-block:: yaml
+
+ ....
+ message_queue:
+ ssl:
+ enabled: True
+ cacert: { file content here }
+ cacert_file: /etc/openstack/rabbitmq-ca.pem
+
+Notice:
+ * The `message_queue.port` is set to **5671** (AMQPS) by default if `ssl.enabled=True`.
+ * Use `message_queue.ssl.version` if you need to specify protocol version. By default is TLSv1 for python < 2.7.9 and TLSv1_2 for version above.
+
Documentation and Bugs
======================
diff --git a/heat/files/mitaka/heat.conf.Debian b/heat/files/mitaka/heat.conf.Debian
index bf1ed85..3ddf3c3 100644
--- a/heat/files/mitaka/heat.conf.Debian
+++ b/heat/files/mitaka/heat.conf.Debian
@@ -1,4 +1,4 @@
-{%- from "heat/map.jinja" import server with context %}
+{%- from "heat/map.jinja" import server, system_cacerts_file with context %}
[DEFAULT]
#
@@ -624,16 +624,31 @@
# notification. (floating point value)
# Deprecated group/name - [DEFAULT]/kombu_reconnect_delay
#kombu_reconnect_delay = 1.0
+
+{%- set rabbit_port = server.message_queue.get('port', 5671 if server.message_queue.get('ssl',{}).get('enabled', False) else 5672) %}
{%- if server.message_queue.members is defined %}
rabbit_hosts = {% for member in server.message_queue.members -%}
- {{ member.host }}:{{ member.get('port', 5672) }}
+ {{ member.host }}:{{ member.get('port', rabbit_port) }}
{%- if not loop.last -%},{%- endif -%}
{%- endfor -%}
{%- else %}
rabbit_host = {{ server.message_queue.host }}
-rabbit_port = {{ server.message_queue.port }}
+rabbit_port = {{ rabbit_port }}
{%- endif %}
+{%- if server.message_queue.get('ssl',{}).get('enabled', False) %}
+rabbit_use_ssl=true
+{%- if server.message_queue.ssl.version is defined %}
+kombu_ssl_version = {{ server.message_queue.ssl.version }}
+{%- elif salt['grains.get']('pythonversion') > [2,7,8] %}
+kombu_ssl_version = TLSv1_2
+{%- endif %}
+{%- if server.message_queue.ssl.cacert_file is defined %}
+kombu_ssl_ca_certs = {{ server.message_queue.ssl.cacert_file }}
+{%- else %}
+kombu_ssl_ca_certs={{ system_cacerts_file }}
+{%- endif %}
+{%- endif %}
# Connect over SSL for RabbitMQ. (boolean value)
@@ -819,4 +834,4 @@
#allow_headers = Content-MD5,X-Image-Meta-Checksum,X-Storage-Token,Accept-Encoding,X-Auth-Token,X-Identity-Status,X-Roles,X-Service-Catalog,X-User-Id,X-Tenant-Id,X-OpenStack-Request-ID
{%- if server.cors.allow_headers is defined %}
allow_headers = {{ server.cors.allow_headers }}
-{%- endif %}
\ No newline at end of file
+{%- endif %}
diff --git a/heat/files/newton/heat.conf.Debian b/heat/files/newton/heat.conf.Debian
index 266c516..43fbf7c 100644
--- a/heat/files/newton/heat.conf.Debian
+++ b/heat/files/newton/heat.conf.Debian
@@ -1,4 +1,4 @@
-{%- from "heat/map.jinja" import server with context %}
+{%- from "heat/map.jinja" import server, system_cacerts_file with context %}
[DEFAULT]
#
@@ -198,16 +198,16 @@
# The messaging driver to use, defaults to rabbit. Other drivers include qpid
# and zmq. (string value)
#rpc_backend = rabbit
-rpc_backend = rabbit
+{%- set rabbit_port = server.message_queue.get('port', 5671 if server.message_queue.get('ssl',{}).get('enabled', False) else 5672) %}
{%- if server.message_queue.members is defined %}
transport_url = rabbit://{% for member in server.message_queue.members -%}
- {{ server.message_queue.user }}:{{ server.message_queue.password }}@{{ member.host }}:{{ member.get('port', 5672) }}
+ {{ server.message_queue.user }}:{{ server.message_queue.password }}@{{ member.host }}:{{ member.get('port', rabbit_port) }}
{%- if not loop.last -%},{%- endif -%}
{%- endfor -%}
/{{ server.message_queue.virtual_host }}
{%- else %}
-transport_url = rabbit://{{ server.message_queue.user }}:{{ server.message_queue.password }}@{{ server.message_queue.host }}:{{ server.message_queue.port }}/{{ server.message_queue.virtual_host }}
+transport_url = rabbit://{{ server.message_queue.user }}:{{ server.message_queue.password }}@{{ server.message_queue.host }}:{{ rabbit_port }}/{{ server.message_queue.virtual_host }}
{%- endif %}
# The default exchange under which topics are scoped. May be overridden by an
# exchange name specified in the transport_url option. (string value)
@@ -610,6 +610,20 @@
# From oslo.messaging
#
+{%- if server.message_queue.get('ssl',{}).get('enabled', False) %}
+rabbit_use_ssl=true
+{%- if server.message_queue.ssl.version is defined %}
+kombu_ssl_version = {{ server.message_queue.ssl.version }}
+{%- elif salt['grains.get']('pythonversion') > [2,7,8] %}
+kombu_ssl_version = TLSv1_2
+{%- endif %}
+{%- if server.message_queue.ssl.cacert_file is defined %}
+kombu_ssl_ca_certs = {{ server.message_queue.ssl.cacert_file }}
+{%- else %}
+kombu_ssl_ca_certs={{ system_cacerts_file }}
+{%- endif %}
+{%- endif %}
+
# Use durable queues in AMQP. (boolean value)
# Deprecated group/name - [DEFAULT]/rabbit_durable_queues
#amqp_durable_queues = false
@@ -817,4 +831,4 @@
#allow_headers = Content-MD5,X-Image-Meta-Checksum,X-Storage-Token,Accept-Encoding,X-Auth-Token,X-Identity-Status,X-Roles,X-Service-Catalog,X-User-Id,X-Tenant-Id,X-OpenStack-Request-ID
{%- if server.cors.allow_headers is defined %}
allow_headers = {{ server.cors.allow_headers }}
-{%- endif %}
\ No newline at end of file
+{%- endif %}
diff --git a/heat/files/ocata/heat.conf.Debian b/heat/files/ocata/heat.conf.Debian
index b2b382b..edef658 100644
--- a/heat/files/ocata/heat.conf.Debian
+++ b/heat/files/ocata/heat.conf.Debian
@@ -1,4 +1,4 @@
-{%- from "heat/map.jinja" import server with context %}
+{%- from "heat/map.jinja" import server, system_cacerts_file with context %}
[DEFAULT]
#
@@ -575,16 +575,16 @@
# A URL representing the messaging driver to use and its full configuration.
# (string value)
#transport_url = <None>
-rpc_backend = rabbit
+{%- set rabbit_port = server.message_queue.get('port', 5671 if server.message_queue.get('ssl',{}).get('enabled', False) else 5672) %}
{%- if server.message_queue.members is defined %}
transport_url = rabbit://{% for member in server.message_queue.members -%}
- {{ server.message_queue.user }}:{{ server.message_queue.password }}@{{ member.host }}:{{ member.get('port', 5672) }}
+ {{ server.message_queue.user }}:{{ server.message_queue.password }}@{{ member.host }}:{{ member.get('port', rabbit_port) }}
{%- if not loop.last -%},{%- endif -%}
{%- endfor -%}
/{{ server.message_queue.virtual_host }}
{%- else %}
-transport_url = rabbit://{{ server.message_queue.user }}:{{ server.message_queue.password }}@{{ server.message_queue.host }}:{{ server.message_queue.port }}/{{ server.message_queue.virtual_host }}
+transport_url = rabbit://{{ server.message_queue.user }}:{{ server.message_queue.password }}@{{ server.message_queue.host }}:{{ rabbit_port }}/{{ server.message_queue.virtual_host }}
{%- endif %}
# DEPRECATED: The messaging driver to use, defaults to rabbit. Other drivers
# include amqp and zmq. (string value)
@@ -2093,6 +2093,20 @@
# From oslo.messaging
#
+{%- if server.message_queue.get('ssl',{}).get('enabled', False) %}
+rabbit_use_ssl=true
+{%- if server.message_queue.ssl.version is defined %}
+kombu_ssl_version = {{ server.message_queue.ssl.version }}
+{%- elif salt['grains.get']('pythonversion') > [2,7,8] %}
+kombu_ssl_version = TLSv1_2
+{%- endif %}
+{%- if server.message_queue.ssl.cacert_file is defined %}
+kombu_ssl_ca_certs = {{ server.message_queue.ssl.cacert_file }}
+{%- else %}
+kombu_ssl_ca_certs={{ system_cacerts_file }}
+{%- endif %}
+{%- endif %}
+
# Use durable queues in AMQP. (boolean value)
# Deprecated group/name - [DEFAULT]/amqp_durable_queues
# Deprecated group/name - [DEFAULT]/rabbit_durable_queues
diff --git a/heat/map.jinja b/heat/map.jinja
index 01ef0f6..b17f7d5 100644
--- a/heat/map.jinja
+++ b/heat/map.jinja
@@ -1,3 +1,7 @@
+{%- set system_cacerts_file = salt['grains.filter_by']({
+ 'Debian': '/etc/ssl/certs/ca-certificates.crt',
+ 'RedHat': '/etc/pki/tls/certs/ca-bundle.crt'
+})%}
{% set server = salt['grains.filter_by']({
'Debian': {
diff --git a/heat/server.sls b/heat/server.sls
index b821e82..2f9ebb1 100644
--- a/heat/server.sls
+++ b/heat/server.sls
@@ -1,4 +1,4 @@
-{%- from "heat/map.jinja" import server with context %}
+{%- from "heat/map.jinja" import server, system_cacerts_file with context %}
{%- if server.enabled %}
heat_server_packages:
@@ -129,5 +129,22 @@
- watch:
- file: /etc/heat/heat.conf
- file: /etc/heat/api-paste.ini
+ {%- if server.message_queue.get('ssl',{}).get('enabled', False) %}
+ - file: rabbitmq_ca
+ {%- endif %}
+
+{%- if server.message_queue.get('ssl',{}).get('enabled', False) %}
+rabbitmq_ca:
+{%- if server.message_queue.ssl.cacert is defined %}
+ file.managed:
+ - name: {{ server.message_queue.ssl.cacert_file }}
+ - contents_pillar: heat:server:message_queue:ssl:cacert
+ - mode: 0444
+ - makedirs: true
+{%- else %}
+ file.exists:
+ - name: {{ server.message_queue.ssl.get('cacert_file', system_cacerts_file) }}
+{%- endif %}
+{%- endif %}
{%- endif %}
diff --git a/tests/pillar/ssl.sls b/tests/pillar/ssl.sls
new file mode 100644
index 0000000..f11f67c
--- /dev/null
+++ b/tests/pillar/ssl.sls
@@ -0,0 +1,12 @@
+# Test of enabling SSL for the following communication paths:
+# - messaging (rabbitmq)
+
+include:
+ - .server_cluster
+
+heat:
+ server:
+ message_queue:
+ port: 5671
+ ssl:
+ enabled: True