diff --git a/changelogs/fragments/rabbitmq_publish-certificate-checks.yml b/changelogs/fragments/rabbitmq_publish-certificate-checks.yml new file mode 100644 index 0000000000..00fa49da9a --- /dev/null +++ b/changelogs/fragments/rabbitmq_publish-certificate-checks.yml @@ -0,0 +1,2 @@ +minor_changes: + - rabbitmq_publish - Support for connecting with SSL certificates. diff --git a/lib/ansible/module_utils/rabbitmq.py b/lib/ansible/module_utils/rabbitmq.py index aabb8a235b..cf76400644 100644 --- a/lib/ansible/module_utils/rabbitmq.py +++ b/lib/ansible/module_utils/rabbitmq.py @@ -10,9 +10,11 @@ __metaclass__ = type from ansible.module_utils._text import to_native from ansible.module_utils.basic import missing_required_lib +from ansible.module_utils.six.moves.urllib import parse as urllib_parse from mimetypes import MimeTypes import os +import json import traceback PIKA_IMP_ERR = None @@ -56,10 +58,16 @@ class RabbitClient(): self.vhost = self.params['vhost'] self.queue = self.params['queue'] self.headers = self.params['headers'] + self.cafile = self.params['cafile'] + self.certfile = self.params['certfile'] + self.keyfile = self.params['keyfile'] if self.host is not None: self.build_url() + if self.cafile is not None: + self.append_ssl_certs() + self.connect_to_rabbitmq() def check_required_library(self): @@ -75,6 +83,17 @@ class RabbitClient(): if self.params['url'] is None and any(self.params[k] is None for k in ['proto', 'host', 'port', 'password', 'username', 'vhost']): self.module.fail_json(msg="Connection parameters must be passed via url, or, proto, host, port, vhost, username or password.") + def append_ssl_certs(self): + ssl_options = {} + if self.cafile: + ssl_options['cafile'] = self.cafile + if self.certfile: + ssl_options['certfile'] = self.certfile + if self.keyfile: + ssl_options['keyfile'] = self.keyfile + + self.url = self.url + '?ssl_options=' + urllib_parse.quote(json.dumps(ssl_options)) + @staticmethod def rabbitmq_argument_spec(): return dict( diff --git a/lib/ansible/modules/notification/rabbitmq_publish.py b/lib/ansible/modules/notification/rabbitmq_publish.py index 5a4aaa6605..41a77029df 100644 --- a/lib/ansible/modules/notification/rabbitmq_publish.py +++ b/lib/ansible/modules/notification/rabbitmq_publish.py @@ -88,13 +88,32 @@ options: - A dictionary of headers to post with the message. default: {} type: dict + cafile: + description: + - CA file used during connection to the RabbitMQ server over SSL. + - If this option is specified, also I(certfile) and I(keyfile) must be specified. + version_added: '2.10' + certfile: + description: + - Client certificate to establish SSL connection. + - If this option is specified, also I(cafile) and I(keyfile) must be specified. + version_added: '2.10' + keyfile: + description: + - Client key to establish SSL connection. + - If this option is specified, also I(cafile) and I(certfile) must be specified. + version_added: '2.10' + requirements: [ pika ] notes: - This module requires the pika python library U(https://pika.readthedocs.io/). - Pika is a pure-Python implementation of the AMQP 0-9-1 protocol that tries to stay fairly independent of the underlying network support library. - - This plugin is tested against RabbitMQ. Other AMQP 0.9.1 protocol based servers may work but not tested/guaranteed. + - This module is tested against RabbitMQ. Other AMQP 0.9.1 protocol based servers may work but not tested/guaranteed. + - The certificate authentication was tested with certificates created + via U(https://www.rabbitmq.com/ssl.html#automated-certificate-generation) and RabbitMQ + configuration variables C(ssl_options.verify = verify_peer) & C(ssl_options.fail_if_no_peer_cert = true). author: "John Imison (@Im0)" ''' @@ -120,6 +139,17 @@ EXAMPLES = ''' url: "amqp://guest:guest@192.168.0.32:5672/%2F" body: "Hello world random queue from ansible module rabbitmq_publish" content_type: "text/plain" + +- name: Publish with certs + rabbitmq_publish: + url: "amqps://guest:guest@192.168.0.32:5671/%2F" + body: "Hello test queue from ansible module rabbitmq_publish via SSL certs" + queue: 'test' + content_type: "text/plain" + cafile: 'ca_certificate.pem' + certfile: 'client_certificate.pem' + keyfile: 'client_key.pem' + ''' RETURN = ''' @@ -155,11 +185,15 @@ def main(): durable=dict(default=False, type='bool'), exclusive=dict(default=False, type='bool'), auto_delete=dict(default=False, type='bool'), - headers=dict(default={}, type='dict') + headers=dict(default={}, type='dict'), + cafile=dict(type='str', required=False), + certfile=dict(type='str', required=False), + keyfile=dict(type='str', required=False), ) module = AnsibleModule( argument_spec=argument_spec, mutually_exclusive=[['body', 'src']], + required_together=[['cafile', 'certfile', 'keyfile']], supports_check_mode=False )