Skip to content

EMQX v6 TLS Configuration for mTLS Client Authentication

Overview

This document describes how to configure EMQX v6 for mutual TLS (mTLS) authentication with Node-RED clients. Devices authenticate using X.509 client certificates instead of passwords, providing strong, passwordless security at scale.

EMQX Configuration

1. TLS Listener Setup

Add the following to your EMQX configuration (typically in emqx.conf or via Helm values):

listeners.ssl.default {
  bind = "0.0.0.0:8883"
  acceptors = 32

  # Server certificate and private key
  certfile = "/etc/emqx/certs/server.crt"
  keyfile = "/etc/emqx/certs/server.key"

  # CA certificate for validating client certs
  cacertfile = "/etc/emqx/certs/ca.crt"

  # Require client certificate
  verify = verify_peer
  fail_if_no_peer_cert = true

  # TLS version and cipher configuration
  tls_versions = ["tlsv1.2", "tlsv1.3"]
  ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256"

  # Optional: CRL checking for revocation
  crl_check = false
  # crl_check_all = false

  # Optional: OCSP stapling (if supported)
  # ocsp_status_request = true
}

2. Certificate-Based Authentication

Enable the certificate authenticator to extract device identity from the client certificate:

authentication = [
  {
    enable = true
    type = cert

    # Extract identity from certificate's Common Name (CN)
    # Common options: cn, dn, san
    # Format: ${cn} extracts the CN field
    user_id_from = "cn"
  }
]

Important: The certificate CN (Common Name) will be used as the MQTT username/clientid. Ensure device certificates have CN set to the device ID (e.g., CN=rpi-prod-1883c404a12f).

3. Authorization and ACLs

Configure ACL rules to restrict device access by client identity:

authorization {
  enable = true
  type = built_in_database
}

# Define per-device ACL rules
# Example for rpi-prod-1883c404a12f:
# User: rpi-prod-1883c404a12f (extracted from certificate CN)
# - Publish:   devices/rpi-prod-1883c404a12f/telemetry
# - Subscribe: devices/rpi-prod-1883c404a12f/command
# - Subscribe: $SYS/brokers/+/clients/rpi-prod-1883c404a12f/connected

acl = [
  {
    action = "allow"
    subject {type = "username", value = "${username}"}
    object {type = "topic", value = "devices/${username}/telemetry"}
    access = "publish"
  },
  {
    action = "allow"
    subject {type = "username", value = "${username}"}
    object {type = "topic", value = "devices/${username}/command"}
    access = "subscribe"
  },
  {
    action = "allow"
    subject {type = "username", value = "${username}"}
    object {type = "topic", value = "$SYS/brokers/+/clients/${username}/connected"}
    access = "subscribe"
  },
  {
    action = "deny"
    subject {type = "all"}
    object {type = "topic", value = "#"}
    access = "all"
  }
]

4. Certificate Installation on EMQX Broker

Place the following files in the EMQX container or volumes:

  • Server Certificate: /etc/emqx/certs/server.crt (public cert for the broker)
  • Server Private Key: /etc/emqx/certs/server.key (broker's private key)
  • CA Certificate: /etc/emqx/certs/ca.crt (CA bundle for validating client certs)

5. Certificate Validity and Rotation

Initial Setup

  • Device Certs: Valid for 2 years (730 days)
  • Server Cert: Valid for 1+ year; rotate annually
  • CA Cert: Valid for 10 years; rarely rotated

Planned Reissuance (Year 2, 2028)

  • Before expiry: Issue new 2-year certificates for all 100 devices
  • Update device certificates via provisioning sync
  • Restart Node-RED MQTT connections
  • No server-side changes needed if same CA is used

6. Revocation (Optional CRL)

To revoke a compromised device certificate:

  1. Revoke the certificate on your CA:

    openssl ca -revoke /path/to/device.crt -keyfile ca.key -cert ca.crt
    

  2. Generate an updated CRL:

    openssl ca -gencrl -keyfile ca.key -cert ca.crt -out ca.crl.pem
    

  3. Publish the CRL to a location accessible to EMQX (e.g., HTTP server or mounted volume)

  4. Update EMQX config to enable CRL checking:

    listeners.ssl.default {
      crl_check = true
      crl_files = ["/etc/emqx/certs/ca.crl.pem"]
    }
    

  5. Reload EMQX or restart the listener for changes to take effect

Node-RED Client Configuration

TLS Configuration Node

Node-RED uses the mqtt-broker node to connect to EMQX. Configure it with:

  • Broker: mqtts.tagai.xyz
  • Port: 8883
  • TLS: Enabled
  • Server Certificate Verification: Enabled
  • Client Certificate: /data/mqtt-certs/client.crt
  • Client Private Key: /data/mqtt-certs/client.key
  • CA Certificate: /data/mqtt-certs/ca.crt
  • Client ID: Device ID (e.g., rpi-prod-1883c404a12f); must match certificate CN
  • Protocol Version: MQTT v5
  • Clean Session: false (retain session state)
  • Keep Alive: 60 seconds
  • Last Will: Publish offline to devices/<device-id>/status
  • Birth Message: Publish online to devices/<device-id>/status

MQTT Nodes

Subscribe to Commands: - Topic: devices/${DEVICE_ID}/command - QoS: 1

Publish Telemetry: - Topic: devices/${DEVICE_ID}/telemetry - QoS: 1 - Retain: false

Monitoring and Debugging

Check TLS Listener Status

emqx_ctl listeners

View Connection Details

emqx_ctl clients list
emqx_ctl clients info <clientid>

Test Client Certificate

mosquitto_sub -h mqtts.tagai.xyz -p 8883 \
  -t "devices/rpi-prod-1883c404a12f/#" \
  --cert config/rpi-prod-1883c404a12f/mqtt/client.crt \
  --key config/rpi-prod-1883c404a12f/mqtt/client.key \
  --cafile config/rpi-prod-1883c404a12f/mqtt/ca.crt

Check Certificate Expiration

On device:

openssl x509 -in /data/mqtt-certs/client.crt -noout -enddate

On broker:

openssl x509 -in /etc/emqx/certs/server.crt -noout -enddate

Troubleshooting

Connection Refused (TLS Handshake Failed)

  • Verify client cert is signed by CA specified in cacertfile
  • Check verify_peer and fail_if_no_peer_cert settings
  • Ensure TLS version matches (tlsv1.2 or tlsv1.3)

Authentication Failure

  • Verify certificate CN matches the expected device ID
  • Check authentication configuration; ensure user_id_from = "cn" is set
  • Review EMQX logs: docker logs <container> | grep -i auth

Certificate Expired

  • Verify cert validity: openssl x509 -in client.crt -noout -dates
  • Regenerate using provisioning script and sync to devices
  • Restart Node-RED to reload certs

ACL Denial

  • Verify device username (from cert CN) matches ACL rules
  • Check logs: emqx_ctl acl list or review authorization logs
  • Ensure topic patterns use ${username} placeholder correctly

References