25

I have NGINX configured like this as a reverse proxy for http requests:

server {
    listen 80;
    server_name 203.0.113.2;
proxy_set_header X-Real-IP  $remote_addr; # pass on real client IP

location / {
    proxy_pass http://203.0.113.1:3000;
}

}

I also want to proxy ssh (Port 22) requests. Can I add another server block like this to the same configuration file:

server {
    listen 22;
    server_name 203.0.113.2;
proxy_set_header X-Real-IP  $remote_addr; # pass on real client IP

location / {
    proxy_pass http://203.0.113.1:22;
}

}

Such that the end result is this:

server {
    listen 80;
    server_name 203.0.113.2;
proxy_set_header X-Real-IP  $remote_addr; # pass on real client IP

location / {
    proxy_pass http://203.0.113.1:3000;
}

} server { listen 22; server_name 203.0.113.2;

proxy_set_header X-Real-IP  $remote_addr; # pass on real client IP

location / {
    proxy_pass http://203.0.113.1:22;
}

}

Ole
  • 707

2 Answers2

23

The ssh protocol is not based on HTTP, and, as such, cannot be proxied through the regular proxy_pass of ngx_http_proxy_module

However, recently, starting with nginx 1.9.0 (released as stable with 1.10.0 on 2016-04-26), nginx did gain support for doing TCP stream proxying, which means that if you have a recent-enough version of nginx, you can, in fact, proxy ssh connections with it (however, note that you wouldn't be able to add anything like the X-Real-IP to the proxied connection, as this is not based on HTTP).

For more information and examples, take a look at:

cnst
  • 3,283
15

Since Nginx Version 1.9.0,NGINX support ngx_stream_core_module module, it should be enabled with the --with-stream. When the stream module is enabled, it is possible to ssh protocol via a TCP proxy.

stream {
    upstream ssh {
        server 192.168.1.12:22;
    }
    server {
        listen        12345;
        proxy_pass    ssh;
    }
}

https://www.nginx.com/resources/admin-guide/tcp-load-balancing/

  • 1
    That's all nice feature of nginx - but IMHO it's useless when you want to have real reverse proxy like nginx does perfect job for HTTP. The thing is streams approach is simple NAT - so I'd rather do that task on edge router. What I would want/like to have here - is exact same feature set as HTTP reverse proxy. In other words, I have only one public IP address - thus port 22 is avail only for one machine - but if there was a way to distinguish requests in a form ssh me@srv1.my.net and ssh me@srv2.my.net - it would be great. It's protocol limitation (SSH won't give out DNS name client uses) – stamster Jan 26 '18 at 18:40
  • @stamster, you can already do almost the same thing, by either using different port numbers (e.g., 122 for srv1 and 222 for srv2), or by using nested ssh sessions, where you first ssh into the public server/IP, and from there, ssh into the leaves; e.g., ssh user@example.org 'ssh user@192.168.5.1'. – cnst Oct 15 '18 at 15:33
  • 1
    No, the requirement/idea is to use default port (22 or any other service..) as destination port, and then to route traffic depending of DNS name or so. Just like we're all using nginx as a reverse web http proxy server, so each domain targets default ports 80, 443, and then nginx routes traffic depending on proxy rules. Of course client has HOST header so it's easy to distinguish which site it targets... Nested SSH sessions are sort of solution but a messy one - that works though. I ended up with different ports on our edge network - good old NAT as the most KISS and reliable solution. – stamster Oct 18 '18 at 10:17
  • @stamster maybe nginx could use the username, but you'd need a unique name for each server. This would be the equivalent to path-based http forwarding. – jiggunjer Apr 13 '20 at 17:06