[ssl] NGINX to reverse proxy websockets AND enable SSL (wss://)?

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!

This question is related to ssl tcp proxy nginx mod-proxy

The answer is


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;

    }
}

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;
    }
}

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


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;
            }
}

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


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;
    }
}

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;
    }
}

Examples related to ssl

Requests (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available.") Error in PyCharm requesting website A fatal error occurred while creating a TLS client credential. The internal error state is 10013 curl: (35) error:1408F10B:SSL routines:ssl3_get_record:wrong version number How to install OpenSSL in windows 10? ssl.SSLError: tlsv1 alert protocol version Invalid self signed SSL cert - "Subject Alternative Name Missing" "SSL certificate verify failed" using pip to install packages ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:749) Powershell Invoke-WebRequest Fails with SSL/TLS Secure Channel "ssl module in Python is not available" when installing package with pip3

Examples related to tcp

What does "app.run(host='0.0.0.0') " mean in Flask What is the difference between HTTP 1.1 and HTTP 2.0? Sending a file over TCP sockets in Python Telnet is not recognized as internal or external command How to open port in Linux adb connection over tcp not working now Understanding [TCP ACKed unseen segment] [TCP Previous segment not captured] How do I debug error ECONNRESET in Node.js? Differences between TCP sockets and web sockets, one more time Is SMTP based on TCP or UDP?

Examples related to proxy

Axios having CORS issue Running conda with proxy WebSockets and Apache proxy : how to configure mod_proxy_wstunnel? "Proxy server connection failed" in google chrome Set proxy through windows command line including login parameters Could not resolve all dependencies for configuration ':classpath' Problems using Maven and SSL behind proxy Using npm behind corporate proxy .pac git returns http error 407 from proxy after CONNECT Forwarding port 80 to 8080 using NGINX

Examples related to nginx

Kubernetes service external ip pending nginx: [emerg] "server" directive is not allowed here Disable nginx cache for JavaScript files Nginx upstream prematurely closed connection while reading response header from upstream, for large requests Nginx: Job for nginx.service failed because the control process exited How can I have same rule for two locations in NGINX config? How to verify if nginx is running or not? Find nginx version? Docker Networking - nginx: [emerg] host not found in upstream How do I rewrite URLs in a proxy response in NGINX

Examples related to mod-proxy

NGINX to reverse proxy websockets AND enable SSL (wss://)? Bad Gateway 502 error with Apache mod_proxy and Tomcat