EMQX Setup with Server/Client certification
This article will show you how to enable self-signed server/client side certification with EMQX by openssl
and provide client sample code in python
.
Basic MQTT broker and client
-
Run EMQX in docker container
$ docker pull emqx/emqx:v3.1.0 $ docker run -d --name emqx31 -p 1883:1883 -p 8083:8083 -p 8883:8883 -p 8084:8084 -p 18083:18083 emqx/emqx:v3.1.0 # Go to the EQMX dashboard: # http://localhost:18083/#/ # with default credential: # admin/password
-
Sample MQTT client in Python without any certification
import paho.mqtt.client as mqtt # The callback for when the client receives a CONNACK response from the server. def on_connect(client, userdata, flags, rc): print("Connected with result code "+str(rc)) # Subscribing in on_connect() means that if we lose the connection and # reconnect then subscriptions will be renewed. client.subscribe("paho/test/single") # The callback for when a PUBLISH message is received from the server. def on_message(client, userdata, msg): print(msg.topic+" "+str(msg.payload)) client = mqtt.Client(client_id='mqtt_testing_client') client.on_connect = on_connect client.on_message = on_message client.connect("localhost", 1883, 60) client.loop_forever()
Enable server side certification
-
Go into docker container
$ docker exec -it emqx31 /bin/sh $ cd /opt/emqx/etc/certs
Create CA organization
-
Create ca-key.pem
$ openssl genrsa -out ca-key.pem -des 1024
-
Create ca-csr.pem
$ openssl req -new -key ca-key.pem -out ca-csr.pem
-
Create ca-cert.pem
$ openssl x509 -req -in ca-csr.pem -signkey ca-key.pem -out ca-cert.pem
Create server certificate
-
Create server-key.pem
$ openssl genrsa -out server-key.pem 1024
-
Create server-csr.pem
Create openssl.cnf as follows, as you see, you can add any domain or ip in
alt_names
section:[req] distinguished_name = req_distinguished_name req_extensions = v3_req [req_distinguished_name] countryName = Country Name (2 letter code) countryName_default = TW localityName = Locality Name (eg, city) localityName_default = Taipei organizationalUnitName = Organizational Unit Name (eg, section) organizationalUnitName_default = Domain Control Validated commonName = Internet Widgits Ltd commonName_max = 64 [v3_req] basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment subjectAltName = @alt_names [alt_names] IP.1 = 127.0.0.1 DNS.1 = *.localhost DNS.2 = localhost
Then
$ openssl req -new -key server-key.pem -config openssl.cnf -out server-csr.pem
-
Create server-cert.pem
$ openssl x509 -req -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -in server-csr.pem -out server-cert.pem -extensions v3_req -extfile openssl.cnf
You should have following files under
/opt/emqx/etc/certs
in docker container:$ ls -1a ca-cert.pem ca-cert.srl ca-csr.pem ca-key.pem server-cert.pem server-csr.pem server-key.pem
EMQX config
-
Modify EMQX config
Find following lines in
/opt/emqx/etc/emqx.conf
and modify them as:listener.ssl.external.keyfile = etc/certs/server-key.pem listener.ssl.external.certfile = etc/certs/server-cert.pem listener.ssl.external.cacertfile = etc/certs/ca-cert.pem
-
Restart EMQX server
$ cd /opt/emqx $ ./bin/emqx restart
Client side connection
-
Copy ca file to client side first
$ docker cp emqx31:/opt/emqx/etc/certs/ca-cert.pem .
-
Sample MQTT client in Python with server side certificate
import ssl import paho.mqtt.client as mqtt def on_connect(client, userdata, flags, rc): """ 0 Connection Accepted 4 Connection Refused, bad user name or password 5 Connection Refused, not authorized """ client.subscribe("paho/test/single") def on_message(client, userdata, msg): print(msg.topic+" "+str(msg.payload)) client = mqtt.Client(client_id='abaw') client.on_connect = on_connect client.on_message = on_message client.tls_set("ca-cert.pem", tls_version=ssl.PROTOCOL_TLSv1_2) client.connect("localhost", 8883, 60) client.loop_forever()
Enable client side certificate
Create client site certificate
-
Create client-key.pem
$ openssl genrsa -out client-key.pem
-
Create client-csr.pem
$ openssl req -new -key client-key.pem -out client-csr.pem
-
Create client-cert.pem
$ openssl x509 -req -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -in client-csr.pem -out client-cert.pem
-
Enable client side certificate in EMQX
Modify
/opt/emqx/etc/emqx.conf
:# enable the client side certificates listener.ssl.external.verify = verify_peer # set it to 'true' to allow the ssl with client side certificate only listener.ssl.external.fail_if_no_peer_cert = true
Restart server
$ cd /opt/emqx $ ./bin/emqx restart
-
Copy the client certification files to client side
$ docker cp emqx31:/opt/emqx/etc/certs/client-key.pem . $ docker cp emqx31:/opt/emqx/etc/certs/client-cert.pem .
-
Sample MQTT client in Python with client side certificate
import ssl import paho.mqtt.client as mqtt def on_connect(client, userdata, flags, rc): print("Connected with result code "+str(rc)) client.subscribe("paho/test/single") def on_message(client, userdata, msg): print(msg.topic+" "+str(msg.payload)) client = mqtt.Client(client_id='mqtt_testing_client') client.on_connect = on_connect client.on_message = on_message client.tls_set( ca_certs="ca-cert.pem", certfile="client-cert.pem", keyfile="client-key.pem", tls_version=ssl.PROTOCOL_TLSv1_2) client.connect("localhost", 8883, 60) client.loop_forever()
References
- https://github.com/emqx/emqx/issues/562
- https://medium.com/@emqtt/securing-emq-connections-with-ssl-432672ab9f06
Til next time,
abawchen
at 19:11