TLS support for RabbitMQ

Change-Id: I02fffa2480c887eab6dcdb22aad5b4f0deb07139
diff --git a/README.rst b/README.rst
index 5aeb715..6a33d56 100644
--- a/README.rst
+++ b/README.rst
@@ -94,9 +94,69 @@
           password: 'password'
           policies:
           - name: HA
-            pattern: '^(?!amq\.).*' 
+            pattern: '^(?!amq\.).*'
             definition: '{"ha-mode": "all"}'
 
+
+
+Enable TLS support
+------------------
+
+The certs and private key passing:
+
+.. code-block:: yaml
+
+   rabbitmq:
+      server:
+        enabled: true
+        ...
+        ssl:
+          enabled: True
+
+          cacert_chain: |
+          -----BEGIN CERTIFICATE-----
+                    ...
+          -----END CERTIFICATE-------
+
+          key: |
+          -----BEGIN RSA PRIVATE KEY-----
+                    ...
+          -----END RSA PRIVATE KEY-------
+
+          cert: |
+          -----BEGIN CERTIFICATE-----
+                    ...
+          -----END CERTIFICATE-------
+
+
+Also you can pass them via specifing a name of ca authority at salt master:
+
+.. code-block:: yaml
+
+   rabbitmq:
+      server:
+        enabled: true
+        ...
+        ssl:
+          enabled: True
+          authority: CA_Authority_Name
+
+In this case keys and certs will be pulled from:
+
+`salt://pki/{{ authority }}/certs/{ rabbitmq.{cert|key} | ca.cert }`
+
+--
+
+Defaut port for TLS is **5671**:
+
+.. code-block:: yaml
+
+  rabbitmq:
+    server:
+      bind:
+        ssl:
+         port: 5671
+
 Usage
 =====
 
@@ -105,9 +165,9 @@
 .. code-block:: yaml
 
     > rabbitmqctl cluster_status
-    
+
     Cluster status of node 'rabbit@ctl-1' ...
-    [{nodes,[{disc,['rabbit@ctl-1','rabbit@ctl-2','rabbit@ctl-3']}]}, 
+    [{nodes,[{disc,['rabbit@ctl-1','rabbit@ctl-2','rabbit@ctl-3']}]},
      {running_nodes,['rabbit@ctl-3','rabbit@ctl-2','rabbit@ctl-1']},
      {partitions,[]}]
     ...done.
diff --git a/rabbitmq/files/rabbitmq.config b/rabbitmq/files/rabbitmq.config
index 2d567f1..8f693f0 100644
--- a/rabbitmq/files/rabbitmq.config
+++ b/rabbitmq/files/rabbitmq.config
@@ -19,39 +19,40 @@
               {cluster_nodes, {[{% for node in cluster.members %}'rabbit@{{ node.name }}'{% if not loop.last %}, {% endif %}{% endfor %}], {{ cluster.mode }}}},
               {%- endif %}
               {loopback_users, []},
-              {tcp_listeners, [{"{{ server.bind.address }}",{{ server.bind.port }}}]}
-              {%- if server.get('ssl', {}).get('enabled', False) %},
+              {tcp_listeners, [{"{{ server.bind.address }}",{{ server.bind.port }}}]},
+              {%- if server.get('ssl', {}).get('enabled', False) %}
               {ssl_listeners, [{"{{ server.bind.get('ssl', {}).get('address', server.bind.address) }}",{{ server.bind.get('ssl', {}).get('port', 5671) }}}]},
-              {ssl_options, [{cacertfile,"{{ server.ssl.get('ca_file', '/etc/rabbitmq/ssl/cacert.pem') }}"},
-                             {certfile,"{{ server.ssl.get('cert_file', '/etc/rabbitmq/ssl/cert.pem') }}"},
-                             {keyfile,"{{ server.ssl.get('key_file', '/etc/rabbitmq/ssl/key.pem') }}"},
+              {ssl_options, [{cacertfile,"{{ server.ssl.ca_file }}"},
+                             {certfile,"{{ server.ssl.cert_file }}"},
+                             {keyfile,"{{ server.ssl.key_file }}"},
                              {verify,verify_{{ server.ssl.get('verify', 'peer') }}},
-                             {versions, [{% if server.ssl.versions is defined %}{% for version in server.ssl.versions %}'{{ version }}'{% if not loop.last %},{% endif %}{% endfor %}{% else %}'tlsv1.2', 'tlsv1.1'{% endif %}]},
+                             {versions, ['{{ server.ssl.versions | join("', '") }}']},
                              {%- if server.ssl.ciphers is defined %}
-                             {ciphers,[{% for ciph in server.ssl.ciphers %}{ {{ ciph }} }{% if not loop.last %},{% endif %}{% endfor %}]},
+                             {ciphers,[ {{ server.ssl.ciphers | join(',') }} ]},
                              {%- endif %}
-                             {fail_if_no_peer_cert,false}]
-              }
+                             {fail_if_no_peer_cert,false}]}
+
               {% endif %}
+
              ]
     }
     {%- if 'rabbitmq_management' in server.plugins %},
     {rabbitmq_management,
               [{listener, [{port, {{ server.management.bind.port }} },
                            {ip, "{{ server.management.bind.address }}" }
+
                            {%- if server.management.get('ssl', {}).get('enabled', False) %},
                            {ssl,true},
-                           {ssl_opts, [{cacertfile,"{{ server.management.ssl.get('ca_file', '/etc/rabbitmq/ssl/cacert.pem') }}"},
-                                       {certfile,"{{ server.management.ssl.get('cert_file', '/etc/rabbitmq/ssl/cert.pem') }}"},
-                                       {keyfile,"{{ server.management.ssl.get('key_file', '/etc/rabbitmq/ssl/key.pem') }}"},
-                                       {%- if server.ssl.ciphers is defined %}
-                                       {ciphers,[{% for ciph in server.ssl.ciphers %}{ {{ ciph }} }{% if not loop.last %},{% endif %}{% endfor %}]},
-                                       {%- endif %}
-                                       {versions, [{% if server.ssl.versions is defined %}{% for version in server.ssl.versions %}'{{ version }}'{% if not loop.last %},{% endif %}{% endfor %}{% else %}'tlsv1.2', 'tlsv1.1'{% endif %}]}]}
-                           {%- endif %}
-                          ]
-              }]
-    }
+                           {ssl_opts, [{cacertfile,"{{ server.ssl.ca_file }}"},
+                                          {certfile,"{{ server.ssl.cert_file }}"},
+                                          {keyfile,"{{ server.ssl.key_file }}"},
+                                          {verify,verify_{{ server.ssl.get('verify', 'peer') }}},
+                                          {versions,[ "{{ server.ssl.versions | join('", "') }}" ]},
+                                          {%- if server.ssl.ciphers is defined %}
+                                          {ciphers,[ {{ server.ssl.ciphers | join(',') }} ]},
+                                          {%- endif %}]}
+                         {%- endif %}
+                         ]}]}
     {%- endif %}
 ].
 {#-
diff --git a/rabbitmq/files/ssl/all_file.pem.j2 b/rabbitmq/files/ssl/all_file.pem.j2
new file mode 100644
index 0000000..004f7cc
--- /dev/null
+++ b/rabbitmq/files/ssl/all_file.pem.j2
@@ -0,0 +1,2 @@
+{% include ssl_key_file %}
+{% include ssl_crt_file %}
diff --git a/rabbitmq/files/ssl/ssl_env.conf.j2 b/rabbitmq/files/ssl/ssl_env.conf.j2
new file mode 100644
index 0000000..2475655
--- /dev/null
+++ b/rabbitmq/files/ssl/ssl_env.conf.j2
@@ -0,0 +1,7 @@
+# Obtaining of an Erlang ssl library path
+export HOME="/var/lib/rabbitmq/"
+ERL_SSL_PATH=`erl -eval 'io:format("~p", [code:lib_dir(ssl, ebin)]),halt().' -noshell`
+
+# Add SSL-related environment vars for rabbitmq-server and rabbitmqctl
+SERVER_ADDITIONAL_ERL_ARGS="-pa $ERL_SSL_PATH -proto_dist inet_tls -ssl_dist_opt server_certfile {{ all_file }} -ssl_dist_opt server_secure_renegotiate true client_secure_renegotiate true server_cacertfile {{ ca_file }}"
+CTL_ERL_ARGS="$SERVER_ADDITIONAL_ERL_ARGS"
diff --git a/rabbitmq/map.jinja b/rabbitmq/map.jinja
index 35586ca..1da966d 100644
--- a/rabbitmq/map.jinja
+++ b/rabbitmq/map.jinja
@@ -1,9 +1,15 @@
-
 {% set server = salt['grains.filter_by']({
-    'Arch': {
-        'pkgs': ['rabbitmq'],
-        'service': 'rabbitmq',
+    'default': {
+        'ssl': {
+            'enabled': False,
+            'versions': ['tlsv1.2','tlsv1.1','tlsv1'],
+            'ca_file': '/etc/rabbitmq/ssl/ca.pem',
+            'cert_file': '/etc/rabbitmq/ssl/cert.pem',
+            'key_file': '/etc/rabbitmq/ssl/key.pem',
+            'all_file': '/etc/rabbitmq/ssl/all_file.pem'
+        },
         'config_file': '/etc/rabbitmq/rabbitmq.config',
+        'env_file': '/etc/rabbitmq/rabbitmq-env.conf',
         'cookie_file': '/var/lib/rabbitmq/.erlang.cookie',
         'ulimit': 8192,
         'disk_free_limit': 50000000,
@@ -19,59 +25,27 @@
             'bind': {
                 'address': '127.0.0.1',
                 'port': '15672'
-            },
-        },
+            }
+        }
+    },
+    'Arch': {
+        'pkgs': ['rabbitmq'],
+        'service': 'rabbitmq'
     },
     'Debian': {
         'pkgs': ['rabbitmq-server', 'gettext-base'],
         'service': 'rabbitmq-server',
-        'config_file': '/etc/rabbitmq/rabbitmq.config',
-        'cookie_file': '/var/lib/rabbitmq/.erlang.cookie',
-        'default_file': '/etc/default/rabbitmq-server',
-        'ulimit': 8192,
-        'disk_free_limit': 50000000,
-        'bind': {
-            'address': '0.0.0.0',
-            'port': '5672',
-            'ssl': {
-                'address': '0.0.0.0',
-                'port': '5671'
-            },
-        },
-        'management': {
-            'bind': {
-                'address': '127.0.0.1',
-                'port': '15672'
-            },
-        },
+        'default_file': '/etc/default/rabbitmq-server'
     },
     'RedHat': {
         'pkgs': ['rabbitmq-server'],
-        'service': 'rabbitmq-server',
-        'config_file': '/etc/rabbitmq/rabbitmq.config',
-        'cookie_file': '/var/lib/rabbitmq/.erlang.cookie',
-        'ulimit': 8192,
-        'disk_free_limit': 50000000,
-        'bind': {
-            'address': '0.0.0.0',
-            'port': '5672',
-            'ssl': {
-                'address': '0.0.0.0',
-                'port': '5671'
-            },
-        },
-        'management': {
-            'bind': {
-                'address': '127.0.0.1',
-                'port': '15672'
-            },
-        },
+        'service': 'rabbitmq-server'
     },
-}, merge=pillar.rabbitmq.get('server', {})) %}
+}, merge=pillar.rabbitmq.get('server', {}), base='default') %}
 
 {% set cluster = pillar.rabbitmq.get('cluster', {}) %}
 
 {%- set rabbitmq_users = {} %}
 {%- for host_name, host in server.get('host', {}).iteritems() %}
 {%- do rabbitmq_users.update({host.user: [host]}) %}
-{%- endfor %}
+{%- endfor %}
\ No newline at end of file
diff --git a/rabbitmq/server/init.sls b/rabbitmq/server/init.sls
index 20c5691..e6731bf 100644
--- a/rabbitmq/server/init.sls
+++ b/rabbitmq/server/init.sls
@@ -1,5 +1,7 @@
 include:
 - rabbitmq.server.service
+- rabbitmq.server.ssl
+
 {%- if not grains.get('noservices', False) %}
 - rabbitmq.server.plugin
 - rabbitmq.server.vhost
diff --git a/rabbitmq/server/service.sls b/rabbitmq/server/service.sls
index 15cdb79..9fff053 100644
--- a/rabbitmq/server/service.sls
+++ b/rabbitmq/server/service.sls
@@ -107,6 +107,13 @@
   - name: {{ server.service }}
   - watch:
     - file: rabbitmq_config
+      {% if server.ssl.enabled %}
+    - file: rabbitmq_cacertificate
+    - file: rabbitmq_certificate
+    - file: rabbitmq_server_key
+    - file: rabbitmq_ssl_all_file
+    - file: rabbitmq_ssl_env
+      {%- endif %}
 {%- endif %}
 
 {%- if grains.get('virtual_subtype', None) == "Docker" %}
diff --git a/rabbitmq/server/ssl.sls b/rabbitmq/server/ssl.sls
new file mode 100644
index 0000000..b4f5567
--- /dev/null
+++ b/rabbitmq/server/ssl.sls
@@ -0,0 +1,74 @@
+{%- from "rabbitmq/map.jinja" import server with context %}
+
+{%- if server.ssl.enabled %}
+
+rabbitmq_cacertificate:
+  file.managed:
+    - name: {{ server.ssl.ca_file }}
+    {%- if server.ssl.cacert_chain is defined %}
+    - contents_pillar: rabbitmq:server:ssl:cacert_chain
+    {%- else %}
+    - source: salt://pki/{{ server.ssl.authority }}/certs/ca.cert
+    {%- endif %}
+    - user: root
+    - group: rabbitmq
+    - mode: 640
+    - makedirs: true
+
+rabbitmq_certificate:
+  file.managed:
+    - name: {{ server.ssl.cert_file }}
+    {%- if server.ssl.cert is defined %}
+    - contents_pillar: rabbitmq:server:ssl:cert
+    {%- else %}
+    - source: salt://pki/{{ server.ssl.authority }}/certs/rabbitmq.cert
+    {%- endif %}
+    - user: root
+    - group: rabbitmq
+    - mode: 640
+    - makedirs: true
+
+rabbitmq_server_key:
+  file.managed:
+    - name: {{ server.ssl.key_file }}
+    {%- if server.ssl.key is defined %}
+    - contents_pillar: rabbitmq:server:ssl:key
+    {%- else %}
+    - source: salt://pki/{{ server.ssl.authority }}/certs/rabbitmq.key
+    {%- endif %}
+    - user: root
+    - group: rabbitmq
+    - mode: 640
+    - makedirs: true
+
+# consist of private key and cert
+rabbitmq_ssl_all_file:
+  file.managed:
+    - name: {{ server.ssl.all_file }}
+    - source: salt://rabbitmq/files/ssl/all_file.pem.j2
+    - template: jinja
+    - user: root
+    - group: rabbitmq
+    - mode: 640
+    - makedirs: true
+    - context:
+        ssl_key_file: {{ server.ssl.key_file }}
+        ssl_crt_file: {{ server.ssl.cert_file }}
+    - watch:
+      - file: rabbitmq_server_key
+      - file: rabbitmq_certificate
+
+rabbitmq_ssl_env:
+  file.managed:
+    - name: {{ server.env_file }}
+    - source: salt://rabbitmq/files/ssl/ssl_env.conf.j2
+    - template: jinja
+    - user: root
+    - group: rabbitmq
+    - mode: 640
+    - makedirs: true
+    - context:
+       all_file: {{ server.ssl.all_file }}
+       ca_file: {{ server.ssl.ca_file }}
+
+{%- endif %}