Search code examples
node.jssslnginxhttpsjava-web-start

How to get SSL to play nice with non-ssl protocol data


Short background: If we go back in time to about 2006-ish: We (ie: my company) used a java client app embedded in the browser that connected via port 443 to a C program backend running on port 8068 on an in-house server. At the time when the java app was first developed, port 443 was the only port that we knew would not be blocked by our customers that used the software (ease of installation and possibly the customer in-house staff didn't have the power or knowledge to control their internal firewall).

Fast-forward to 2016, and I'm hired to help develop a NodeJS/Javascript version of that Java app. The Java app continues to be used during development of its replacement, but whoops - we learn that browsers will drop support for embedded Java in the near future. So we switch to Java Web Start, so that the customers can continue to download the app and it still connects to the in house server with it's port 443->8068 routing.

2017 rolls around and don't you know, we can't use the up-coming JS web-app with HTTPS/SSL and the Java app at the same time, 'cause they use the same port. "Ok let's use NGINX to solve the problem." But due to in house politics, customer needs, and a turn-over of web-developer staff, we never get around to truly making that work.

So here we are at 2020, ready to deploy the new web version of the client software, and the whole 443 mess rears it's ugly head again.

Essentially I am looking to allow (for the time being) the Java app to continue using 443, but now need to let the web app use HTTPS too. Back in 2017/2018 we Googled ways to let them cohabitate through NGINX, but we never really got them to work properly, or the examples and tutorials were incomplete or confusing. It seemed like we needed to either use streaming along the lines of https://www.nginx.com/blog/running-non-ssl-protocols-over-ssl-port-nginx-1-15-2/ , or look at the incoming HTTPS header and do an 'if (https) { route to nodeJS server } else { assume it must be the java app and route to port 8068 }' -sort of arrangement inside the NGINX config file.

Past Googled links appear to not exist anymore, so if anyone knows of an NGINX configuration that allows an HTTPS website to hand off to a non-SSL application that still needs to use 443, I would greatly appreciate it. And any docs and/or tutorials that point us in the right direction would be helpful too. Thanks in advance!


Solution

  • You can do this using ssl_preread option. Basically, this option will allow access to the variable $ssl_preread_protocol, that contains the protocol negotiated at SSL port. If no valid protocol was detected, the variable will be empty.

    Using this parameters, you could use the follow configuration to your environment:

    stream {
        upstream java {
            server __your_java_server_ip__:8068;
        }
    
        upstream nodejs {
            server __your_node_js_server_ip__:443;
        }
    
        map $ssl_preread_protocol $upstream {
            default java;
            "TLSv1.2" nodejs;
        }
    
        server {
            listen 443;
    
            proxy_pass $upstream;
            ssl_preread on;
        }
    }
    

    In your case, this configuration will pass the connection directly to your nodejs and java backend servers, so, nodejs will need to negotiate the SSL. You can pass this work to NGiNX using another server context, like:

    stream {
        upstream java {
            server __your_java_server_ip__:8068;
        }
    
        upstream nodejs {
            server 127.0.0.1:444;
        }
    
        map $ssl_preread_protocol $upstream {
            default java;
            "TLSv1.2" nodejs;
        }
    
        server {
            listen 443;
    
            proxy_pass $upstream;
            ssl_preread on;
        }
    }
    
    http {
        server {
            listen 444 ssl;
            __your_ssl_cert_configurations_here__
    
            location / {
                proxy_pass http://__your_nodejs_server_ip__:80;
            }
        }
    }
    

    You'll need NGiNX at least version 1.15.2 to this configuration to work, and compiled with ngx_stream_ssl_preread_module module (need to compile with --with-stream_ssl_preread_module configuration parameter, because this module is not built by default).

    Source: https://www.nginx.com/blog/running-non-ssl-protocols-over-ssl-port-nginx-1-15-2/