Reverse SSH through 3G/NAT

When the Raspberri Pi is connected via the 3G/UMTS network (how to do this) you can no longer ssh into it because 3G uses NAT. Only the Pi itself can open a connection to other machines. Fortunately we can use a feature built into ssh called “remote port forwarding” which works like this:

The Pi’s ssh client connects to your Linux desktop’s ssh server and tells it to forward connections from a local local port (e.g. 2222) back to a remote port on the Pi. In this case the remote port is 22 so we can connect to the Pi’s ssh server. The first connection is the tunnel through which the second connection in the reverse direction is established. Each side acts both as client and server.

If your desktop doesn’t run Linux you can simply use another Pi! :-) There probably are ssh servers for Windows that support port forwarding – contact me if you’re aware of one.

We’ll relax security a bit to make things work smoothly:

  • On the Pi we will store the password to be used for the desktop’s ssh server in plaintext. Do not use this password for any other account. On the desktop, restrict the user account so that it can’t do anything harmful. Be aware that if someone steals or breaks into the Pi they can use these credentials to ssh into your desktop. (This would be the same for certificate based authentication, so it doesn’t help us here.)
  • We’ll also disable strict host key checking on the Pi so the connection doesn’t break when your desktop’s host key changes.

Unless you have a static IP you’ll need to configure your router to use a dynamic DNS service such as (or if you use a FritzBox). Set up port forwarding in the router (cf. Wikipedia’s list of ports). Let’s assume your dynamic DNS address is and you are forwarding port 12345 to port 22 on your dekstop.

To tighten the setup of your desktop’s ssh server you might want to restrict the users that may log in at all. For example, to allow only the john account add this line at the top of /etc/ssh/sshd_config:

AllowUsers john

Test your setup by ssh-ing from the Pi to your desktop:

ssh -p 12345

Next install sshpass on the Pi. sshpass avoids the interactive ssh password prompt so the connection can be established automatically.

sudo apt-get install sshpass

Now initiate the reverse ssh connection from the Pi:



sshpass -p "$PASSWORD" \
ssh -o ServerAliveInterval=60 \
    -o ServerAliveCountMax=2 \
    -o StrictHostKeyChecking=no \
    -o UserKnownHostsFile=/dev/null \
    -o ConnectTimeout=15 \
    -N -R 2222:localhost:22 $REMOTE_ADDRESS -p $REMOTE_PORT

See here and here for a documentation of the options used above. The ServerAlive... options make sure that when the desktop is shut down the connection is also shut down. This is required so that it can be re-established when the desktop is up again. The ...Host... options avoid key verification failures when your desktop’s dynamic IP changes.

On the desktop you can now connect to the Pi as user pi:

ssh pi@localhost -p 2222

Of course we really want the Pi to initiate the connection automatically. So we wrap the above call in a loop and add an @reboot line to the crontab to run this script at startup (the code below is copied from in the ip-anemometer repository):



if [ "$#" != "3" ]; then
 echo "Maintain a reverse ssh connection, forwarding port $FWD_PORT on the remote machine."
 echo "If the connection fails or is dropped, wait $WAIT_SECONDS seconds and retry."
 echo "Usage: $(basename $0) [remote_user@]remote_server port password"
 echo "Then, on the remote ssh server: ssh $USER@localhost -p $FWD_PORT"


# By default the tunnel never seems to timeout. This is bad because if the connection to the
# server has been established once, then the server disconnects (link down, or maybe it's a laptop
# that doesn't run 24/7), it could never be re-established. So it's important to set a timeout for
# the tunnel. Note that keepalive is handled transparently by ssh; it does not mean any payload data
# has to be sent through the tunnel at these intervals.

while true; do
 sshpass -p "$PASSWORD" \
 ssh -o ServerAliveInterval=$SERVER_ALIVE_INTERVAL \
 -o ServerAliveCountMax=$SERVER_ALIVE_COUNT_MAX \
 -o StrictHostKeyChecking=no \
 -o UserKnownHostsFile=/dev/null \
 -o ConnectTimeout=15 \

The crontab entry:

@reboot ./ 12345 johnspassword

Keep in mind that after starting up the desktop you’ll have to wait up to a minute (or whatever interval you chose) before the connection is established. In my experience there is no harm in setting WAIT_SECONDS as low as 60 – it doesn’t use up bandwidth, doesn’t bog down the system or cause any other trouble.

2 thoughts on “Reverse SSH through 3G/NAT

  1. Thanks for this! Really easy to follow, the only thing that I did differently is added the sshpass as a service instead of in crontab.

    The command is the same as yours :
    /usr/bin/sshpass -p “password” ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=2 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=15 -N -R 22222:localhost:22 -p 1234

    And I created a simple service using my own instructions at :

  2. Hi,
    thx for the interesting tips! I’ll suggest to add this script to a cronjob every minute instead of your 60 seconds loop. If your script crashes for any reason you’ll have to go to the site to get it work again… see best practices between loops and cronjobs especially on Pis

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.