Last Updated 2024-02-22.
This post is gonna be a long one, but it's mostly just a brain dump of mine. So 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.
Honestly now it seems pretty funny to me that all this is even possible. You can do so much more with SSH than just using it for remote terminal sessions!
Implementation
Note: All of these should in theory support IPv6, I have only shown IPv4 for the sake of simplicity. IPv6 may also, depending on your system, require for the address to be enclosed within the [ ] characters.
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
If you would like other machines on your network to be able to access the port, then you can add the flag -o GatewayPorts=true
and simply connect with any IP address to that machine.
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 alongside HTTPS (over Port 443)
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;
(listen [::1]:443 ssl;
for IPv6). 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
.
In some cases, SSLH may require further configuration via IPTables. To save you from all the trouble I have provided a sample shell script to configure them here (It is even compatible with IPv6!)
Check back to the main blog page for a detailed guide on how to deploy SSLH with dual-stack IPv4 and IPv6, I plan on writing it soon! (as of February 22 2024)
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.