EMQX 6 Certificate-Based Device Authentication Setup¶
Overview¶
This guide provides step-by-step instructions to configure EMQX 6 with certificate-based authentication for your Raspberry Pi fleet. Devices authenticate using X.509 client certificates instead of usernames and passwords, providing:
- Strong Authentication: Cryptographically secure device identity
- No Password Management: Eliminates password rotation burden
- Device-Specific Authorization: ACLs based on certificate identity
- Mutual TLS (mTLS): Both broker and client verify each other
Prerequisites¶
- EMQX 6 installed and running
- OpenSSL installed on your provisioning machine
- MQTTX CLI installed for testing
- Access to this repository's scripts
Step 1: Generate Certificates¶
1.1 Initialize the CA (Certificate Authority)¶
Initialize the root Certificate Authority once:
./scripts/provision-mqtt-certs.sh --init
This creates:
- config/mqtt-ca/ca.key - CA private key (4096-bit RSA)
- config/mqtt-ca/ca.crt - CA public certificate (10-year validity)
Output: You'll see the CA certificate details with fingerprint.
1.2 Generate Device Certificates¶
Generate certificates for all devices:
./scripts/provision-mqtt-certs.sh --all
Or for a specific device:
./scripts/provision-mqtt-certs.sh rpi-prod-1883c404a12f
This creates for each device:
- config/{device}/mqtt/client.key - Device private key
- config/{device}/mqtt/client.crt - Device certificate (2-year validity)
- config/{device}/mqtt/ca.crt - Copy of CA certificate
Verification: Check certificate details:
openssl x509 -in config/rpi-prod-1883c404a12f/mqtt/client.crt -noout -text | head -20
Expected CN value: CN=rpi-prod-1883c404a12f
Step 2: Configure EMQX Server¶
2.1 Prepare Server Certificates¶
You need server-side certificates for the broker. Generate or obtain:
# Generate self-signed server certificate (for testing)
# Or use a valid certificate from Let's Encrypt/Cloudflare
Place on EMQX broker:
- /etc/emqx/certs/server.crt - Server certificate
- /etc/emqx/certs/server.key - Server private key
- /etc/emqx/certs/ca.crt - CA certificate (for validating client certs)
2.2 EMQX Configuration (emqx.conf)¶
Add or modify the TLS listener configuration:
# ==============================================================================
# Listener - TLS/SSL (MQTT over TLS)
# ==============================================================================
listeners.ssl.default {
# Binding address and port
bind = "0.0.0.0:8883"
acceptors = 32
# ============================================================================
# Server Certificates
# ============================================================================
certfile = "/etc/emqx/certs/server.crt"
keyfile = "/etc/emqx/certs/server.key"
# ============================================================================
# Client Certificate Validation (for mTLS)
# ============================================================================
# CA certificate for validating client certificates
cacertfile = "/etc/emqx/certs/ca.crt"
# Require clients to present valid certificate
# Options:
# - verify_none : Don't request client certificate
# - verify_peer : Request but don't require
# - verify_fail_if_no_peer_cert : Require certificate
verify = verify_peer
fail_if_no_peer_cert = true
# ============================================================================
# TLS Version and Security
# ============================================================================
# Only allow TLS 1.2+
tls_versions = ["tlsv1.2", "tlsv1.3"]
# Strong cipher suites (modern browsers/clients)
ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305"
# Prefer server's cipher order
honor_cipher_order = "on"
# ============================================================================
# Optional: Certificate Revocation
# ============================================================================
# Enable if you need to revoke compromised device certificates
crl_check = false
# crl_check_all = false
# crl_files = ["/etc/emqx/certs/ca.crl.pem"]
}
2.3 Certificate Authentication¶
Configure EMQX to extract device identity from certificate CN:
# ==============================================================================
# Authentication
# ==============================================================================
authentication = [
{
enable = true
type = cert
# Extract device ID from certificate Common Name (CN)
# The CN will be used as the MQTT username
# Example: Certificate with CN=rpi-prod-1883c404a12f → username=rpi-prod-1883c404a12f
user_id_from = "cn"
}
]
2.4 Authorization (Optional but Recommended)¶
Configure per-device topic access using ACLs:
# ==============================================================================
# Authorization (ACL)
# ==============================================================================
authorization {
enable = true
type = built_in_database
cache {
enable = true
}
}
# Device-specific topic permissions using ${username} placeholder
acl = [
# Allow each device to publish telemetry
{
action = "allow"
subject {type = "username", value = "${username}"}
object {type = "topic", value = "devices/${username}/telemetry"}
access = "publish"
},
# Allow each device to subscribe to commands
{
action = "allow"
subject {type = "username", value = "${username}"}
object {type = "topic", value = "devices/${username}/command"}
access = "subscribe"
},
# Allow device to check its own connection status
{
action = "allow"
subject {type = "username", value = "${username}"}
object {type = "topic", value = "$SYS/brokers/+/clients/${username}/connected"}
access = "subscribe"
},
# Deny everything else by default
{
action = "deny"
subject {type = "all"}
object {type = "topic", value = "#"}
access = "all"
}
]
2.5 Apply Configuration¶
After updating emqx.conf:
# For Docker Compose
docker-compose restart emqx
# For standalone EMQX
emqx ctl conf reload
# Or restart EMQX
docker restart emqx
Step 3: Verify Setup¶
3.1 Check EMQX Configuration¶
Connect to EMQX Dashboard (http://localhost:18083 by default): 1. Login with default credentials (admin/public) 2. Go to Listeners → verify SSL listener on port 8883 3. Go to Authentication → verify Certificate authenticator enabled 4. Go to Authorization → verify ACL rules loaded
3.2 Test TLS Connection¶
Use OpenSSL to verify the TLS connection works:
openssl s_client -connect mqtts.tagai.xyz:8883 \
-cert config/rpi-prod-1883c404a12f/mqtt/client.crt \
-key config/rpi-prod-1883c404a12f/mqtt/client.key \
-CAfile config/mqtt-ca/ca.crt \
-showcerts
Expected output:
...
Verify return code: 0 (ok)
3.3 Test MQTT Connection with MQTTX CLI¶
See Step 4 below.
Step 4: Test with MQTTX CLI¶
4.1 Install MQTTX CLI¶
# macOS (Homebrew)
brew install mqtt-x
# Or download from: https://mqttx.app/downloads
4.2 Test Connection¶
Connect to the broker:
mqttx conn \
-h mqtts.tagai.xyz \
-p 8883 \
-l mqtts \
--cert config/rpi-prod-1883c404a12f/mqtt/client.crt \
--key config/rpi-prod-1883c404a12f/mqtt/client.key \
--ca config/mqtt-ca/ca.crt \
-i rpi-prod-1883c404a12f
Expected output:
[10:45:12] » [CONNECT] Connecting to mqtts.tagai.xyz:8883...
[10:45:12] « [CONNECT] Connection successful (83ms)
[10:45:12] « [CONNACK] Code: 0 Success
4.3 Publish Test Message¶
In another terminal:
mqttx pub \
-h mqtts.tagai.xyz \
-p 8883 \
-l mqtts \
-t "devices/rpi-prod-1883c404a12f/telemetry" \
-m '{"temperature": 25.5, "humidity": 65}' \
--cert config/rpi-prod-1883c404a12f/mqtt/client.crt \
--key config/rpi-prod-1883c404a12f/mqtt/client.key \
--ca config/mqtt-ca/ca.crt \
-i rpi-prod-1883c404a12f
Expected output:
[10:45:15] » [PUBLISH] devices/rpi-prod-1883c404a12f/telemetry
[10:45:15] « [PUBACK] Packet ID: 1
4.4 Subscribe to Topics¶
mqttx sub \
-h mqtts.tagai.xyz \
-p 8883 \
-l mqtts \
-t "devices/rpi-prod-1883c404a12f/#" \
--cert config/rpi-prod-1883c404a12f/mqtt/client.crt \
--key config/rpi-prod-1883c404a12f/mqtt/client.key \
--ca config/mqtt-ca/ca.crt \
-i rpi-prod-1883c404a12f
Step 5: Deploy to Production¶
5.1 Distribute Device Certificates¶
Copy device certificates to each Raspberry Pi:
./scripts/sync-device-configs.sh
# Or manually:
scp config/rpi-prod-1883c404a12f/mqtt/* pi@rpi-prod-1883c404a12f:/etc/mqtt/
5.2 Configure Node-RED¶
Update Node-RED MQTT client to use certificates. See nodered-mqtt-client.md for detailed instructions.
Example Node-RED configuration:
{
"id": "mqtt-client",
"type": "mqtt-broker",
"name": "EMQX Broker",
"broker": "mqtts.tagai.xyz",
"port": 8883,
"tls": {
"ca": "/etc/mqtt/ca.crt",
"cert": "/etc/mqtt/client.crt",
"key": "/etc/mqtt/client.key"
},
"clientid": "rpi-prod-1883c404a12f"
}
5.3 Configure MQTT Client on Device¶
For any MQTT client (Node-RED, mosquitto, etc.):
# Example with mosquitto_pub
mosquitto_pub -h mqtts.tagai.xyz -p 8883 \
-t "devices/rpi-prod-1883c404a12f/telemetry" \
-m "test" \
--cert /etc/mqtt/client.crt \
--key /etc/mqtt/client.key \
--cafile /etc/mqtt/ca.crt
Troubleshooting¶
Connection Refused¶
Problem: Cannot connect to broker
connect ECONNREFUSED mqtts.tagai.xyz:8883
Solutions:
1. Verify EMQX is running: docker ps | grep emqx
2. Check firewall: sudo ufw status (port 8883 should be open)
3. Verify listener is enabled in EMQX config: listeners.ssl.default
Certificate Verification Failed¶
Problem:
Error: certificate verify failed
Solutions: 1. Check certificate CN matches device ID:
openssl x509 -in config/rpi-prod-1883c404a12f/mqtt/client.crt -noout -subject
subject=CN=rpi-prod-1883c404a12f
- Verify CA certificate path is correct
- Regenerate certificate if CN is wrong:
rm -rf config/rpi-prod-1883c404a12f/mqtt ./scripts/provision-mqtt-certs.sh rpi-prod-1883c404a12f
Authentication Failed¶
Problem:
[DISCONNECT] Code: 135 (NOT_AUTHORIZED)
Solutions:
1. Check EMQX logs: docker logs emqx
2. Verify authenticator is enabled: check authentication.type = cert
3. Verify device username (from CN) exists in EMQX
4. Check ACL rules are correct
Cannot Access Topic¶
Problem:
[PUBLISH FAILED] Error: User not authorized to access topic
Solutions:
1. Verify topic matches ACL rules
2. Check username in certificate CN
3. Review ACL configuration in EMQX
4. Device should only publish to: devices/{device-id}/telemetry
5. Device should only subscribe to: devices/{device-id}/command
Certificate Expired¶
Problem:
Error: certificate has expired
Solutions: 1. Check certificate validity:
openssl x509 -in config/rpi-prod-1883c404a12f/mqtt/client.crt -noout -dates
-
Regenerate certificate:
./scripts/provision-mqtt-certs.sh rpi-prod-1883c404a12f -
Deploy to device:
scp config/rpi-prod-1883c404a12f/mqtt/* pi@rpi-prod-1883c404a12f:/etc/mqtt/
Certificate Lifecycle¶
| Phase | Timeline | Action |
|---|---|---|
| Initial Generation | 2026-01 | Generate 2-year certs for all devices |
| Valid Period | 2026-2028 | No renewal needed |
| Pre-Expiry Prep | ~Jul 2027 | Plan for reissuance |
| Reissuance | ~Dec 2027 | Generate new 2-year batch |
| Deployment | ~Jan 2028 | Sync new certs to devices |
| Expiry | 2028-01 | Old certificates expire |
Security Best Practices¶
✅ Do: - Keep private keys secure (600 permissions, private directories) - Use strong cipher suites (TLS 1.2+) - Enable mutual TLS (verify_peer + fail_if_no_peer_cert) - Monitor certificate expiration dates - Use separate certificates for each device - Enable ACL to restrict per-device topic access
❌ Don't:
- Share private keys between devices
- Use self-signed certificates without validation
- Disable certificate verification (verify = verify_none)
- Commit certificate private keys to git
- Use weak ciphers or old TLS versions
Quick Reference¶
Generate Certificates¶
./scripts/provision-mqtt-certs.sh --init # Initialize CA
./scripts/provision-mqtt-certs.sh --all # Generate all devices
./scripts/provision-mqtt-certs.sh rpi-prod-1883c404a12f # Single device
Test Connection¶
# TLS handshake test
openssl s_client -connect mqtts.tagai.xyz:8883 \
-cert config/rpi-prod-1883c404a12f/mqtt/client.crt \
-key config/rpi-prod-1883c404a12f/mqtt/client.key \
-CAfile config/mqtt-ca/ca.crt
# MQTT connection test
mqttx conn -h mqtts.tagai.xyz -p 8883 -l mqtts \
--cert config/rpi-prod-1883c404a12f/mqtt/client.crt \
--key config/rpi-prod-1883c404a12f/mqtt/client.key \
--ca config/mqtt-ca/ca.crt -i rpi-prod-1883c404a12f
View Certificate Details¶
openssl x509 -in config/rpi-prod-1883c404a12f/mqtt/client.crt -noout -text
Check Certificate Validity¶
openssl x509 -in config/rpi-prod-1883c404a12f/mqtt/client.crt -noout -dates
Related Documentation¶
- emqx-mtls-configuration.md - Detailed configuration reference
- mqtt-mtls-implementation.md - Implementation details
- nodered-mqtt-client.md - Node-RED MQTT setup
- ../archive/MQTT-MTLS-CHECKLIST.md - Archived pre-deployment checklist
Support¶
For issues or questions:
1. Check Troubleshooting section
2. Review EMQX logs: docker logs emqx
3. Consult EMQX documentation: https://docs.emqx.io/
4. Check MQTTX CLI documentation: https://mqttx.app/docs