From 48af9bdfec638962fe2a6750a2949dd4c6fe267a Mon Sep 17 00:00:00 2001 From: Tim Rupp Date: Tue, 25 Jun 2019 06:27:32 -0700 Subject: [PATCH] Adds tls_version argument to mqtt module (#58264) Fixes: #22034 This patch adds support for a tls_version parameter that allows the TLS version used to be configurable. By default the module will let the underlying system libraries pick the maximum supported version. This parameter is useful for servers that are unable to support newer versions of TLS --- lib/ansible/modules/notification/mqtt.py | 67 +++++++-- test/integration/targets/mqtt/aliases | 5 + test/integration/targets/mqtt/meta/main.yml | 2 + test/integration/targets/mqtt/tasks/main.yml | 4 + .../integration/targets/mqtt/tasks/ubuntu.yml | 142 ++++++++++++++++++ .../setup_mosquitto/files/mosquitto.conf | 35 +++++ .../targets/setup_mosquitto/meta/main.yml | 3 + .../targets/setup_mosquitto/tasks/main.yml | 3 + .../targets/setup_mosquitto/tasks/ubuntu.yml | 24 +++ 9 files changed, 270 insertions(+), 15 deletions(-) create mode 100644 test/integration/targets/mqtt/aliases create mode 100644 test/integration/targets/mqtt/meta/main.yml create mode 100644 test/integration/targets/mqtt/tasks/main.yml create mode 100644 test/integration/targets/mqtt/tasks/ubuntu.yml create mode 100644 test/integration/targets/setup_mosquitto/files/mosquitto.conf create mode 100644 test/integration/targets/setup_mosquitto/meta/main.yml create mode 100644 test/integration/targets/setup_mosquitto/tasks/main.yml create mode 100644 test/integration/targets/setup_mosquitto/tasks/ubuntu.yml diff --git a/lib/ansible/modules/notification/mqtt.py b/lib/ansible/modules/notification/mqtt.py index 57fb74199f3..6961be385e0 100644 --- a/lib/ansible/modules/notification/mqtt.py +++ b/lib/ansible/modules/notification/mqtt.py @@ -87,9 +87,16 @@ options: authentication. Support for this feature is broker dependent. version_added: 2.3 aliases: [ keyfile ] - - -# informational: requirements for nodes + tls_version: + description: + - Specifies the version of the SSL/TLS protocol to be used. + - By default (if the python version supports it) the highest TLS version is + detected. If unavailable, TLS v1 is used. + type: str + choices: + - tlsv1.1 + - tlsv1.2 + version_added: 2.9 requirements: [ mosquitto ] notes: - This module requires a connection to an MQTT broker such as Mosquitto @@ -112,7 +119,10 @@ EXAMPLES = ''' # import os +import ssl import traceback +import platform +from distutils.version import LooseVersion HAS_PAHOMQTT = True PAHOMQTT_IMP_ERR = None @@ -132,6 +142,10 @@ from ansible.module_utils._text import to_native # def main(): + tls_map = { + 'tlsv1.2': ssl.PROTOCOL_TLSv1_2, + 'tlsv1.1': ssl.PROTOCOL_TLSv1_1, + } module = AnsibleModule( argument_spec=dict( @@ -147,6 +161,7 @@ def main(): ca_cert=dict(default=None, type='path', aliases=['ca_certs']), client_cert=dict(default=None, type='path', aliases=['certfile']), client_key=dict(default=None, type='path', aliases=['keyfile']), + tls_version=dict(default=None, choices=['tlsv1.1', 'tlsv1.2']) ), supports_check_mode=True ) @@ -166,6 +181,7 @@ def main(): ca_certs = module.params.get("ca_cert", None) certfile = module.params.get("client_cert", None) keyfile = module.params.get("client_key", None) + tls_version = module.params.get("tls_version", None) if client_id is None: client_id = "%s_%s" % (socket.getfqdn(), os.getpid()) @@ -179,21 +195,42 @@ def main(): tls = None if ca_certs is not None: - tls = {'ca_certs': ca_certs, 'certfile': certfile, - 'keyfile': keyfile} + if tls_version: + tls_version = tls_map.get(tls_version, ssl.PROTOCOL_SSLv23) + else: + if LooseVersion(platform.python_version()) <= "3.5.2": + # Specifying `None` on later versions of python seems sufficient to + # instruct python to autonegotiate the SSL/TLS connection. On versions + # 3.5.2 and lower though we need to specify the version. + # + # Note that this is an alias for PROTOCOL_TLS, but PROTOCOL_TLS was + # not available until 3.5.3. + tls_version = ssl.PROTOCOL_SSLv23 + + tls = { + 'ca_certs': ca_certs, + 'certfile': certfile, + 'keyfile': keyfile, + 'tls_version': tls_version, + } try: - mqtt.single(topic, payload, - qos=qos, - retain=retain, - client_id=client_id, - hostname=server, - port=port, - auth=auth, - tls=tls) + mqtt.single( + topic, + payload, + qos=qos, + retain=retain, + client_id=client_id, + hostname=server, + port=port, + auth=auth, + tls=tls + ) except Exception as e: - module.fail_json(msg="unable to publish to MQTT broker %s" % to_native(e), - exception=traceback.format_exc()) + module.fail_json( + msg="unable to publish to MQTT broker %s" % to_native(e), + exception=traceback.format_exc() + ) module.exit_json(changed=False, topic=topic) diff --git a/test/integration/targets/mqtt/aliases b/test/integration/targets/mqtt/aliases new file mode 100644 index 00000000000..6d6f14e59d8 --- /dev/null +++ b/test/integration/targets/mqtt/aliases @@ -0,0 +1,5 @@ +notification/mqtt +shippable/posix/group1 +skip/osx +skip/freebsd +skip/rhel diff --git a/test/integration/targets/mqtt/meta/main.yml b/test/integration/targets/mqtt/meta/main.yml new file mode 100644 index 00000000000..86f3d043639 --- /dev/null +++ b/test/integration/targets/mqtt/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_mosquitto diff --git a/test/integration/targets/mqtt/tasks/main.yml b/test/integration/targets/mqtt/tasks/main.yml new file mode 100644 index 00000000000..37442abfeca --- /dev/null +++ b/test/integration/targets/mqtt/tasks/main.yml @@ -0,0 +1,4 @@ +- include: ubuntu.yml + when: + - ansible_distribution == 'Ubuntu' + - ansible_distribution_release != 'trusty' diff --git a/test/integration/targets/mqtt/tasks/ubuntu.yml b/test/integration/targets/mqtt/tasks/ubuntu.yml new file mode 100644 index 00000000000..71ff3e90bca --- /dev/null +++ b/test/integration/targets/mqtt/tasks/ubuntu.yml @@ -0,0 +1,142 @@ +- name: Install pip packages + pip: + name: paho-mqtt>=1.4.0 + state: present + +- name: MQTT non-TLS endpoint + mqtt: + topic: /node/s/bar/blurb + payload: foo + qos: 1 + client_id: me001 + register: result + +- assert: + that: + - result is success + +- name: Send a test message to TLS1.1 endpoint, no client version specified + mqtt: + topic: /node/s/bar/blurb + payload: foo-tls + qos: 1 + client_id: me001 + ca_certs: /tls/ca_certificate.pem + certfile: /tls/client_certificate.pem + keyfile: /tls/client_key.pem + port: 8883 + register: result + +- assert: + that: + - result is success + +- name: Send a test message to TLS1.2 endpoint, no client version specified + mqtt: + topic: /node/s/bar/blurb + payload: foo-tls + qos: 1 + client_id: me001 + ca_certs: /tls/ca_certificate.pem + certfile: /tls/client_certificate.pem + keyfile: /tls/client_key.pem + port: 8884 + register: result + +- assert: + that: + - result is success + +# TODO(Uncomment when TLS1.3 is supported in moquitto and ubuntu version) +# +# - name: Send a test message to TLS1.3 endpoint +# mqtt: +# topic: /node/s/bar/blurb +# payload: foo-tls +# qos: 1 +# client_id: me001 +# ca_certs: /tls/ca_certificate.pem +# certfile: /tls/client_certificate.pem +# keyfile: /tls/client_key.pem +# port: 8885 +# register: result + +#- assert: +# that: +# - result is success + +- name: Send a message, client TLS1.1, server (required) TLS1.2 - Expected failure + mqtt: + topic: /node/s/bar/blurb + payload: foo-tls + qos: 1 + client_id: me001 + ca_certs: /tls/ca_certificate.pem + certfile: /tls/client_certificate.pem + keyfile: /tls/client_key.pem + tls_version: tlsv1.1 + port: 8884 + register: result + failed_when: result is success + +- assert: + that: + - result is success + +# TODO(Uncomment when TLS1.3 is supported in moquitto and ubuntu version) +# +# - name: Send a message, client TLS1.1, server (required) TLS1.3 - Expected failure +# mqtt: +# topic: /node/s/bar/blurb +# payload: foo-tls +# qos: 1 +# client_id: me001 +# ca_certs: /tls/ca_certificate.pem +# certfile: /tls/client_certificate.pem +# keyfile: /tls/client_key.pem +# tls_version: tlsv1.1 +# port: 8885 +# register: result +# failed_when: result is success + +# - assert: +# that: +# - result is success + +- name: Send a message, client TLS1.2, server (required) TLS1.1 - Expected failure + mqtt: + topic: /node/s/bar/blurb + payload: foo-tls + qos: 1 + client_id: me001 + ca_certs: /tls/ca_certificate.pem + certfile: /tls/client_certificate.pem + keyfile: /tls/client_key.pem + tls_version: tlsv1.2 + port: 8883 + register: result + failed_when: result is success + +- assert: + that: + - result is success + +# TODO(Uncomment when TLS1.3 is supported in moquitto and ubuntu version) +# +# - name: Send a message, client TLS1.2, server (required) TLS1.3 - Expected failure +# mqtt: +# topic: /node/s/bar/blurb +# payload: foo-tls +# qos: 1 +# client_id: me001 +# ca_certs: /tls/ca_certificate.pem +# certfile: /tls/client_certificate.pem +# keyfile: /tls/client_key.pem +# tls_version: tlsv1.2 +# port: 8885 +# register: result +# failed_when: result is success + +# - assert: +# that: +# - result is success diff --git a/test/integration/targets/setup_mosquitto/files/mosquitto.conf b/test/integration/targets/setup_mosquitto/files/mosquitto.conf new file mode 100644 index 00000000000..84a80b71c3b --- /dev/null +++ b/test/integration/targets/setup_mosquitto/files/mosquitto.conf @@ -0,0 +1,35 @@ +# Plain MQTT protocol +listener 1883 + +# MQTT over TLS 1.1 +listener 8883 +tls_version tlsv1.1 +cafile /tls/ca_certificate.pem +certfile /tls/server_certificate.pem +keyfile /tls/server_key.pem + +# MQTT over TLS 1.2 +listener 8884 +tls_version tlsv1.2 +cafile /tls/ca_certificate.pem +certfile /tls/server_certificate.pem +keyfile /tls/server_key.pem + +# TODO(This does not appear to be supported on Ubuntu 18.04. Re-try on 20.04 or next LTS release) +# MQTT over TLS 1.3 +# +# listener 8885 +# tls_version tlsv1.3 +# cafile /tls/ca_certificate.pem +# certfile /tls/server_certificate.pem +# keyfile /tls/server_key.pem + +log_dest syslog + +log_type error +log_type warning +log_type notice +log_type information +log_type debug + +connection_messages true diff --git a/test/integration/targets/setup_mosquitto/meta/main.yml b/test/integration/targets/setup_mosquitto/meta/main.yml new file mode 100644 index 00000000000..af05db79d41 --- /dev/null +++ b/test/integration/targets/setup_mosquitto/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - setup_tls diff --git a/test/integration/targets/setup_mosquitto/tasks/main.yml b/test/integration/targets/setup_mosquitto/tasks/main.yml new file mode 100644 index 00000000000..4f35f16f623 --- /dev/null +++ b/test/integration/targets/setup_mosquitto/tasks/main.yml @@ -0,0 +1,3 @@ +--- +- include: ubuntu.yml + when: ansible_distribution == 'Ubuntu' diff --git a/test/integration/targets/setup_mosquitto/tasks/ubuntu.yml b/test/integration/targets/setup_mosquitto/tasks/ubuntu.yml new file mode 100644 index 00000000000..5675cb89235 --- /dev/null +++ b/test/integration/targets/setup_mosquitto/tasks/ubuntu.yml @@ -0,0 +1,24 @@ +- name: Install https transport for apt + apt: + name: apt-transport-https + state: latest + force: yes + +- name: Install Mosquitto Server + apt: + name: mosquitto + state: latest + register: result + until: result is success + delay: 3 + retries: 10 + +- name: Ensure TLS config + copy: + src: mosquitto.conf + dest: /etc/mosquitto/mosquitto.conf + +- name: Start Mosquitto service + service: + name: mosquitto + state: restarted