1

We have several virtual (Ubuntu) servers running in our company. Each has its network connection bridged, so it gets its own IP address from the router (DHCP-reserved). We need to run several websites with their own subdomains.

We have our top domain (let's say example.com)

Then we have sub1.example.com, sub2.example.com, and sub3.example.com

The simplest way to do this seemed was to run a reverse-proxy (so that in the future we could move some to cloud and use load balancing).

I've set up a new VM with Ubuntu 20.04 LTS running NGINX and had it configured as reverse-proxy.

I currently have 3 configurations:

redirect_https:

server {
    listen 80;
    listen [::]:80;
    
    server_name *.example.com;
    return 301 https://$host$request_uri;
}

ssl-proxy:

server {
    listen 443 ssl;
    server_name example.com;
    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded_Proto https;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_redirect off;
        proxy_pass https://10.10.x.y;
        proxy_http_version 1.1;
    }
    
    ssl_certificate        /etc/<path_to_cert>/fullchain.pem;
    ssl_certificate_key    /etc/<path_to_cert>/privkey.pem;
}

ssl-proxy-subdomains:

server {
    listen 443 ssl;
    server_name sub1.example.com sub2.example.com sub3.example.com;
    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded_Proto https;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_redirect off;
        proxy_pass https://10.10.x.y;
        proxy_http_version 1.1;
    }
    
    ssl_certificate        /etc/<path_to_cert>/fullchain.pem;
    ssl_certificate_key    /etc/<path_to_cert>/privkey.pem;
}

This all works fine.

In order to be able to edit our sites from home, I wanted to configure the proxy to pass SSH traffic as well, that's where I get confused.

I have read a number of posts here, on other sites, NGINX documentation, I don't seem to figure this out.

I followed the example right out of documentation: https://nginx.org/en/docs/stream/ngx_stream_ssl_preread_module.html

Seems pretty straightforward.

My config is virtually identical (only changed parts relevant to my company, like IP addresses, etc.)

For the sake of completeness, the example I used is titled Selecting an upstream based on server name

My config (modified):

map $ssl_preread_server_name $name {
    example.com           backend_main;
    sub1.example.com      backend_1;
    sub2.example.com      backend_2;
    sub3.example.com      backend_3;
    default               backend_proxy;
}

upstream backend_main { server 10.10.x.y:22; }
upstream backend_1 { server 10.10.x.y:22; }
upstream backend_2 { server 10.10.x.y:22; }
upstream backend_3 { server 10.10.x.y:22; }
upstream backend_proxy { server 10.10.x.y:22; }

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

log_format proxy '$remote_addr [$time_local] '
    '$protocol $status $bytes_sent $bytes_received '
    '$session_time "$upstream_addr" '
    '"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';

access_log /var/log/nginx/proxy-access.log proxy;
error_log /var/log/nginx/proxy-error.log;

The above chunk of code lies within stream {...} block in /etc/nginx/nginx.conf after the html {...} block (right before the commented mail {...} block).

The router takes external port 22 and forwards it to internal port 2222 of the reverse proxy.

The problem I'm facing is that map always selects the default option. If I remove/comment the default, I can't connect at all. If I change the IP address on the default, it connects me to that server, but again, only whatever "default" is.

The end goal is to be able to ssh directly into one of the subdomains (e.g. ssh user@sub1.example.com). At the moment I have to ssh into one server to which I gave myself external access, then ssh an internal one from there.

The only thing that comes to mind is that this may not run well with the existing HTTPS proxy, but perhaps stream module should be able to handle those as well (and if it does, will I be able to set headers? otherwise the backends only see the requests coming from the reverse proxy).

I have a suspicion that this is a simple issue and that I overlooked something, but after spending 8 hours of reading and trying different things, I feel it's time to ask for help...

2
  • TLS is not SSH, so you cannot proxy SSH with nginx. You need to use SSH jumphost feature to access IPv4 private addresses behind NAT. – Tero Kilkanen Jul 3 '20 at 6:34
  • Thanks @TeroKilkanen. I know TLS and SSH are not the same thing, but from all the prior reading, I was under the impression that stream block of NGINX could help me. And it does (no weird certificate errors...), but as others have stated below, SSH does not use SNI, so the host name is not passed along with the request, which explains why the default host was always selected. When I commented out the default, in the error log I could see something like host "-" not found. That should've been a sign to me that the host name is not being sent... – nurchi Jul 3 '20 at 15:54
3

SSH client doesn't use SNI extensions of TLS protocol, which were developed to support shared hosting with HTTPS. You can try to setup your ssh client the following way:

Host example.com *.example.com
    ProxyCommand openssl s_client -quiet -servername %h -connect example.com:2222

However I don't know if it would work. You can read more about s_client openssl command here. Also take a look at this article: Proxy SSH using TLS/SNI and NGINX.

1
  • Thanks Ivan. Your explanation makes sense, very much appreciated. I've skimmed through your link, I may have to go that route. – nurchi Jul 3 '20 at 15:47
2

The obvious thing is that ssh is not https and its protocol does not send a hostname with which you can do matching like you can with https.

I have a few hosts with similar restrictions, as they are virtual machines which don't have a global IPv4 address. For these I access them via ssh with IPv6. If for some reason you haven't deployed IPv6, you're several years behind the curve now...

8
  • Thank you for your answer. Maybe I don't fully understand something about IPv6, but from our ISP we get only 1 static IP address. Internally we only have a handful of devices attached to the network, so I really don't see a justification to switch to IPv6. – nurchi Jul 3 '20 at 15:45
  • You only get one IPv4 address and have more than one device. That's sufficient justification to use IPv6. The alternative is nasty hacky workarounds that aren't even necessary anymore. – Michael Hampton Jul 3 '20 at 15:51
  • Example please... – nurchi Jul 3 '20 at 15:55
  • @nurchi For instance, the other answer. – Michael Hampton Jul 3 '20 at 15:57
  • 1
    It's basically the way the Internet was originally designed to work, and did until we ran out of IPv4 addresses. Then came the nasty workaround NAT, which we've been living with for so long (25+ years) that people tend to mistakenly think it is normal, instead of what it really is, a workaround for a design problem in IPv4 that breaks one of its core features. IPv6 restores this and eliminates the need for such workarounds, along the way simplifying the network. – Michael Hampton Jul 3 '20 at 17:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.