nginxのngx_stream_ssl_moduleでTLS終端+WebSocket負荷分散

先日、NginxのTCP Load BalancingがOSS版でも使えるらしいので試すで書いたとおり、Nginx 1.9よりTCP Load Balancing機能が使える見込みである。


今回は、更にTLS終端を可能にするngx_stream_ssl_moduleも合わせて使用し、WebSocket over TLSの負荷分散を試してみる。

ngx_stream_ssl_module

ngx_stream_ssl_moduleでは、接続をTLSで受付け、その中身のメッセージをバックエンドに送信する。
TCP Load Balancingの時と同じようにHTTP/HTTPSに限った機能ではないことが特徴となる。
(ただし、ALPNが必要なプロトコルだと少々扱いが難しい)



使うには、前回同様最新リビジョンをビルドする必要がある

http://hg.nginx.org/nginx/ より最新リビジョンをダウンロードしてビルドする。

sudo apt-get install libpcre3 libpcre3-dev build-essential

wget http://hg.nginx.org/nginx/archive/61d7ae76647d.tar.gz
tar zxvf ./61d7ae76647d.tar.gz
cd nginx-61d7ae76647d

##今回はオプションを増やす
./auto/configure  --with-stream  --with-stream_ssl_module --with-http_ssl_module
make
sudo make install

WebSocket over TLSで試す

TLSを終端するために今回はwss(WebSocket over TLS)で試す。


ブラウザはwss://でWebSocket通信を試み、WebSocketサーバは平文で受け付ける。
Nginxでは単純にTLSのメッセージの中身をそのままBackend(WebSocketサーバ)に渡し、返事を受け取りブラウザに返す。


この時、WebSocketのメッセージのやりとりはブラウザとWebSocketサーバでやりとりされていることになる(NginxはただL4で中継しているだけ)。


nginx側設定

nginx.confにおいて、listenでport番号の後ろにsslと指定することでTLS接続を受け付けることが出来る。
別途証明書と鍵は用意しておく。

stream {
    upstream backend {
        server 192.168.0.2:8080;
        server 192.168.0.3:8080;
    }

    server {
        listen 8443 ssl;
        ssl_certificate      /home/vagrant/server.crt;
        ssl_certificate_key  /home/vagrant/server.key;
        proxy_connect_timeout 10s;
        proxy_timeout 10s;
        proxy_pass backend;
    }
}
WebSocketサーバ(backend)

NginxのバックエンドでPlain TCPでWebsocket通信を行う。

server.js

var ws = require('websocket.io');
var server = ws.listen(8080, function () {
    console.log('Server running');
});
server.on('connection', function(client) {
    client.on('message', function(request) {
        console.log(request);
        client.send(request);
    });
});
client側

ブラウザでwss接続するためよう

<script type="text/javascript">
  var ws = new WebSocket("wss://192.168.0.1:8443"); //nginxのIP
  ws.send("{message}")
...    


こんな感じの設定で、Chromeから無事websocketのやりとりが出来た。
自己署名書のwss://アクセスを許可する場合は、一旦https://でアクセスし許可する必要がある。