nginx

How to configure HTTP CONNECT to SSH Proxy with NGINX?


I'm a user of https://github.com/proxytunnel/proxytunnel to connect to SSH over HTTPS. This requires the HTTP server to redirect the traffic to a local SSH server. Following http://dag.wiee.rs/howto/ssh-http-tunneling/ I implemented this on my server using Apache. Works great!

Question

Now I wish to transfer that config to nginx using ngx_http_proxy_connect_module. Is this possible?

Details

For simplicity, the following example shows the non encrypted configuration. This Apache configuration is from my NixOS config.

httpd = {
 enable = true;
 virtualHosts."localhost:21343" = {
   listen = [
     {
       ip = "*";
       port = 21343;
       ssl = false;
     }
   ];
   extraConfig = ''
     LoadModule proxy_module modules/mod_proxy.so
     LoadModule proxy_connect_module modules/mod_proxy_connect.so
     LoadModule proxy_http_module modules/mod_proxy_http.so
     ProxyRequests on
     AllowConnect 22
     <Proxy *>
       # Deny all proxying by default ...
       Require all denied
     </Proxy>
     <Proxy 127.0.0.1>
       # Now allow proxying through localhost only
       Require all granted
     </Proxy>
 '';
 };

This results in a successful connection as tested by proxytunnel.exe -v (Here the connection goes through an additional local proxy 127.0.0.1:54450, which is not relevant to the question)

proxytunnel.exe -v -q -p 127.0.0.1:54450 -r example.org:21343 -d 127.0.0.1:22
Tunneling to example.org:21343 (remote proxy)
Communication with local proxy:
-> CONNECT example.org:21343 HTTP/1.1
-> Host: example.org:21343
-> Proxy-Connection: Keep-Alive
<- HTTP/1.1 200 Connection established
<-
Tunneling to 127.0.0.1:22 (destination)
Communication with remote proxy:
-> CONNECT 127.0.0.1:22 HTTP/1.1
-> Host: 127.0.0.1:22
-> Proxy-Connection: Keep-Alive
<- HTTP/1.0 200 Connection Established
<- Proxy-agent: Apache/2.4.59 (Unix)
<-
Tunnel established.
SSH-2.0-OpenSSH_9.6

Now I configure the same with nginx, because nginx is my main HTTP server and I wish to do this over a subdomain, instead of another random non-standard port.

nginx = {
 enable = true;
 recommendedProxySettings = true;
 recommendedTlsSettings = true;
 recommendedGzipSettings = true;
 recommendedBrotliSettings = true;
 recommendedZstdSettings = true;
 recommendedOptimisation = true;
 additionalModules = [ pkgs.nginxModules.http_proxy_connect_module_v24 ];
 virtualHosts = {
   "example.org" =  {
     root = "/var/www/example.org";
     enableACME = true;
     forceSSL = true;
   };
   "ssh.example.org" =  {
     extraConfig = ''
       proxy_connect;
       proxy_connect_allow  all; # For testing set to all
       proxy_connect_address 127.0.0.1:22;
     '';
   };
 };
};

Connecting to this results in a rejection however.

proxytunnel.exe -v -q -p 127.0.0.1:54450 -r ssh.example.org:80 -d 127.0.0.1:22
Tunneling to ssh.example.org:80 (remote proxy)
Communication with local proxy:
-> CONNECT ssh.example.org:80 HTTP/1.1
-> Host: ssh.example.org:80
-> Proxy-Connection: Keep-Alive
<- HTTP/1.1 200 Connection established
<-
Tunneling to 127.0.0.1:22 (destination)
Communication with remote proxy:
-> CONNECT 127.0.0.1:22 HTTP/1.1
-> Host: 127.0.0.1:22
-> Proxy-Connection: Keep-Alive
<- HTTP/1.1 405 Not Allowed
<- Server: nginx
<- Date: Fri, 10 May 2024 05:33:10 GMT
<- Content-Type: text/html
<- Content-Length: 150
<- Connection: keep-alive
<-

What am I doing wrong? Is this possible?


Solution

  • This has been solved. Configuring NGINX for SSH over HTTPS / HTTP with proxytunnel has some minute details, that were elaborated in issue https://github.com/proxytunnel/proxytunnel/issues/84 .

    Long story short, with https://github.com/chobits/ngx_http_proxy_connect_module compiled into NGINX:

    proxy_connect; 
    proxy_connect_allow 22; # To limit access to only the SSH port
    proxy_connect_address 127.0.0.1; # To limit access only to localhost and prevent connections to other places
    

    Next, you need to set the server block that this is in as default_server to prevent routing issues, details explained in the issue linked above.

    Finally you can connect. If your proxy_connect statement is in a server that has HTTPS, with a Corporate proxy in between, the connection command will look like: proxytunnel -X -p corporate.com:8888 -r domain.com:443 -d 127.0.0.1:22 and with no corporate firewall in the way, the command looks like: proxytunnel -E -p domain.com:443 -d 127.0.0.1:22.

    Additionally if you use HTTPS instead of HTTP and are on Windows you will need to ignore the certificate check -z, --no-check-certificate or perform some extra steps as explained in issue: https://github.com/proxytunnel/proxytunnel/issues/82 . SSH has still its own encryption, so it doesn't particularly matter whether or not you can trust the certificate.

    Tested a bunch, works flawlessly in all kinds of challenging connection scenarios. Packet sniffing firewalls don't have a clue.