Recently I needed to do some web development on a set of hosts which are part of a test/staging platform. This test system is not reachable directly via the Internet, only via SSH tunneling. Simply setting up a tunnel did not work though, because there are several hosts and only one can use port 80, the others would have to be accessed using different ports. This in turn would not work because the system generates lots of URLs for links and HTTP redirect messages to all involved hosts, and these URLs don’t include the changed port number. All the while, the host names entered in the browser (and therefore in the “Host” HTTP header) must be what the systems expect, otherwise the Apache virtual host mapping and some of my own software breaks.
In the end I used a few /etc/hosts entries, multiple SSH tunnels, and the ipfw/natd firewalling/network address translation software included in Mac OS X to do it. I used this as an opportunity to learn about natd/ipfw, which were completely new to me. Since I still don’t really know what I’m doing in this area, you’re welcome to post improvements to this setup in the comments :-)
The setup works like this:
- I need to access port 80 on three hosts, let’s call them hosta.example.com, hostb.example.com and hostc.example.com
- Three different SSH tunnels are set up to these three hosts/ports, listening on the local ports 10082, 10083, 10084
- Since there are no publicly visible DNS records for the hosts, their names are added to the /etc/hosts file. We assign the names to additional 127.0.0.x addresses, which we activate on the loopback interface.
- Firewall rules are set up to divert traffic to the three new addresses to three separate natd processes. Each one rewrites packets for a particular host so they get sent to the corresponding SSH tunnel. When the response packets arrive out of the tunnel, the mapping is reversed.
The additions in the /etc/hosts file look like this:
127.0.0.2 hosta.example.com 127.0.0.3 hostb.example.com 127.0.0.4 hostc.example.com
The script I use to set up everything looks like this:
function cleanup {
echo "cleaning up..."
killall natd
ipfw -f flush
perl -p -i -e 's/^(127.0.0.(2|3|4).+)/#\1/g' /etc/hosts
sysctl -w net.inet.ip.fw.verbose=0
exit
}
trap cleanup INT
perl -p -i -e 's/^#(127.0.0.(2|3|4).+)/\1/g' /etc/hosts
sysctl -w net.inet.ip.fw.verbose=1
killall natd
ipfw -f flush
for i in 2 3 4; do
ifconfig lo0 127.0.0.$i alias
ipfw add divert 1000$i log tcp from me to 127.0.0.$i
ipfw add divert 1000$i log tcp from 127.0.0.$i 1008$i to me
natd -l -port 1000$i -interface lo0 \
-proxy_only -proxy_rule port 80 server 127.0.0.$i:1008$i
done
ssh -g -a -x -N -T \
-L10082:hosta.example.com:80 \
-L10083:hostb.example.com:80 \
-L10084:hostc.example.com:80 \
mliyanage@gateway.example.com
When I start it, it stays in the foreground. As soon as I’m finished, I hit Ctrl-C and it cleans up everything.
Hi Sascha :-)
Well this was really more of an exercise to learn about ipfw/natd. Accessing the servers was only an excuse to tinker with something new...
The SOCKS solution is cool and works really well in Firefox, but I cannot get it to work in Safari. Maybe a SOCKS v5/v4 issue? Firefox allows me to choose v4.
Never mind, the solution is here: http://www.dribin.org/dave/blog/archives/2004/11/22/ssh_socks/
|



Uah! Why don't you just:
ssh -D 4000 remote.server.tld
And then set one of your web browsers up to Socks proxy on 127.0.0.1:4000. Viola, problem solved, all web browsers in the "remote" subnet are normally reachable.