Using SSH to Connect to Your Raspberry Pi Over The Internet
2019-04-22 - By Robert Elder
In this article, we will discuss one method of using SSH to remotely log into another computer from anywhere that you can access the internet. There are actually many different ways to accomplish this task in general, but this article will focus on showing you a method that involves setting up a proxy server with your favourite cloud provider, and then tunneling the connection to the Raspberry Pi through the proxy server, and down to your Raspberry Pi using SSH and port forwarding.
However, before you rush off to set this up, I recommend that you make sure you know what you're doing when setting up this kind of configuration. If you do something incorrectly, you could be exposing you and your data to every hacker out there on the internet! This is because the proxy (the one that you setup and control) will need to be accessible via a public IP on the open internet.
In order to get started with this technique, you will have to set up your own proxy server using one of the many popular cloud providers. This is something you will have to pay for each month, but a service like Amazon Web Services is fairly cheap if you go with a small sized instance. It will likely cost you less than $5.00 per month to set one up. It doesn't have to be powerful, it just needs to be a simple machine with network access and Linux installed. To give you an idea of what the setup will look like once you have it up and running, here is an illustration:
The setup described in the picture above is one where the laptop and the Raspberry Pi are in different countries and have their own local LAN connections. Both of them connect to the internet through their own router on their own LAN (not the same router as might be suggested by the way the pictures were cropped). Both the Laptop and the Raspberry Pi will set up their own SSH connection to the proxy. In this example, we will assume that the proxy server has public IP address '123.123.123.123'.
In order to achieve the final solution depicted above, you must create 3 SSH key pairs. The first is created on your Laptop with the public key added to '~/.ssh/authorized_keys' on the proxy server ('my-proxy-keypair-laptop.pub'). The second is created on the Raspberry Pi with the public key added to '~/.ssh/authorized_keys' on the proxy server ('my-proxy-keypair-pi.pub'). The third is created on the Laptop with the public key added to '~/.ssh/authorized_keys' on the Raspberry Pi ('my-first-keypair.pub'). If you need to refresh your memory about how to set up SSH keypairs, see the article How To Set Up and Distribute SSH Key Pairs.
Let's review the three SSH commands that you'd need to run to set up a connection from your laptop to the Raspberry Pi through the proxy if you did it manually every time you wanted to set up the tunnel:
# Represented by the Green arrow in the picture. (Run this command from Laptop)
# Forward the Laptop's incomming traffic to port 2222 through the
# tunnel and send it to 127.0.0.1:2223 once inside the proxy server.
ssh -L 2222:127.0.0.1:2223 -i ~/.ssh/my-proxy-keypair-laptop admin@123.123.123.123
# Represented by the Red arrow in the picture. (Run this command from Raspberry Pi)
# SSH into to the proxy server, then forward any incoming traffic destined
# for port 2223 (on the proxy server), down the tunnel, and send it to
# port 22 on the Raspbery Pi (127.0.0.1:22)
ssh -R 2223:127.0.0.1:22 -i ~/.ssh/my-proxy-keypair-pi admin@123.123.123.123
# Represented by the Yellow arrow in the picture. (Run this command from Laptop)
# Open an SSH connection, but try to connect to 127.0.0.1:2222 with the
# user 'pi'. Since the laptop's port 2222 is forwarded onto the proxy
# servers's port 2223, and port 2223 on the proxy is forwarded back down
# to the Raspberry Pi, this forwards the SSH connection request down to
# the Raspberry pi.
ssh -p 2222 -i ~/.ssh/my-first-keypair pi@127.0.0.1
Pay special attention to the red, green and yellow arrows in the picture. Each of them corresponds to a different SSH command that is shown above. These three commands show how you can connect to the Raspberry Pi over the internet by typing these commands manually on the command-line, but what we want is a more automatic solution. We also need to set up ssh configs that let us SSH directly onto the pi without having to manually set up the dependent tunnel and port forwarding rules. Furthermore, we need a way to make sure that the Raspberry Pi keeps trying to connect to the proxy server even if the power goes out, the local internet goes down, or some other event occurs that disrupts connectivity. Here is an equivalent ssh config file you can use on your laptop:
Host my-proxy
HostName 123.123.123.123
Port 22
User admin
IdentityFile ~/.ssh/my-proxy-keypair-laptop
LocalForward 2222 127.0.0.1:2223
Host pi-backup
HostName 127.0.0.1
Port 2222
User robert
IdentityFile ~/.ssh/my-first-keypair
ProxyJump my-proxy
Putting this in your ~/.ssh/config file on your laptop will cause the SSH tunnel to the proxy to be automatically set up whenever you try to ssh to 'pi-backup'. These two host entries will take care of the first half of the green arrow and half of the yellow one. However, we still have to do something on the Raspberry Pi to allow incoming connections that come through the proxy server. For this, you can create a ~/.ssh/config rule on the Raspberry Pi:
Host my-proxy
HostName 123.123.123.123
User admin
Port 22
IdentityFile ~/.ssh/my-proxy-keypair-pi
ServerAliveInterval 60
The above SSH host rule will set up a connection to the proxy server, but we haven't included the directive to make it actually listen for incoming connections from our laptop yet. For that part, we'll include it as part of a script that can run regularly to make sure that the tunnel is active and working:
#!/bin/bash
LOCAL_SSH_PORT=22
REMOTE_SSH_PORT=2223
LOCAL_SSH_ADDRESS=127.0.0.1
REMOTE_SSH_ADDRESS=127.0.0.1
PROXY_HOST=my-proxy
SSH_COMMAND="ssh -q -N -R ${REMOTE_SSH_ADDRESS}:${REMOTE_SSH_PORT}:${LOCAL_SSH_ADDRESS}:${LOCAL_SSH_PORT} ${PROXY_HOST}"
LOG_FILE=~/.ssh/tunnel-log.log
# Do a request to a site we own regularly so we can remotely check if the Raspberry Pi is alive and what it's public IP is.
curl https://www.example.com/?rpi_checkin=true > /dev/null 2>&1
# Check if there was a tunnel launched with the same command, otherwise, start one
if [ $(pgrep -f -x "$SSH_COMMAND" | wc -l) -eq 0 ]; then
$SSH_COMMAND &
PREV_PID=$!
echo "$(date): No tunnel was active: starting tunnel with pid: ${PREV_PID}." >> "${LOG_FILE}"
fi
MATCHING_LISTENS=$(ssh ${PROXY_HOST} "netstat -an" | egrep "tcp.*${REMOTE_SSH_ADDRESS}:${REMOTE_SSH_PORT}.*LISTEN")
LISTEN_CHECK_RTN=$?
# Count listens using awk, wc -l won't work if there is no trailing newline.
NUM_MATCHING_LISTENS=$(echo -en "${MATCHING_LISTENS}" | awk 'END{print NR}')
# Re-start the tunnel if we can't ssh into the remote and verify that the tunnel is working
if [ ${LISTEN_CHECK_RTN} -ne 0 ] ; then
pkill -f -x "$SSH_COMMAND"
$SSH_COMMAND &
PREV_PID=$!
echo "$(date): Failed to check for active tunnel, restarted tunnel. New pid: ${PREV_PID}." >> "${LOG_FILE}"
elif [ ${NUM_MATCHING_LISTENS} -lt 1 ] ; then
pkill -f -x "$SSH_COMMAND"
$SSH_COMMAND &
PREV_PID=$!
echo "$(date): No matching listens found in proxy server, restarted tunnel. New pid: ${PREV_PID}." >> "${LOG_FILE}"
else
echo "$(date): Tunnel appears to be active on remote, do nothing." >> "${LOG_FILE}"
fi
You can put this script on the Raspberry Pi, and then run it from a cron job, or manually on the command-line. Every time the script runs, it will check to make sure there is an active SSH tunnel that is also forwarding traffic from 127.0.0.1:2223 on the proxy back down to port 22 on the Raspberry Pi. This script also does a couple other things like log what is happening (which is useful for debugging purposes), and it also regularly pings some other host that we control for reasons that will be explained later.
One security related note about using the ssh -R flag is that this command can pose a potential security risk: If you were to SSH into a remote host and then use -R to bind to a port that was listening on a public network interface, you could end up forwarding traffic from the open internet down to your Raspberry Pi. Because of this, the default SSH config usually doesn't let you use the -R flag to bind to interfaces other than localhost. This can be changed with the 'GatewayPorts' directive in /etc/ssh/sshd_config, but this is not recommended for what we're doing here. If you want to be ultra safe, you should ensure that you aren't listening on a public interface on the proxy server for the incomming Raspberry Pi connections. However, because the proxy server is going to be somewhere in the cloud, it will have to be listening on a public interface for SSH connections to the proxy server iteself! To mitigate the risk of hacking attempts on the proxy server, I would strongly recommend setting up firewall rules to explicitly whitelist both your own IP address, and the IP address of where the Raspberry Pi is. What happens if your IP changes though? Well, you'll just have to update the firewall rule, or losen the security by only allowing IP addresses from a larger address range that is guaranteed to be on your ISP's network, but not open to the entire internet. When the IP address of the Raspberry Pi changes, you won't be present to check the IP address, so that's why the script listed above includes an 'rpi_checkin' curl request. If the Raspberry Pi address changes, you can check your logs on a simple web server that you set up somewhere (for example on the proxy server itself), and then see what IP it's talking to you from and white-list that.
How to Set Up A Proxy Server?
I haven't yet explained how to set up your proxy server if you decide you need to connect to your Raspberry Pi over the internet. The exact solution will depend on the cloud provider you use, but if you'd like an explanation of how to launch a simple server on Amazon Web Services, you can check out the guide on Amazon Cloud Servers For Beginners: Console VS Command-Line. This guide explains how to launch servers from the web interface and the command-line. You'll only need to launch the server once, so you can follow the instructions on how to launch a server through the web interface. You can ignore pretty much everything about using the 'AWS Command Line Interface', and the section 'Launching An Amazon Server Via The Command Line'. You should, however, follow the steps in the section 'Connecting To Your Server With SSH' to verify that you can actually connect to the server. This guide also shows you a bit of information about security groups in AWS, although you should consider using a rule that restricts SSH access to 'My IP address' instead of '0.0.0.0/0' as shown in the guide which would allow full public access to SSH login attempts.
Alternative Methods
There are alternative methods that can accomplish the same task described here, but these won't be covered by this article. Some of these can also lead to serious security risks if you're not careful in setting them up. Here are a list of concepts and terms that you can search for elsewhere online if you're curious:
- Set up SSH port forwarding rules with a home or office router.
- Use a VNC server.
- Use VPN Services.
A Guide to Recording 660FPS Video On A $6 Raspberry Pi Camera
Published 2019-08-01 |
$1.00 CAD |
An Overview of How to Do Everything with Raspberry Pi Cameras
Published 2019-05-28 |
DS18B20 Raspberry Pi Setup - What's The Deal With That Pullup Resistor?
Published 2019-06-12 |
A Beginners Guide to Securing A Raspberry Pi
Published 2019-04-22 |
Using SSH and Raspberry Pi for Self-Hosted Backups
Published 2019-04-22 |
Pump Room Leak & Temperature Monitoring With Raspberry Pi
Published 2019-06-20 |
A Surprisingly Common Mistake Involving Wildcards & The Find Command
Published 2020-01-21 |
Join My Mailing List Privacy Policy |
Why Bother Subscribing?
|