Home

abawchen's graffiti

17 Jun 2019

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