161
votes

I'm so lost and new to building NGINX on my own but I want to be able to enable secure websockets without having an additional layer.

I don't want to enable SSL on the websocket server itself but instead I want to use NGINX to add an SSL layer to the whole thing.

Every web page out there says I can't do it, but I know I can! Thanks to whoever (myself) can show me how!

8

8 Answers

230
votes

Just to note that nginx has now support for Websockets on the release 1.3.13. Example of use:

location /websocket/ {

    proxy_pass ​http://backend_host;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 86400;

}

You can also check the nginx changelog and the WebSocket proxying documentation.

55
votes

Have no fear, because a brave group of Ops Programmers have solved the situation with a brand spanking new nginx_tcp_proxy_module

Written in August 2012, so if you are from the future you should do your homework.

Prerequisites

Assumes you are using CentOS:

  • Remove current instance of NGINX (suggest using dev server for this)
  • If possible, save your old NGINX config files so you can re-use them (that includes your init.d/nginx script)
  • yum install pcre pcre-devel openssl openssl-devel and any other necessary libs for building NGINX
  • Get the nginx_tcp_proxy_module from GitHub here https://github.com/yaoweibin/nginx_tcp_proxy_module and remember the folder where you placed it (make sure it is not zipped)

Build Your New NGINX

Again, assumes CentOS:

  • cd /usr/local/
  • wget 'http://nginx.org/download/nginx-1.2.1.tar.gz'
  • tar -xzvf nginx-1.2.1.tar.gz
  • cd nginx-1.2.1/
  • patch -p1 < /path/to/nginx_tcp_proxy_module/tcp.patch
  • ./configure --add-module=/path/to/nginx_tcp_proxy_module --with-http_ssl_module (you can add more modules if you need them)
  • make
  • make install

Optional:

  • sudo /sbin/chkconfig nginx on

Set Up Nginx

Remember to copy over your old configuration files first if you want to re-use them.

Important: you will need to create a tcp {} directive at the highest level in your conf. Make sure it is not inside your http {} directive.

The example config below shows a single upstream websocket server, and two proxies for both SSL and Non-SSL.

tcp {
    upstream websockets {
        ## webbit websocket server in background
        server 127.0.0.1:5501;
        
        ## server 127.0.0.1:5502; ## add another server if you like!

        check interval=3000 rise=2 fall=5 timeout=1000;
    }   

    server {
        server_name _;
        listen 7070;

        timeout 43200000;
        websocket_connect_timeout 43200000;
        proxy_connect_timeout 43200000;

        so_keepalive on;
        tcp_nodelay on;

        websocket_pass websockets;
        websocket_buffer 1k;
    }

    server {
        server_name _;
        listen 7080;

        ssl on;
        ssl_certificate      /path/to/cert.pem;
        ssl_certificate_key  /path/to/key.key;

        timeout 43200000;
        websocket_connect_timeout 43200000;
        proxy_connect_timeout 43200000;

        so_keepalive on;
        tcp_nodelay on;

        websocket_pass websockets;
        websocket_buffer 1k;
    }
}
48
votes

This worked for me:

location / {
    # redirect all HTTP traffic to localhost:8080
    proxy_pass http://localhost:8080;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # WebSocket support
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

-- borrowed from: https://github.com/nicokaiser/nginx-websocket-proxy/blob/df67cd92f71bfcb513b343beaa89cb33ab09fb05/simple-wss.conf

20
votes

for .net core 2.0 Nginx with SSL

location / {
    # redirect all HTTP traffic to localhost:8080
    proxy_pass http://localhost:8080;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # WebSocket support
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $http_connection;
}

This worked for me

10
votes

For me, it came down to the proxy_pass location setting. I needed to change over to using the HTTPS protocol, and have a valid SSL certificate set up on the node server side of things. That way when I introduce an external node server, I only have to change the IP and everything else remains the same config.

I hope this helps someone along the way... I was staring at the problem the whole time... sigh...

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}
upstream nodeserver {
        server 127.0.0.1:8080;
}
server {
        listen 443 default_server ssl http2;
        listen [::]:443 default_server ssl http2 ipv6only=on;
        server_name mysite.com;
        ssl_certificate ssl/site.crt;
        ssl_certificate_key ssl/site.key;
        location /websocket { #replace /websocket with the path required by your application
                proxy_pass https://nodeserver;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection $connection_upgrade;
                proxy_http_version 1.1;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
                proxy_intercept_errors on;
                proxy_redirect off;
                proxy_cache_bypass $http_upgrade;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-NginX-Proxy true;
                proxy_ssl_session_reuse off;
            }
}
6
votes

A good, concise article by Pankaj Malhotra discusses how to do this with NGINX and is available here.

The basic NGINX configuration is reproduced below:

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

upstream appserver {
    server 192.168.100.10:9222; # appserver_ip:ws_port
}

server {
    listen 8888; // client_wss_port

    ssl on;
    ssl_certificate /path/to/crt;
    ssl_certificate_key /path/to/key;


    location / {
        proxy_pass http://appserver;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}
5
votes

Using nginx/1.14.0

i have a websocket-server running on port 8097 and users connect from to wss on port 8098, nginx just decrypts the content and forwards it to the websocket server

So i have this config file (in my case /etc/nginx/conf.d/default.conf)

server {
    listen   8098;
        ssl on;
        ssl_certificate      /etc/ssl/certs/domain.crt;
        ssl_certificate_key  /root/domain.key;
    location / {

        proxy_pass http://hostname:8097;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 86400;

    }
}
1
votes

If you want to add SSL in your test environment, then you can use mkcert. Below I mentioned the GitHub URL.
https://github.com/FiloSottile/mkcert
And also below I mentioned sample nginx configuration for reverse proxy.

server {
        listen 80;
        server_name test.local;
        return 301 https://test.local$request_uri;
}
server {
  listen 443 ssl;
  server_name test.local;
  ssl_certificate /etc/nginx/ssl/test.local.pem;
  ssl_certificate_key /etc/nginx/ssl/test.local-key.pem;

   location / {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $remote_addr;
      proxy_set_header X-Client-Verify SUCCESS;
      proxy_set_header Host $http_host;
      proxy_set_header X-NginX-Proxy true;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_pass http://localhost:3000;
      proxy_redirect off;
      proxy_buffering off;
    }
}