At MIT, it's fairly trivial to get a static IP address (and subdomain under
mit.edu) for a specific MAC address connected to their wired Ethernet network
just by emailing the IT department. Having a static IP address is very
convenient for hosting websites and game servers, but more importantly it allows
easy access to my strong desktop computer from anywhere via SSH. Sadly, my
living situation has changed and I no longer have a simple static IP address for
There are two concerns to be addressed here: a static public IP address and port forwarding. Let's start with the former. I have recently moved into an apartment where there is only one choice of internet service provider. They offer a reasonable consumer plan of 100Mbit/s for $40/mo with a dynamic IP. Their cheapest business plan with a static IP is 50Mbit/s for $100/mo. Not a great deal.
Now, I could probably get by with purchasing a domain and reconfiguring DNS records every time my dynamic IP changed. For some situations, your IP address will only change when your ISP-provided modem has been disconnected for a long-enough time period, like during a sustained power outage. For other situations, like where your ISP employs carrier grade NAT, it may happen more frequently. Ultimately, I don't know how frequently my public IP address changes, and I'd prefer not to care.
Now onto the second concern: port forwarding. Regardless of my ISP's network situation, we have a wireless router in our apartment for connecting multiple devices. Thus, even if I can reliably determine our public IP address, I must configure our router to forward connections on certain ports to my computer. I don't control this router, and I'd prefer not to have to repeatedly badger my roommate whenever I want to test out new services. If my ISP did employ another layer of NAT, then this wouldn't help anyway.
Both of these concerns can be addressed simultaneously using a proxy and SSH reverse port forwarding.
A commonly used flag of
-L, which listens on a port of the local
machine and forwards traffic as if it originated from the remote machine. One
can also use the
-R flag to do the opposite: to listen on the remote machine
and forward traffic as if it originated on the local machine.
First we will need a proxy server. You can use any host, but Google appears to
be the only major cloud provider offering an indefinite free VM at this
time1. You'll have to search for another website to describe the in-depth
steps for setting up an
f1-micro instance on Google's cloud.
The basic steps for any cloud provider are as follows:
- Create the tiniest uninterruptible VM possible
- Add a small hard drive if necessary
- Select a linux distribution you're familiar with
- Configure an SSH key
- Configure the network rules (see below)
You have a choice when configuring network rules. If you use the server-optimized version of Ubuntu chosen by most providers, it will already come with a firewall that will block all ports besides ssh2. This is separate from your providers network rules, which can also block traffic destined for your VM. I would recommend setting your cloud provider's network rules to accept traffic from all IP addresses destined to any port. Then you only have to mess with the software firewall3.
Now you can launch the VM. It will be given a static IP address that will last until the VM is terminated. Given the reliability of modern cloud data centers, that means you now effectively have a permanent static IP.
Setting Up a Specific Port
Say you want to run a web server on port
80. First you need to set up the
proxy VM. This involves two things: opening the port in the software firewall
and also set up permissions for listening on that port. For Linux, a process
requires root privileges, or more specifically the
capability, to listen to a port below
1024. I went with just logging in as
root4, but in theory you can use the
setcap utility on
In order to log in as root, on my VM I ran:
sudo mkdir /root/.ssh/ sudo cp ~/.ssh/authorized_keys /root/.ssh/authorized_keys
You may also want to double check that your
Now you should open the port on your VM:
sudo iptables -w -A INPUT -p tcp --dport 80 -j ACCEPT
Now on your server in your local network, run the following:
ssh -N -R "*:80:localhost:80" firstname.lastname@example.org
root@ portion of the destination IP address if you chose to use the
-N flag says that SSH doesn't need to run a command on the destination; it
will remain connected indefinitely. The syntax of the
-R flag is
* means listen on
all network interfaces, the
port is the port to listen to on the proxy VM, and
hostport are the destination of the traffic once it has been
forwarded back to your local server. You can use the
-R flag multiple times to
listen on multiple ports simultaneously.
For a basic attempt at reliability in the face of network interruptions, I wrap that command in this small script:
#!/bin/bash while true; do echo connecting ssh -N -R "*:80:localhost:80" email@example.com wait 3 done
Realistically, this should probably be a systemd service, but that's for another day.
For the time being, I mostly want to connect to my server for SSH. The easiest
option is to forward a port like
8192 on the proxy VM to port
22 on my local
server. Then I can simply add a small entry to
~/.ssh/config and never worry about ports again:
Host MyServer HostName my.vm.ip.here Port 8192
Now I can log in simply using
ssh MyServer. The slightly more roundabout way
of achieving this is to make port
22 of the proxy forward to port
22 on my
server5. This requires first changing the what port the
on the proxy will listen to. I've chosen to make port
23 log in to the actual
VM whereas port
22 gets forwarded to my server.
First, open port
sudo iptables -w -A INPUT -p tcp --dport 23 -j
ACCEPT. Then add
Port 23 to
/etc/ssh/sshd_config and run
sudo service ssh
restart. Finally, change how you invoke the ssh reverse port forwarding as so:
ssh -N -R "*:22:localhost:22" firstname.lastname@example.org -p 23
This will drive
ssh a little crazy because it detects that it connected to a
different computer than expected. I suppose this is quite literally a
man-in-the-middle attack, which
ssh tries to prevent. You'll have to remove a
few lines from
~/.ssh/known_hosts to quell things.
You could chose the other way around: to set the software firewall to accept all traffic and to then filter things using your provider's network firewall. Google's web interface is terribly slow so I have not chosen this. One could also just set both firewalls to accept all traffic. Probably not a good idea. ↩
Only because I did not pay attention to the capability aspect when I first encountered this issue. ↩
This is what I actually use because I wanted to allow somone easy access to my desktop and I wanted it to be as convenient as possible. ↩