Skip to main content

nginx 1.15.2, ssl_preread_protocol, multiplex HTTPS and SSH on the same port

06-08-2018 | Remy van Elst | Text only version of this article


Table of Contents


The NGINX blog recently had a nice article on a new feature of NGINX 1.15.2, $sslprereadprotocol. This allows you to multiplex HTTPS and other SSL protocols on the same port, or as their blog states, 'to distinguish between SSL/TLS and other protocols when forwarding traffic using a TCP (stream) proxy'. This can be used to run SSH and HTTPS on the same port (or any other SSL protocol next to HTTPS). By running SSH and HTTPS on the same port, one can circumvent certain firewall restrictions. If the session looks like HTTPS, nginx will handle it, if it looks like something else, it will forward it to the configured other program. I used to use SSLH to get this functionality, but now it's built into the nginx webserver.

If you like this article, consider sponsoring me by trying out a Digital Ocean VPS. With this link you'll get a $5 VPS for 2 months free (as in, you get $10 credit). (referral link)

This small guide will cover the installation of the latest version of nginx on Ubuntu (16.04) and configuring this multiplex feature.

You must use NGINX in proxy mode. This means that nginx will act as a load balancer or proxy in front of your application (like Django, Rails, etc).

Install the latest version of NGINX

nginx provides a repository for both CentOS, Debian/Ubuntu and SUSE. In this example we will use Ubuntu.

Download the signing key:

wget http://nginx.org/keys/nginx_signing.key

Add the repository:

echo "deb http://nginx.org/packages/mainline/ubuntu/ xenial nginx" > /etc/apt/sources.list.d/nginx.list
echo "deb-src http://nginx.org/packages/mainline/ubuntu/ xenial nginx" >> /etc/apt/sources.list.d/nginx.list

Replace xenial with your version of Ubuntu (use lsb_release -a to find out).

Install nginx:

apt-get update; 
apt-get install nginx

Configure nginx for sslprereadprotocol

Quoted from the nginx blog:

The following configuration snippet uses the $ssl_preread_protocol variable in a map block to set the $upstream variable to the name of the upstream group appropriate for the protocol being used on the connection. The proxy_pass directive then forwards the request to the selected upstream group. Note that the ssl_preread on directive must be included in the server block for the $ssl_preread_protocol variable to work.

stream {
    upstream ssh {
        server 192.0.2.10:22;
    }

    upstream https {
        server 192.0.2.20:443;
    }

    map $ssl_preread_protocol $upstream {
        default ssh;
        "TLSv1.2" web;
    }

    # SSH and SSL on the same port
    server {
        listen 443;

        proxy_pass $upstream;
        ssl_preread on;
    }
}

In this case, if the protocol detected is TLSv1.2, HTTPS is assumed and the traffix is forwarded to the HTTPS server (192.0.2.20). Otherwise the traffic is forwarded to the SSH host (192.0.2.10).

More fun with ssl_preread

The ssl_preread module can detect more than the protocol. The SNI server name is also supported, which allows for proxy forwarding to different backend servers based on the requested SSL hostname. Quoting the documentation:

map $ssl_preread_server_name $name {
    backend.example.com      backend;
    default                  backend2;
}

upstream backend {
    server 192.168.0.1:12345;
    server 192.168.0.2:12345;
}

upstream backend2 {
    server 192.168.0.3:12345;
    server 192.168.0.4:12345;
}

server {
    listen      12346;
    proxy_pass  $name;
    ssl_preread on;
}

Do note that this also requires a newer version of nginx than by default in the Ubuntu 16.04 release.


Tags: firewall  folder  multiplex  nginx  proxy  security  ssh  sslh  subfolder  tutorials