aedes icon indicating copy to clipboard operation
aedes copied to clipboard

[question]Aedes with Let's Encrypt and Paho mqtt client

Open hchautrung opened this issue 1 year ago • 6 comments

Hi all,

I want to implement MQTT over TLS using generated Let's Encrypt of domain mqtt.llos.unimetaverse.net.

  1. 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;
  }
}
  1. 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.

  1. 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

hchautrung avatar Nov 03 '24 10:11 hchautrung

you are missing aedes.handle part in your code

robertsLando avatar Nov 04 '24 09:11 robertsLando

@robertsLando its included const mqtt_server = tls.createServer(tls_options, aedes.handle) . It works fine on localhost with a self singed certificate without Nginx

hchautrung avatar Nov 04 '24 14:11 hchautrung

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

robertsLando avatar Nov 04 '24 15:11 robertsLando

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;
  }
}


hchautrung avatar Nov 06 '24 00:11 hchautrung

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?

yafoo avatar Feb 20 '25 00:02 yafoo

At the moment I use the config above, nginx without SSL certs, and MQTT broker is configured with letsencrypt

hchautrung avatar Feb 23 '25 05:02 hchautrung