Using SSH as a method of Firewall circumvention

More than just a simple terminal session

Posted by Zain Ahmed on August 06, 2021

Introduction

By nature, SSH is a secure protocol, which allows for all traffic going through a SSH tunnel to be encrypted, readable only by the client and server. SSH is typically used for a simple command line interface session on a remote server, but OpenSSH extends beyond simply running commands on a remote machine.

The Scenario

It was yet another day of school, and my friend and I were about to start coding over SSH on our servers at home. We noticed, to our horror, that all the SSH connections would timeout. We thought about dealing with it later, but got hit by another roadblock: Github was now blocked. I recalled reading once about using SSH as a proxy and decided to crack on with a solution.

Implementation

It turns out that OpenSSH client has the -D flag, which binds a SOCKS5 proxy from the SSH Server to any local port.
I primarily used ssh -D 1337 -f -C -q -N <SSH Server Host/IP> -p 80 to use SSH over port 80, and then bind a Socks5 proxy to localhost port 1337.
Git can also be proxied over Socks5, using git --config --global http.proxy socks5://127.0.0.1:1337. This can be unset using git --config --global --unset http.proxy.
To forward remote ports to a local machine, SSH provides the -L flag, which can be used as such:
ssh -L <local port>:<Remote Machine Host/IP (Not SSH server)>:<remote port> -f -C -q -N <SSH Server Host/IP> -p 80 To forward RDP from a machine over the remote server's LAN, I used the following to bind RDP to 127.0.0.1:3388
ssh -L 3388:192.168.0.3:3389 -f -C -q -N remotesshserver.example.com -p 80
Reverse port forwarding is also possible using the -R flag, which allows for local ports to be bound to the remote server. Examples:
ssh -R <remote port>:<Local Machine Host/IP (Not SSH server)>:<local port:> -f -C -q -N <SSH Server Host/IP> -p 80
To forward a local service running on port 8080 to a remote machine on port 443, one would simply run:
ssh -R 443:192.168.0.3:8080 -f -C -q -N remotesshserver.example.com -p 80

Use with HTTPS

Originally this solution worked just fine alongside with NGINX, as I was using cloudflare in between web clients and my server. Cloudflare was configued to always use HTTPS, which resulted in port 80 not being needed. When migrating sites off of cloudflare and directly onto my server, I ran into the issue of needing to process both HTTPS and HTTP traffic, as HTTP would be needed to redirect accidental traffic to HTTPS. After a few hours of searching, I came across SSLH, a nifty SSH/SSL multiplexer allowing a server to run HTTPS and SSH on the same port. I setup SSLH to bind to my server's LAN IP (do not use 0.0.0.0), and set NGINX to listen on 443 locally only. This can be done by setting the listen directive in server blocks to listen 127.0.0.1:443 ssl; This is also very important, if both SSLH and NGINX try to bind to the same port on the same IP, either one of them will fail.

You can install SSLH on Debian/Ubuntu using apt install sslh.

Conclusion

Once setup, I was able to process HTTP traffic on port 80 and both SSH and HTTPS on port 443 with no issues, for the rest of my high school career. Although this is very useful for bypassing firewalls, it is also very useful for development, I still use this almost daily in different scenarios, such as running a local app with HTTPS through my NGINX server.