RPi - exposing http and ssh to the internet

This is just a quick guide for my friend Gonza to describe how SSH tunnelling can let you connect to a Raspberry Pi (or any other box running a unix-like OS) from anywhere even if it's behind an internet connection with a dynamic IP. This lets you avoid bothering to set up dyndns or similar. What you need is:

  1. RPi on home network (we'll call this rpi)
  2. remote box running linux, with a known IP address (we'll call this remote)

What we're going to do is establish an SSH connection between rpi and remote that will remain up at all times, and which will re-establish the connection without any user intervention. We'll be authenticating using an SSH key, so if you don't already have one run the following, I think the defaults are ok:

    me@rpi~ $ ssh-keygen -t rsa

And upload it to your remote server, and test it works nicely:

    me@rpi~ $ ssh-copy-id me@remote-server.com
    me@rpi~ $ ssh remote-server.com
    me@remote~ $ 

Next we'll make sure the remote has sshd configured so that as a client connecting via SSH you can specify the ports involved - so open up /etc/ssh/sshd_conf in your favourite editor:

    me@remote~ $ sudo emacs -nw /etc/ssh/sshd_conf
And either uncomment or add the following line:
    GatewayPorts clientspecified
Now that we've got rpi able to connect to remote via SSH we'll setup SSH tunnelling between the two. What this means is that we'll nominate some ports on remote which will have any traffic forwarded directly to some given ports on rpi - in this case I'm using:

type rpi port remote port 
ssh 40022 22
http 40080 8080

This will mean that not only can I SSH to rpi, but there's another one I can use for running, say, some web site or service. To do this we'll use autossh which is responsible for establishing the connection and keeping it up:

    me@rpi~ $ sudo apt-get install autossh
    me@rpi~ $ sudo autossh -Nf -M 40980 -o "PubkeyAuthentication=yes" -o "PasswordAuthentication=no" -i /home/me/.ssh/id_rsa -R remote-server.com:40080:localhost:8080 me@remote-server.com
    me@rpi~ $ sudo autossh -Nf -M 40922 -o "PubkeyAuthentication=yes" -o "PasswordAuthentication=no" -i /home/me/.ssh/id_rsa -R remote-server.com:40022:localhost:22 me@remote-server.com

To test this out, we can run a simple server on our rpi using nc:

    me@rpi~ $ while true; do { echo -e "HTTP/1.1 200 OK\r\n"; date ; uname -a ; echo; echo; } | nc -l 8080; done

And we can cURL the IP address of remote, which will forward the request/response between your laptop and rpi - I've used xxx.yyy.zzz.www in place of the actual IP address:

    me@laptop~ $ curl xxx.yyy.zzz.www:40080
    Sun May  1 13:36:14 UTC 2016
    Linux rpi 3.2.0-4-amd64 #1 SMP Debian 3.2.41-2+deb7u2 x86_64 GNU/Linux
We can try out connecting via SSH:
    me@laptop~ $ ssh me@xxx.yyy.zzz.www -p 40020
    me@rpi~ $ 

To get this command to run after we reboot we can muck around with systemd, or we could create a cron job - the latter is easier, so let's do that:

    $ crontab -e
And enter
    @reboot autossh -Nf -M 40980 -o "PubkeyAuthentication=yes" -o "PasswordAuthentication=no" -i /home/me/.ssh/id_rsa -R remote-server.com:40080:localhost:8080 me@remote-server.com
    @reboot autossh -Nf -M 40922 -o "PubkeyAuthentication=yes" -o "PasswordAuthentication=no" -i /home/me/.ssh/id_rsa -R remote-server.com:40022:localhost:22 me@remote-server.com

And if you want things to be a little easier you could add the following to your ~/.ssh/config:

    Host rpi
        HostName xxx.yyy.zzz.www
        Port 40020
This lets you connect to ssh without having to remember the IP address and port, so you can connect like so:
    me@laptop ~$ ssh me@rpi
    me@rpi ~$

Update, 2016-09-08: There's actually an even simpler way to do this if you don't have a remote machine and a domain, you can expose a tor hidden service. The caveat is that you're only able to access it from within the tor network, which means you won't be able to access it from your iPhone.