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
This commit is contained in:
Tim Rupp 2019-06-25 06:27:32 -07:00 committed by Martin Krizek
parent ac101f7f33
commit 48af9bdfec
9 changed files with 270 additions and 15 deletions

View file

@ -87,9 +87,16 @@ options:
authentication. Support for this feature is broker dependent. authentication. Support for this feature is broker dependent.
version_added: 2.3 version_added: 2.3
aliases: [ keyfile ] aliases: [ keyfile ]
tls_version:
description:
# informational: requirements for nodes - 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 ] requirements: [ mosquitto ]
notes: notes:
- This module requires a connection to an MQTT broker such as Mosquitto - This module requires a connection to an MQTT broker such as Mosquitto
@ -112,7 +119,10 @@ EXAMPLES = '''
# #
import os import os
import ssl
import traceback import traceback
import platform
from distutils.version import LooseVersion
HAS_PAHOMQTT = True HAS_PAHOMQTT = True
PAHOMQTT_IMP_ERR = None PAHOMQTT_IMP_ERR = None
@ -132,6 +142,10 @@ from ansible.module_utils._text import to_native
# #
def main(): def main():
tls_map = {
'tlsv1.2': ssl.PROTOCOL_TLSv1_2,
'tlsv1.1': ssl.PROTOCOL_TLSv1_1,
}
module = AnsibleModule( module = AnsibleModule(
argument_spec=dict( argument_spec=dict(
@ -147,6 +161,7 @@ def main():
ca_cert=dict(default=None, type='path', aliases=['ca_certs']), ca_cert=dict(default=None, type='path', aliases=['ca_certs']),
client_cert=dict(default=None, type='path', aliases=['certfile']), client_cert=dict(default=None, type='path', aliases=['certfile']),
client_key=dict(default=None, type='path', aliases=['keyfile']), client_key=dict(default=None, type='path', aliases=['keyfile']),
tls_version=dict(default=None, choices=['tlsv1.1', 'tlsv1.2'])
), ),
supports_check_mode=True supports_check_mode=True
) )
@ -166,6 +181,7 @@ def main():
ca_certs = module.params.get("ca_cert", None) ca_certs = module.params.get("ca_cert", None)
certfile = module.params.get("client_cert", None) certfile = module.params.get("client_cert", None)
keyfile = module.params.get("client_key", None) keyfile = module.params.get("client_key", None)
tls_version = module.params.get("tls_version", None)
if client_id is None: if client_id is None:
client_id = "%s_%s" % (socket.getfqdn(), os.getpid()) client_id = "%s_%s" % (socket.getfqdn(), os.getpid())
@ -179,21 +195,42 @@ def main():
tls = None tls = None
if ca_certs is not None: if ca_certs is not None:
tls = {'ca_certs': ca_certs, 'certfile': certfile, if tls_version:
'keyfile': keyfile} 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: try:
mqtt.single(topic, payload, mqtt.single(
topic,
payload,
qos=qos, qos=qos,
retain=retain, retain=retain,
client_id=client_id, client_id=client_id,
hostname=server, hostname=server,
port=port, port=port,
auth=auth, auth=auth,
tls=tls) tls=tls
)
except Exception as e: except Exception as e:
module.fail_json(msg="unable to publish to MQTT broker %s" % to_native(e), module.fail_json(
exception=traceback.format_exc()) msg="unable to publish to MQTT broker %s" % to_native(e),
exception=traceback.format_exc()
)
module.exit_json(changed=False, topic=topic) module.exit_json(changed=False, topic=topic)

View file

@ -0,0 +1,5 @@
notification/mqtt
shippable/posix/group1
skip/osx
skip/freebsd
skip/rhel

View file

@ -0,0 +1,2 @@
dependencies:
- setup_mosquitto

View file

@ -0,0 +1,4 @@
- include: ubuntu.yml
when:
- ansible_distribution == 'Ubuntu'
- ansible_distribution_release != 'trusty'

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,3 @@
---
dependencies:
- setup_tls

View file

@ -0,0 +1,3 @@
---
- include: ubuntu.yml
when: ansible_distribution == 'Ubuntu'

View file

@ -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