moscajs / aedes

Barebone MQTT broker that can run on any stream server, the node way
MIT License
1.79k stars 231 forks source link

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

Open hchautrung opened 4 days ago

hchautrung commented 4 days ago

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
robertsLando commented 3 days ago

you are missing aedes.handle part in your code

hchautrung commented 3 days ago

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

robertsLando commented 3 days ago

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

hchautrung commented 2 days ago

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