2

I'm having a problem configuring nginx and node to support socket.io over SSL.

My nginx config:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    listen 443 ssl;
    listen [::]:80;
    listen [::]:443 ssl;

    access_log /var/log/nginx/livetest.log;
    server_name live-test.dev www.live-test.dev;

    ssl_certificate /etc/nginx/ssl/nginx.crt;
    ssl_certificate_key /etc/nginx/ssl/nginx.key;
    ssl_session_cache shared:SSL:50m;
    ssl_session_timeout 5m;

    if ($ssl_protocol = "") {
        rewrite ^ https://$host$request_uri? permanent;
    }

    location / {
        proxy_pass https://live_test;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}

live_test is upstream for node.js running on port 6020. When testing in chrome it does stop on polling with (failed) status. When using wscat:

wscat --connect wss://live-test.dev

I receive: error: Error: self signed certificate

I'm wondering what may be wrong here? Here is my node.js app:

var express = require('express');
var cookie = require('cookie');
var app = express();
var http = require('http').Server(app);
var socketIo = require('socket.io');
var redis = require('redis');
var redisClient = client = redis.createClient();

io.on('connection', function(socket){
    var cookies = cookie.parse(socket.handshake.headers.cookie);

    console.log(cookies);
});

http.listen(6020, function(){
    console.log('listening on 6020');
});

I have a feeling I'm missing something in my node.js app. I thought that since nginx handle SSL node.js does not have to anymore, but perhaps I'm mistaken.

And yes, I'm using self-signed certificate for SSL. Will node.js / socket.io work with self-signed cert?

@UPDATE

Following some reading I changed my node.js app:

var express = require('express');
var cookie = require('cookie');
var fs = require('fs');
var app = express();
var https = require('https').Server(app, {
    key: fs.readFileSync('/etc/nginx/ssl/nginx.key'),
        cert: fs.readFileSync('/etc/nginx/ssl/nginx.crt'),  
});
var socketIo = require('socket.io');
var redis = require('redis');
var redisClient = client = redis.createClient();

var io = new socketIo(https);

io.on('connection', function(socket){
    var cookies = cookie.parse(socket.handshake.headers.cookie);

    console.log(cookies);
});

https.listen(6020, function(){
    console.log('listening on 6020');
});

@UPDATE2

Following the comment by abcdn I did try wscat with -n flag, now getting error: error: Error: unexpected server response (502)

while nginx error.log contains: 2017/03/07 13:44:10 [error] 10556#10556: *140 upstream prematurely closed connection while reading response header from upstream

@UPDATE 3

After further reading, I turned my app.js back to http.

9
  • 1
    Did you try to wscat -n, i.e. wscat --no-check - skip certificate testing? Commented Mar 7, 2017 at 13:38
  • @abcdn I did try it just a minute ago, updated my question Commented Mar 7, 2017 at 13:42
  • @abcdn I believe this means nginx correctly handle the request and it's node that fails? Commented Mar 7, 2017 at 13:53
  • 1
    Yes, I think that means exactly what you said. I am also not sure if you need to use https twice, if live_test is is running on the same machine. What for? You just need to use https between Nginx and the outside world. I am not a network topology specialist, but it seems to me that the traffic between Nginx and the local port of node.js circulates locally only. Commented Mar 7, 2017 at 16:25
  • @abcdn You are right. I did go back to http version. Even more, I managed to find the issue. It was due to using subdomain in my actual code - browser was blocking it until I went directly to the URL and opened the unsecure connection. When switched to single domain it works just fine! Thank you for the help :) Commented Mar 7, 2017 at 17:46

1 Answer 1

1

var fs = require( 'fs' );
var app = require('express')();
var https        = require('https');
var io = require('socket.io')(https);
var HTTPSOptions = {
    cert: fs.readFileSync('path to ssl certificate file'),
    key: fs.readFileSync('path to ssl key file'),
    requestCert: false,
    rejectUnauthorized: false,
};
HTTPSOptions.agent = new https.Agent(HTTPSOptions);
var httpsServer = https.createServer(HTTPSOptions, app);
io = io.listen(httpsServer, {
    log: false
});

//io.sockets.on('connection', function (sock) {
//    console.log("CONNECTED");
//});

var Redis = require('ioredis');
var redis = new Redis();

redis.psubscribe('*', function () {});

redis.on('pmessage', function (subscribed, channel, message) {
    console.log("channel: " + channel);
    console.log("message: " + message);
    message = JSON.parse(message);
    io.emit(channel + ':' + message.event, message.data);
});

httpsServer.listen(6001, function(){
    console.log('Listening on Port 6001');
});

I spent 3 days to find a solution for this and this is my final version that works perfectly. I hope it helps anyone that got stuck like me :)

Sign up to request clarification or add additional context in comments.

1 Comment

Yes, you have "~" in last line, I think that's the cause. This is very nice of you posting your solution here. I did actually let nginx handle all SSL related stuff which it was handling for another app anyway. But let's hope someone will encounter this question and find your answer working!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.