Hi,
I'm going crazy switching a nodejs ws server from the ws:// protocol to the wss:// one
Lets' say this is the ws server:
var WebSocketServer = require('ws').Server
, wss = new WebSocketServer({
port: 999
});
wss.on('connection', function connection(ws) {
var id = ws.upgradeReq.headers['sec-websocket-key'];
console.log("CONNECTED "+id);
ws.on('message', function incoming(message) {
console.log("Message "+message);
});
ws.on('close', function close() {
console.log("DISCONNECTED");
});
});
It works fine. I can connect, I can send a message, and disconnection is fine.
Now I'm trying to connect from web page served by nginx over https:// protocol and I cannot connect to a non secure ws server (browser is blocking it). So I need to switch to a secure protocol.
What I have understood until now is that I need to use something like:
var WebSocketServer = require('ws').Server
, wss = new WebSocketServer({
ssl: true,
port: 999,
ssl_key: 'path/to/privkey.pem',
ssl_cert: 'path/to/cert.pem'
});
The script is on the same server as nginx, and I'm pointing the same key and cert files.
But wat else should I change? When I'm running the server with the above changes I can still connect using ws:// but not using wss://.
All pages I'have found confuse me because everywhere people talk about "app", "https", "proxies" and so on... :)
Can somebody help me with this?
Thank you!!!
The script is on the same server as nginx, and I'm pointing the same key and cert files.
In this case I would just use nginx as a SSL termination proxy and forward the upgrade requests to the WebSocket server without changing anything on your WebSocket server.
If you can't use the above solution you have to create an HTTPS server and then pass that server to the WebSocket server constructor.
const server = https.createServer(...);
const wss = new WebSocket.Server({ server });
I'm going to try this.
Is it correct to say that ws secure protocol can only be used in conjunction with a http server? Or can it in some way support secure protocol in a "standalone" way?
Is it correct to say that ws secure protocol can only be used in conjunction with a http server?
Correct.
So.. that's why "everywhere people talk about "app", "https", "proxies" and so on...".
Thank you!!!
I've found an easier (for me) alternative. Using stunnel.
Stunnel is a daemon that runs in background. It loads ssl certificates, accepts connections on a choosen port and acts like a proxy to another port, decrypting traffic from the client before forwarding it to the ws server (or whatever it is) and vice versa.
In this way I have not to alter nginx configuration or add an https server to the nodejs script.
Easier for me and maybe useful for someone else.
Whatever works better for you but I think it is not necessary. All you have to do is add something like this in the server block of your NGINX config.
location ~ ^/foo {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://127.0.0.1:3000;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
where /foo is the path for WebSocket requests and http://127.0.0.1:3000 the URL of the WebSocket server.
I tried it, but had an "ERR_INSECURE_RESPONSE". Most probably because something was wrong with the certificate/proxy_pass value (tried also to use thedomain) or something like this. Surely if I had tried more it would wok. But I prefer to leave nginx conf untouched. Just because I'm new to nginx too :)
Thanks again!
Closing this, discussion can continue if necessary.
@lpinca Using your SSL example that's in the repo throws an error
Error: connect ECONNREFUSED 127.0.1.1:3000
@lpinca my apologies to post on a closed thread, but I didn't feel good creating a new one asking for advice, so I thought it would be good to raise my question here as a continuation of this thread:
I've implemented wss this way per your suggestion on this thread:
const server = https.createServer(...);
const wss = new WebSocket.Server({ server });
But it bothers me the fact that I have to give access to the private SLL cert and key to the user or group (linux) that runs the node module. I believe that could be considered as a security concern?
I already have Apache2 serving https, so I was thinking of using Apache instead of node, that way I wouldn't have to give the user special access.
I'd then try the proxy pass option:
ProxyPass /ws ws://192.168.0.197:3000/
ProxyPassReverse /ws ws://192.168.0.197:3000/
But I read you mentioning in other issues your concerns on Apache handling these connections, as it would need to spawn multiple threads vs only a single non blocking thread within node.
What do you think about that possible security concern vs the performance that Apache would provide?
@josezulu it depends on you.
But it bothers me the fact that I have to give access to the private SLL cert and key to the user or group (linux) that runs the node module. I believe that could be considered as a security concern?
That actually is the same with the user under which Apache is run.
I would use a "proper" reverse proxy like HAProxy or NGINX. If that isn't an option, Node.js directly. Last time I tried Apache was not good for this.
@lpinca thank you for your response. I went with node's https server.
But now I'm having problems with Avast antivirus on Internet Explorer 11, it blocks the wss connection with error:
SCRIPT12057: WebSocket Error: Network Error 12057, It was not possible to connect to the revocation server or a definitive response could not be obtained.
It works fine on Chrome. It works fine on IE11 another computer without Avast.
When I disable Avast's WebShield "HTTPS Scanning", the problem goes away. But of course users will want that turned on.
Have you heard of issues like this one?
No I'm sorry.
@lpinca : I have fixed my issue.
The https options should include the "ca" file. Now I'm not sure if the lack of it breaks any other browser + anti virus combinations, but it solved it with Avast.
I've noticed that not everyone includes the "ca" files in their tutorials, the Node docs also do not include it.
var options = {
key: fs.readFileSync('/path-to-keys/privkey.pem'),
cert: fs.readFileSync('/path-to-keys/cert.pem'),
ca: fs.readFileSync('/path-to-keys/chain.pem')
};
https.createServer(options, ...)
Most helpful comment
Whatever works better for you but I think it is not necessary. All you have to do is add something like this in the server block of your NGINX config.
where
/foois the path for WebSocket requests andhttp://127.0.0.1:3000the URL of the WebSocket server.