Autentificación para un broker Mosquitto

Ricardo Vega

por Ricardo Vega el 26 de octubre, 2016

Etiquetas:

CiberseguridadM2MIoTProtocoloPython

6 minutos.

Las anteriores semanas nos entretuvimos bastante en dos posts detallando como el protocolo MQTT nos podía ayudar (y mucho) en la tarea de comunicar nuestros sensores y actuadores en general (con Arduino en particular) con un elemento central que haga las veces de servidor.

Este servidor puede ser un ordenador, un servidor coorporativo, un hosting, una solución "on cloud" o un mini-ordenador como las Raspberry Pi. Los procesos de instalación que tenemos que llevar en ellos son muy semejantes ya que seguramente todos trabajen con un sistema operativo Linux.

En este post, voy a centrarme en una Raspberry Pi aunque los pasos serán exactamente iguales en cualquier distribución Debian y tendrá modificaciones ligeras en otros entornos.

MQTT Mosquitto Seguridad

Llevas ya tres parrafos leídos y aún no te he dicho de qué va a ir exactamente esta entrada. Nuestro objetivo para hoy será aprender a configurar un broker MQTT con autentificación para securizar un poco el acceso al mismo de forma que podamos exponerlo en un servidor público y aún así tengamos zonas privadas.

Concretamente, y como ya es habitual en este blog, emplearemos Mosquitto como broker. Dicho sistema deberá estar ya instalado en nuestra Raspberry Pi tal y como hemos detallado en otros posts.

Sin embargo, por si aún no lo tienes instalado en tu Raspberry Pi. Estas son los pasos que debes seguir:

1git clone https://github.com/eclipse/mosquitto.git
2cd mosquitto
3sudo apt-get install build-essential
4sudo apt-get install libc-ares-dev
5sudo apt-get install uuid-dev
6make binary
7make install

Como ves, estamos descargando Mosquitto directamente desde el repositorio y compilándolo por nostros mismos lo que nos asegura la instalación de la última versión disponible.

Una vez conseguido esto, añadiremos la funcionalidad de la autentificación a través de un plugin llamado mosquitto-auth-plug. Veamos como configurarlo.

En primer lugar clonamos el repositorio GIT desde Github:

1git clone https://github.com/jpmens/mosquitto-auth-plug.git
2cd mosquitto-auth-plug

Si te entretienes un rato en leer el README del proyecto (cosa que siempre te recomiendo hagas) puede que te llame la atención como habla de la posibilidad de trabajar con distintos backends:

  • MySQL
  • PostgreSQL
  • CDB
  • SQLite3
  • Redis
  • TLS PSK
  • LDAP
  • HTTP
  • JWT
  • MongoDB

Estas distintas opciones responden a la necesidad de usar un backend que almacene los usuarios con permisos en el sistema, es decir, una especie de almacén que guarde usuario/contraseña.

Aunque creo que no es la mejor opción, vamos a emplear la opción que con nuestros conocimientos actuales de APIs creo que mejor se adapta a la mayoría de lectores de este blog: APIs HTTP que podemos escribir nosotros mismos con NodeJS o Python como hemos visto anteriormente.

Necesitaremos renombrar el fichero ´config.mk.in´ como ´config.mk´ y editarlo como se muestra a continuación:

1# Select your backends from this list
2BACKEND_CDB ?= no
3BACKEND_MYSQL ?= no
4BACKEND_SQLITE ?= no
5BACKEND_REDIS ?= no
6BACKEND_POSTGRES ?= no
7BACKEND_LDAP ?= no
8BACKEND_HTTP ?= yes
9BACKEND_MONGO ?= no
10
11# Specify the path to the Mosquitto sources here
12MOSQUITTO_SRC = /home/USER/mqtt/mosquitto
13
14# Specify the path the OpenSSL here
15OPENSSLDIR = /usr/bin

Además, para instalar este plugin, necesitaremos instalar un serie de dependencias:

1sudo apt-get install openssl
2sudo apt-get install libssl-dev
3sudo apt-get install libcurl4-openssl-dev

Ahora podemos construir mosquitto-auth-plugin ejecutando el siguiente comando.

1sudo make

Esta operación creará un fichero llamado auth-plug.so en el mismo directorio. Deberemos copiar dicho fichero a la raíz de Mosquitto:

1cp auth-plug.so ../mosquitto
2cd ../mosquitto

Ahora editaremos la configuración de Mosquitto editando el fichero mosquitto.conf. Concretamente editaremos la sección "Security". Añadiremos lo siguiente:

1# =================================================================
2# Security
3# =================================================================
4
5# If set, only clients that have a matching prefix on their
6# clientid will be allowed to connect to the broker. By default,
7# all clients may connect.
8# For example, setting "secure-" here would mean a client "secure-
9# client" could connect but another with clientid "mqtt" couldn't.
10#clientid_prefixes
11
12# Boolean value that determines whether clients that connect
13# without providing a username are allowed to connect. If set to
14# false then a password file should be created (see the
15# password_file option) to control authenticated client access.
16# Defaults to true.
17#allow_anonymous true
18
19# In addition to the clientid_prefixes, allow_anonymous and TLS
20# authentication options, username based authentication is also
21# possible. The default support is described in "Default
22# authentication and topic access control" below. The auth_plugin
23# allows another authentication method to be used.
24# Specify the path to the loadable plugin and see the
25# "Authentication and topic access plugin options" section below.
26#auth_plugin
27
28auth_plugin /home/USER/mosquitto/auth-plug.so
29auth_opt_backends http
30auth_opt_http_ip 127.0.0.1
31auth_opt_http_port 80
32#auth_opt_http_hostname example.org
33auth_opt_http_getuser_uri /mosquitto/authplugin/auth
34auth_opt_http_superuser_uri /mosquitto/authplugin/superuser
35auth_opt_http_aclcheck_uri /mosquitto/authplugin/acl

Como ves, definimos las URLS de nuestra API REST que servirá para indicar si la autentificación es o no correcta. Mosquitto enviará peticiones a dichos endpoints comprobando si un determinado usuario tiene o no acceso a un determinado topic.

La definición de esta API es bastante sencilla, pudiendo crearla por ejemplo con Python.

1from flask import Flask, Response, request
2
3app = Flask(__name__)
4
5@app.route('/auth', methods=['POST'])
6def auth():
7 response = Response(content_type='text/plain', status=403)
8 if all((w in request.args for w in ['username', 'password'])):
9 username = request.args['username']
10 password = request.args['password']
11 if username == 'ricardo.vega@ricveal.com' and password == '13579':
12 response.status_code = 200
13 return response
14
15@app.route('/superuser', methods=['POST'])
16def superuser():
17 response = Response(content_type='text/plain', status=403)
18 if all((w in request.args for w in ['username', 'password'])):
19 username = request.args['username']
20 password = request.args['password']
21 if username == 'ricardo.vega@ricveal.com' and password == '13579':
22 response.status_code = 200
23 return response
24
25
26@app.route('/acl', methods=['POST'])
27def acl():
28 response = Response(content_type='text/plain', status=403)
29 if all((w in request.args for w in ['username', 'topic'])):
30 username = request.args['username']
31 topic = request.args['topic']
32 if username == 'ricardo.vega@ricveal.com' and topic == 't/1':
33 response.status_code = 200
34 return response
35
36if __name__ == '__main__':
37 app.run()

Por favor, el código mostrado arriba es altamente inseguro pero te dará una API funcionando rápidamente. Si quieres usar este método de forma correcta, deberías acceder a una base de datos o semejante para obtener los valores que se esperan.

No escribas directamente en tu código usuarios, contraseñas, etc.

Ya tenemos todos los elementos que necesitamos para probar la seguridad. Para ejecutar mosquitto con esta configuración, tan sólo tendremos que que ejecutar:

1mosquitto -c /home/USER/mosquitto/mosquitto.conf

A partir de este momento, podrás hacer un uso normal con publicadores y subscriptores pero deberemos pasar en esta ocasión los datos de usuario y contraseña:

1mosquitto_pub -t "test" -m "hola_mundo" -u "ricardo.vega@ricveal.com" -p "13579"
2mosquitto_sub -t "test" -u "ricardo.vega@ricveal.com" -p "13579"

Y en el caso de estar empleando Arduino y el cliente Arduino Client for MQTT deberemos añadir el username y el password en la conexión:

1client.connect("Arduino Client", "ricardo.vega@ricveal.com", "13579")

Como ves, no es mucho más complicado tener un broker securizado. Gracias a esta opción, podríamos abrirlo a Internet y marcar como privados (con seguridad) determinados "topics" (o todos) para información que no queramos compartir con nadie.

Esto nos permite, por ejemplo, poner un cliente Arduino con MQTT en nuestra casa del pueblo y conectarnos al broker que tenemos configurado en nuestra casa en la ciudad de forma segura, pudiendo por ejemplo montar un sistema de riego remoto manejado desde nuestra casa.

Como te dije en el anterior post, las posibilidades que se nos abren son casi infinitas y ya no tenemos escusas para no dejar volar nuestra imaginación.

Espero que te haya gustado este post. La próxima semana abandonaremos un poco MQTT para hablar de otros temas que estoy seguro también te interesan.

Como te digo siempre, puedes utilizar Twitter para ponerte en contacto conmigo así como emplear los comentarios para plantear tus dudas sobre esta entrada.

¡Un saludo!

Discutir en Twitter

Compartir artículo
Ricardo Vega
Ricardo Vega es un desarrollador "full-stack" al que le gusta "cacharrear con todo" pero está especializado sobre todo en tecnologías Javascript, principalmente en React. Intenta devolver a Internet lo que Internet le ha dado.

Sigue leyendo 😀


Apoya al blog y mi actividad


Si te ha gustado este artículo, valora apoyarme económicamente a través de Patreon o comprándome un café. Cualquier pequeño donativo, significa mucho y ayuda a la continuidad del blog.

Puedes consultar otras opciones adicionales e información adicional en /donate

Permanezcamos en contacto!


¿Quieres enterarte de todas las novedades del sector? ¿Te gustaría trabajar conmigo? ¡Puedes contactar conmigo de forma muy sencilla!

@ricveal

ricardo.vega@ricveal.com

Ricardo Vega
Copyright © 2021 | Política de Privacidad