Set up nginx proxy to nodejs websocket * * * Tài liệu này trình bày hai cách cấu hình, `https ở nginx -> wss nodejs` hoặc từ `https ở nginx -> ws` ở nodejs Để điều hướng request từ cổng 443 vào websocket server (demo bằng nodejs) * Yêu cầu, chạy nginx 1.4 trên ubuntu # From https to wss * nginx nghe cổng 443, toàn bộ request `wss://domain.com/ws` sẽ được chuyển về `wss://127.0.0.1:2910` tức là cả nginx và nodejs websocket server đều chạy https file websocketserver.js ``` import * as https from 'https'; import * as fs from 'fs'; import * as websocket from 'websocket'; export class WS { private httpserver; public constructor(keypath:string, certpath:string) { var option = { key: fs.readFileSync(keypath), cert: fs.readFileSync(certpath) }; this.httpserver = https.createServer(option, function (req, res) { res.writeHead(404); res.end(); }); } public run() { var me = this; this.httpserver.listen(2910, function () { console.log('Websocket server / OK / ' + 2910); }); var wsServer = new websocket.server({ httpServer: me.httpserver, autoAcceptConnections: false }); function originIsAllowed(origin:string) { // put logic here to detect whether the specified origin is allowed. return true; } wsServer.on('request', function (request) { if (!originIsAllowed(request.origin)) { // Make sure we only accept requests from an allowed origin request.reject(); console.log((new Date()) + 'Connection from origin ' + request.origin + ' rejected.'); return; } console.log('new coming'); var connection = request.accept('proto', request.origin); connection.on('message', function (message) { console.log('received: ' + message); }); connection.on('close', function (reasonCode, description) { console.log('closed'); }); }); } } new WS('./key.pem', './cert.cert').run(); ``` cấu hình nginx ``` upstream websocket { server 127.0.0.1:2910; } server { charset utf-8; listen 443; server_name domain.com; ssl on; ssl_certificate /etc/ssl/certs/chained.pem; ssl_certificate_key /etc/ssl/private/domain.key; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-G\ CM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES\ 128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA; ssl_session_cache shared:SSL:50m; ssl_dhparam /etc/ssl/certs/dhparam.pem; ssl_prefer_server_ciphers on; access_log /home/thanhpk/tmp/meotrics-access443.log; error_log /home/thanhpk/tmp/meotrics-error443.log; location /ws { proxy_pass https://websocket; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } } ``` nginx sẽ gửi request tới https://127.0.0.1:2910/ws, tuy nhiên sẽ bị chặn lại bởi key và cert của nodejs chưa đăng ký -> cần tự xác thực một certificate dùng trong server. Gõ lệnh sau đây ở ubuntu ``` openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.crt -days 100 -nodes ``` Nhập thông tin cho certificate, chú ý, khi nhập _Common Name (eg, your name or your FQDN of the server)_ phải điền `127.0.0.1`. Nếu bỏ qua bước này, sinh lại certificate và nhập lại. Sau khi sinh, sẽ có 2 file key.pem và cert.crt vào terminal gõ ``` apt-get install ca-certificates cp cert.crt /usr/share/ca-certificates dpkg-reconfigure ca-certificates ``` Chọn ask, rồi tìm và tích chọn _cert.crt_ để báo cho hệ thống biết _cert.crt_ là một cert hợp lệ. Cuối cùng, chạy nodejs server bằng cách gõ `node websocketserver.js` vào terminal. Thử lại bằng cách vào trình duyệt Chrome, bật cửa số console, gõ ``` var ws = new WebSocket('wss://domain.com/ws', 'proto'); ``` Cửa số terminal chạy nodejs sẽ hiển thị thông tin kết nối. # From https to ws nginx chạy https:443 còn nodejs chạy ws bình thường, khi cấu hình kiểu này, ko cần bước tự ký nữa file websocketserver.js ``` import * as http from 'http'; import * as fs from 'fs'; import * as websocket from 'websocket'; export class WS { private httpserver; public constructor() { this.httpserver = https.createServer(function (req, res) { res.writeHead(404); res.end(); }); } public run() { var me = this; this.httpserver.listen(2910, function () { console.log('Websocket server / OK / ' + 2910); }); var wsServer = new websocket.server({ httpServer: me.httpserver, autoAcceptConnections: false }); function originIsAllowed(origin:string) { // put logic here to detect whether the specified origin is allowed. return true; } wsServer.on('request', function (request) { if (!originIsAllowed(request.origin)) { // Make sure we only accept requests from an allowed origin request.reject(); console.log((new Date()) + 'Connection from origin ' + request.origin + ' rejected.'); return; } console.log('new coming'); var connection = request.accept('proto', request.origin); connection.on('message', function (message) { console.log('received: ' + message); }); connection.on('close', function (reasonCode, description) { console.log('closed'); }); }); } } new WS().run(); ``` cấu hình nginx ``` upstream websocket { server 127.0.0.1:2910; } server { charset utf-8; listen 443; server_name domain.com; ssl on; ssl_certificate /etc/ssl/certs/chained.pem; ssl_certificate_key /etc/ssl/private/domain.key; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-G\ CM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES\ 128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA; ssl_session_cache shared:SSL:50m; ssl_dhparam /etc/ssl/certs/dhparam.pem; ssl_prefer_server_ciphers on; access_log /home/thanhpk/tmp/meotrics-access443.log; error_log /home/thanhpk/tmp/meotrics-error443.log; location /ws { proxy_pass http://websocket; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } } ``` chạy nodejs server bằng cách gõ node websocketserver.js, vào trình duyệt, bật cửa số console, gõ var ws = new WebSocket('wss://domain.com/ws', 'proto');