thanhpk
6/1/2016 - 5:04 PM

nginx websocket proxy pass

nginx websocket proxy pass

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');