[question]Aedes with Let's Encrypt and Paho mqtt client
Hi all,
I want to implement MQTT over TLS using generated Let's Encrypt of domain mqtt.llos.unimetaverse.net.
- use nginx stream configuration
stream {
log_format basic '$proxy_protocol_addr - $remote_addr [$time_local] $protocol $status $bytes_sent $bytes_received $session_time "$upstream_addr"';
access_log /var/www/llos-mqtt.unimetaverse.net/logs/stream.log basic;
error_log /var/www/llos-mqtt.unimetaverse.net/logs/stream-error.log;
upstream backend {
hash $remote_addr consistent;
server 192.168.98.56:8883; # MQTT broker address and port
}
server {
listen 8883 ssl; # Listen on port 443 for MQTT over SSL
listen [::]:8883 ssl;
# SSL configuration
ssl_certificate /var/www/mqtt.llos.unimetaverse.net/letsencrypt/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mqtt.llos.unimetaverse.net/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/mqtt.llos.unimetaverse.net/chain.pem;
#Optional: Enhance security settings. Use a unique name for SSL session cache to avoid conflicts
ssl_session_cache shared:MQTT_SSL:10m;
ssl_session_timeout 10m;
ssl_prefer_server_ciphers on;
proxy_connect_timeout 1s;
proxy_timeout 10m; # is default
proxy_pass backend;
}
}
- Backend Node.js MQTT using aedes hosting in local IP 192.168.98.56 and open firewall port 8883
const tls = require('tls');
const aedes = require('aedes')();
const ca = fs.readFileSync('certs/signed.llos.unimetaverse.net/fullchain.pem');
const key = fs.readFileSync('certs/signed.llos.unimetaverse.net/privkey.pem');
const cert = fs.readFileSync('certs/signed.llos.unimetaverse.net/cert.pem');
const tls_port = 8883;
const tls_options = {
ca: ca,
key: key,
cert: cert,
requestCert: false,
rejectUnauthorized: false
};
const mqtt_server = tls.createServer(tls_options, aedes.handle)
mqtt_server.listen(tls_port, function () {
console.log(`LLOS's MQTT Broker listening on port ${tls_port}...`)
})
mqtt_server.on('secureConnection', (conn) => {
console.log(`SECURE_CONNECTION`);
});
Note: fullchain.pem is generated by Let's Encrypt only contains intermediate cert and certificate only, not including root-ca certificate.
- Client using paho to connect to the broker using the generated let's encrypt
LLOS_ENDPOINT = "mqtt.llos.unimetaverse.net" # "172.16.2.177"
LLOS_PORT = 8883
PATH_TO_LLOS_ROOT_CA = "../certs/signed.llos.unimetaverse.net/root_fullchain.pem"
PATH_TO_LLOS_CERTIFICATE = "../certs/signed.llos.unimetaverse.net/cert.pem"
PATH_TO_LLOS_PRIVATE_KEY = "../certs/signed.llos.unimetaverse.net/privkey.pem"
def connect_llos():
client = paho_mqtt.Client(paho_mqtt.CallbackAPIVersion.VERSION2, CLIENT_ID)
client.username_pw_set("", "")
client.tls_set(ca_certs=PATH_TO_LLOS_ROOT_CA,
keyfile=PATH_TO_LLOS_PRIVATE_KEY,
certfile=PATH_TO_LLOS_CERTIFICATE,
cert_reqs=ssl.CERT_REQUIRED,
tls_version=ssl.PROTOCOL_TLSv1_2)
#client.tls_insecure_set(True)
print("Connecting to {} with client ID '{}'...".format(LLOS_ENDPOINT, CLIENT_ID))
client.connect(LLOS_ENDPOINT, LLOS_PORT, 60)
client.loop_start()
return client
def main():
client = connect_llos()
Run the Python script and get the error ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get issuer certificate (_ssl.c:1000)
To resolve the error I have to merge the root CA into fullchain.pem and update Python script to use the merged file root_fullchain.pem
$ curl -o isrgrootx1.pem https://letsencrypt.org/certs/isrgrootx1.pem
$ cp fullchain.pem root_fullchain.pem
$ cat isrgrootx1.pem >> root_fullchain.pem
Run the Python script again without error but the console.log(SECURE_CONNECTION) not showing.
Any one can help on this
Thank you
you are missing aedes.handle part in your code
@robertsLando its included const mqtt_server = tls.createServer(tls_options, aedes.handle) . It works fine on localhost with a self singed certificate without Nginx
Sorry missed that line, anyway I have no clue how to help here, for sure it's a misconfiguration on nginx or you are creating certificates wrongly
Hi removed ssl and cert part in the nginx config and it works, I quite not understand.
stream {
log_format basic '$proxy_protocol_addr - $remote_addr [$time_local] $protocol $status $bytes_sent $bytes_received $session_time "$upstream_addr"';
access_log /var/www/llos-mqtt.unimetaverse.net/logs/stream.log basic;
error_log /var/www/llos-mqtt.unimetaverse.net/logs/stream-error.log;
upstream backend {
hash $remote_addr consistent;
server 192.168.98.56:8883; # Your actual MQTT broker address and port
}
server {
listen 8883;
proxy_connect_timeout 1s;
proxy_timeout 10m; # is default
proxy_pass backend;
}
}
Hi removed ssl and cert part in the nginx config and it works, I quite not understand.
stream { log_format basic '$proxy_protocol_addr - $remote_addr [$time_local] $protocol $status $bytes_sent $bytes_received $session_time "$upstream_addr"'; access_log /var/www/llos-mqtt.unimetaverse.net/logs/stream.log basic; error_log /var/www/llos-mqtt.unimetaverse.net/logs/stream-error.log; upstream backend { hash $remote_addr consistent; server 192.168.98.56:8883; # Your actual MQTT broker address and port } server { listen 8883; proxy_connect_timeout 1s; proxy_timeout 10m; # is default proxy_pass backend; } }
I also want to add SSL, but I don't know how. My understanding is that since the certificate is added to nginx, there is no need to add it in aedes. How did you set it up later? Did the system run successfully?
At the moment I use the config above, nginx without SSL certs, and MQTT broker is configured with letsencrypt