Can nginx serve SSH and HTTP(S) at the same time on the same port

multiplexingnginx

Context

I have a personal server that I use for the web. I sometimes need to SSH/SFTP to it.

Disclamer: I have very little experience with nginx internals.

Problem

This morning, I figured out that the free wifi in a well-know cafe chain was blocking SSH (actually, they are blocking anything that's not on 80/443). But when I need SSH, I need it, so I looked for ways to share SSH and HTTPS on the same port.

What I looked at

I have looked at a few possible solutions that can run on port 443:

  • SSHL: a SSH/OpenVPN/HTTPS multiplexer;
  • OpenVPN: a VPN solution has a built-in multiplexer for OpenVPN and HTTPS;
  • HAProxy: a webserver/load balancer can also multiplex everything.

All of these seem pretty straight-forward but I don't really like the fact of adding layers and complexity and possibly slowing things down just in the unlikely event that I need to SSH on 443.

Putting nginx into the mix

I know that nginx already supports raw TCP streams handling. So I was wondering if I could use that on port 443 too directly in nginx. The idea being that nginx could choose to use the http module if it recognizes HTTP(S) or stream for everything else.

Questions

In that context, I have two questions:

  • Is nginx even capable of doing such a distinction? (I am not even sure I would be able to listen on port 443 in both the http and the stream block at the same time.)
  • If so, would there be any blatant performance issue with that setup? (I am thinking about transfer speed with SFTP for instance, not really SSH per se.)

Best Answer

Since nginx version 1.15.2 added new variable $ssl_preread_protocol. And in official blog added post about how to use this variable for multiplexing HTTPS and SSH on the same port https://www.nginx.com/blog/running-non-ssl-protocols-over-ssl-port-nginx-1-15-2/

Example of configuring SSH(by default) and HTTPS:

stream {
    upstream ssh {
        server 192.0.2.1:22;
    }

    upstream web {
        server 192.0.2.2: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;
    }
}