moscajs / mosca

MQTT broker as a module
mosca.io
3.2k stars 508 forks source link

Mosca and letsencrypt #608

Closed buff closed 7 years ago

buff commented 7 years ago

Hi. This might be a config issue on my side. I am using letsencrypt to generate my certificate. It is not self-signed as far as I am aware but unless I use the rejectUnauthorized: false in the mqtt.js library I cannot connect. The Paho mqtt client library simply rejects my connection. What additional config do I need or how else can I debug my problem?

mcollina commented 7 years ago

how are you using it in your application? Is this over tcp or over websockets?

buff commented 7 years ago

Websockets.

buff commented 7 years ago

The websockets connection works when it is ws. The same key/cert permutation works in my nginx configuration for serving my web pages. Why does mosca think this permutation is the sign of a self signed certificate??

mcollina commented 7 years ago

Can you please upload your Mosca configuration? Usually those issues are related to a client misconfigration, rather than a server one.

buff commented 7 years ago

Hi, My settings are below. I have a mqtt and a websocket server running. PORT : 3535, WS_PORT : 3443, KEY : '/etc/letsencrypt/live/someurl.com/privkey.pem', CERT : '/etc/letsencrypt/live/someurl.com/cert.pem', // ======================= // Configuration ========= // ======================= var port = config.PORT || process.env.PORT; var pubsubsettings = { type: 'mongo', url: db.URL, pubsubCollection: 'mqttCollections', mongo: {} }; var moscaSettings = { port: port, backend: pubsubsettings, //stats: true //publish the stats every 10s (default false). persistence: { factory:mosca.persistence.Mongo, url: db.URL } //logger: { level: 'debug' } };

var moscaWSSettings; if(env === 'development') { moscaWSSettings = { http: { port: config.WS_PORT, bundle: true, static: './' }, backend: pubsubsettings, persistence: { factory:mosca.persistence.Mongo, url: db.URL } //logger: { level: 'debug' } }; logger.debug('Http server configured'); } else { moscaWSSettings = { https: { port: config.WS_PORT, bundle: true, static: './' }, backend: pubsubsettings, persistence: { factory:mosca.persistence.Mongo, url: db.URL }, secure : { keyPath: config.KEY, certPath: config.CERT, } //logger: { level: 'debug' } }; logger.debug('Https server configured'); }

//Setup the Mosca server var server = new mosca.Server(moscaSettings); server.on('ready', function() { logger.info('Mosca server is up and running port[%d]', port); });

// Create WS server var WSServer = new mosca.Server(moscaWSSettings); WSServer.on('ready', function() { logger.info('WS Mosca server is up and running port[%d]', config.WS_PORT); });

buff commented 7 years ago

My connection options on the client using mqtt.js are below. If I comment out the rejectUnauthorized property the server rejects the connection. var conOptions = { port: config.WS_PORT, wsOptions: {}, //Default websocket connections clientId: deviceId, keepalive: config.KEEP_ALIVE, reconnectPeriod: config.RECONNECT_PERIOD, clean: true, protocolId: 'MQTT', protocolVersion: 4, connectTimeout: config.CONNECTION_TO, will: { topic: 'WillMsg', payload: 'Connection Closed abnormally..!', qos: 0, retain: false }, //rejectUnauthorized: false, qos: 0 //0 – at most once, 1 – at least once, 2 – exactly once };

buff commented 7 years ago

I have tried with both the paho javascript library and mqtt one. I have also tried with different web based clients and the behaviour is all the same.

mcollina commented 7 years ago

@buff is it the server that rejects the connections? With what error? Usually rejectUnauthorized has effects only on the client.

Are you specifying somewhere in MQTT.js that you are connecting via websocket?

buff commented 7 years ago

I use wss:// in the host name. This is the error using the paho client. The error is WebSocket connection to 'wss://someurl:3443/' failed: Error in connection establishment: net::ERR_INSECURE_RESPONSE Paho.MQTT.ClientImpl._doConnect @ mqttws31.js:979 Paho.MQTT.ClientImpl.connect @ mqttws31.js:849 Client.connect @ mqttws31.js:1799 $scope.connect @ admin.controller.js:181 fn @ VM253:4 expensiveCheckFn @ angular.js:16123 callback @ angular.js:26490 $eval @ angular.js:17913 $apply @ angular.js:18013 (anonymous) @ angular.js:26495 defaultHandlerWrapper @ angular.js:3540 eventHandler @ angular.js:3528 angular.js:14110 onConnectFail:[AMQJS0007E Socket error:undefined.][7]

mcollina commented 7 years ago

Are you sure env is properly set in your example?

You should also go with https://github.com/mcollina/mosca/blob/master/examples/Server_With_All_Interfaces-Settings.js#L19, it is clearer to read.

buff commented 7 years ago

Aaah... yes.. That is certainly much easier to read. env is correctly set

buff commented 7 years ago

I see that I don't have different port settings for mqtt(s) and http(s). Going to give your config a go and see if this makes a difference.

buff commented 7 years ago

Have tried with those port settings... Have tried the example wss in the mqtt source code. Still no love. Is there a way I can debug further... I get no output from the server even with debug set? The most info I have received re the error is from the paho client.

mcollina commented 7 years ago

Does those certificate work with basic HTTPS from node core? https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener

buff commented 7 years ago

Yes... I am using it for my main website as well as to secure my api.

mcollina commented 7 years ago

this certificate is configured within nginx/apache, or are you serving https from Node?

If your certificate works with standard HTTPs, then I'll recommend you to use https://github.com/mcollina/mosca/blob/master/lib/server.js#L613-L619. That attaches it to your own https server.

buff commented 7 years ago

The certificate is configured within nginx/apache. Will try the attach

mcollina commented 7 years ago

So, it's not configured in Node.js Have you tried configuring it within standard node.js http? You won't need to encode twice if it's on your website. You can proxy-pass websockets as well with NGINX. You should have TLS configured there (like the normal website).

buff commented 7 years ago

Wait a minute... Maybe I am not understanding the question. I have used the moscasettings config example that you linked to before. I just changed the certificate path to point to my certs. The nginx/apache config is for the api and main website. I call the mqtt webserver using pm2 and go directly to the port that was configured. I don't use nginx/apache for mqtt. What does it mean to configure "it with standard node.js http"?

mcollina commented 7 years ago

Have you tried serving your full website using https://nodejs.org/api/https.html? If your certificate is in nginx, the answer is no. Given that I strongly suspect there is something else going on, I am trying to remove Mosca from the equation. So, can you check if you can serve an "hello world" using https://nodejs.org/api/https.html and your certificates?

buff commented 7 years ago

I have tried the example in this link using the certificate. It is not in nginx... nginx and apache reference the certificate. The hello world example works if I reference my certificates...

mcollina commented 7 years ago

Then call attachHttpServer in your example. It should work, otherwise there is something else in your setup that is wrong.

Another options is doing SSL termination within NGINX also for websockets.

buff commented 7 years ago

I have tried the attach method. It works for a ws connection... it does not work for the wss. I also realised that when I tried the node.js http example I was using curl -k. This is the same as using rejectUnauthorized: false in the mqtt client. If I then try the --cacert option it only works with the letsencrypt fullchain.pem and not the cert.pem. A quick look indicates something of the sort is/was being experienced by the rabbit guys. So there is def something wrong re the certificate. Will give you feedback if I find anything more...

buff commented 7 years ago

The attach method works if I use the fullchain.pem for both the curl request and the attached server. In the case of wss I don't see how that could be implemented.

buff commented 7 years ago

Hi... I'm so embarrassed. I finally worked out what was wrong. I was using IP addresses to connect to my wss interface. As I did not declare this when I created the certificate I got the following error: error: Error: Hostname/IP doesn't match certificate's altnames: "IP: xx.yy.zz is not in the cert's list: ". Thank you for all your help troubleshooting this 'problem' ;-)

mcollina commented 7 years ago

No worries :).