{"id":397,"date":"2025-01-30T21:12:22","date_gmt":"2025-01-30T21:12:22","guid":{"rendered":"https:\/\/www.aran.cat\/wp\/?p=397"},"modified":"2025-01-30T21:21:37","modified_gmt":"2025-01-30T21:21:37","slug":"how-to-setup-reverse-ssh-tunnel-on-raspberry-pi-behind-a-3g-usb-stick-modem","status":"publish","type":"post","link":"https:\/\/www.aran.cat\/wp\/?p=397","title":{"rendered":"How to Setup Reverse SSH Tunnel on Raspberry Pi behind a 3G USB Stick Modem"},"content":{"rendered":"\n<figure class=\"wp-block-embed is-type-wp-embed is-provider-it-react wp-block-embed-it-react\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"wp-embedded-content\" data-secret=\"lv1fhBXfir\"><a href=\"https:\/\/www.it-react.com\/index.php\/2020\/01\/06\/how-to-setup-reverse-ssh-tunnel-on-linux\/\">How to Setup Reverse SSH Tunnel on Raspberry Pi behind a 3G USB Stick Modem<\/a><\/blockquote><iframe loading=\"lazy\" class=\"wp-embedded-content\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; visibility: hidden;\" title=\"\u201cHow to Setup Reverse SSH Tunnel on Raspberry Pi behind a 3G USB Stick Modem\u201d \u2014 IT-REACT\" src=\"https:\/\/www.it-react.com\/index.php\/2020\/01\/06\/how-to-setup-reverse-ssh-tunnel-on-linux\/embed\/#?secret=Ev1moa438G#?secret=lv1fhBXfir\" data-secret=\"lv1fhBXfir\" width=\"600\" height=\"338\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"><\/iframe>\n<\/div><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">There are cases when a remote computer could be hard to reach. This computer could be placed behind a firewall or a router (NAT) whose rules or settings cannot be changed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">An easy and practical solution that could help us in this case is to set up a reverse SSH tunnel on Linux. Reverse SSH tunneling allows you to create a connection from the remote computer to a local computer and using this established connection to set up a new connection from your computer back to the remote computer. How can you achieve this, I\u2019ll explain you in the following lines.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Step One \u2013 Solution Design<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">My concrete case where I need this kind of implementation is a <a href=\"https:\/\/www.amazon.com\/gp\/product\/B07NY4WN6W\/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B07NY4WN6W&amp;linkCode=as2&amp;tag=itreact-20&amp;linkId=df8458ed3b9d5ac6d56b10caf7f16051\">Raspberry Pi<\/a> connected to Internet using a 3G USB dongle. The Raspberry Pi is used in a remote house to convert a classic alarm system to a smart alarm (with Internet and SMS functionality).<br><br>I\u2019ll write more about this project in a different post, for now let\u2019s focus on ssh tunnel subject, which is quite a challenging part.<br><br>Here is the design of the solution:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2020\/01\/Zeichnung1.png\" alt=\"\" class=\"wp-image-323\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">I would like to be able to access the <a href=\"https:\/\/www.amazon.com\/gp\/product\/B07NY4WN6W\/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B07NY4WN6W&amp;linkCode=as2&amp;tag=itreact-20&amp;linkId=df8458ed3b9d5ac6d56b10caf7f16051\" target=\"_blank\" rel=\"noreferrer noopener\">Raspberry Pi<\/a> from anywhere, not only from my local computer. For this solution we\u2019d need a Linux computer that will be anytime available and directly accessible from Internet. Or even better, we can choose a VPS (virtual private server) solution from one of the many providers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I am living in Germany and I chose a VPS solution from www.ionos.de. At the time I\u2019m writing this article, the solution is very affordable, only 1Euro\/month and you get 1Core CPU, 0,5GB and 20GB SSD. It is also very easy to set up and it has a very nice and intuitive interface.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Step two \u2013 Setting up a reverse SSH tunnel<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Note<\/strong>: We are working with root user, if you are not logged in as root, add sudo in the front of the commands.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We\u2019ll start by setting up the reverse SSH tunnel on the <a href=\"https:\/\/www.amazon.com\/gp\/product\/B07NY4WN6W\/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B07NY4WN6W&amp;linkCode=as2&amp;tag=itreact-20&amp;linkId=df8458ed3b9d5ac6d56b10caf7f16051\">Raspberry Pi<\/a> by typing in the following command:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># ssh -R 3333:localhost:22 user@xxx.xxx.xxx.xxx<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Note:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>3333 \u2013 the port used for the reverse SSH tunnel (port should be opened on VPS).<\/li>\n\n\n\n<li>22 \u2013 default ssh port of Raspberry<\/li>\n\n\n\n<li>user@xxx.xxx.xxx.xxx \u2013 user and IP address of the VPS, replace with your actual user and IP<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">I\u2019ve opened port 3333 in the Firewall on the management web-interface of the VPS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">My iptables on VPS has the default configuration, so is not dropping any packets. In this case we don\u2019t have to do any settings in iptables.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># iptables -L -vn\n<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">Chain INPUT (policy ACCEPT 39 packets, 4642 bytes)\n  pkts bytes target     prot opt in     out     source               destination\n Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)\n  pkts bytes target     prot opt in     out     source               destination\n Chain OUTPUT (policy ACCEPT 33 packets, 5915 bytes)\n  pkts bytes target     prot opt in     out     source               destination\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Step three \u2013 Connecting to the SSH tunnel from VPS<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">After we\u2019ve created the tunnel with the command at step 1, Raspberry can be accessed by executing the following command on the VPS:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># ssh localhost -p 3333<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In order to access Raspberry from another machine, we have to first ssh to the VPS<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># ssh user@xxx.xxx.xxx.xxx<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And then logging in Raspberry from the VPS:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># ssh localhost -p 3333<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Step four \u2013 Connecting to the SSH tunnel from another machine<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To open the SSH Tunnel directly to Raspberry from another machine, without connecting first to the VPS (actually the connection will also pass through VPS, but we\u2019ll not see anymore the VPS login), we have to add\/modify one variable in \/etc\/ssh\/sshd-config on the VPS.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># nano \/etc\/ssh\/sshd-config<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Look for GatewayPorts. If you found it and it looks like this <kbd>#GatewayPorts no<\/kbd>, delete \u201c#\u201d (to uncomment it) and change it to <kbd>GatewayPorts yes<\/kbd> . If the variable doesn\u2019t exist in the file, simply add <kbd>GatewayPorts yes<\/kbd> at the end of the file<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2020\/01\/sshd-config.jpg\" alt=\"\" class=\"wp-image-368\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">After you changed the file, we have to reload the sshd service.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># systemctl reload sshd.service<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now we can start a connection directly to Raspberry from a Windows machine by using this command in CLI, or using Putty:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&gt; ssh -p 3333 pi@xxx.xxx.xxx.xxx<\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2020\/01\/sshfromwindows-1.jpg\" alt=\"\" class=\"wp-image-384\"\/><\/figure>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2020\/01\/putty-reverse-ssh.jpg\" alt=\"\" class=\"wp-image-430\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">\u2026and we\u2019re in, now we can insert commands in Raspberry<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Step five \u2013 Creating a persistent SSH tunnel<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The tunnel we created so far won\u2019t be persistent and will be dropped if the connection of Raspberry drops, if we want to make our reverse SSH tunnel persistent we need to install autossh.<br>Here is the command to install autossh on Raspberry:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># apt-get install autossh<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We will use a script that will start automatically at boot and create the ssh tunnel. I\u2019ve found a script that can do our job pretty good and adapted to my needs. You can find it <a href=\"https:\/\/gist.github.com\/Clement-TS\/48ae8d23f6452cd1a3a071640c1bd07b\">here<\/a> . Thanks to the author.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To make the Reverse SSH Tunnel start at boot without our intervention, we need to set up ssh-key based authentication. This means that the connection will be created without being necessary to introduce the password.<br>How to achieve a ssh-key based authentication you can find <a href=\"https:\/\/www.it-react.com\/index.php\/2020\/01\/12\/create-and-install-ssh-keys-on-linux-and-windows\/\">here<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here is the original script:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#! \/bin\/sh\n# author: Cl\u00e9ment D\u00e9siles\n# date: 01\/08\/2016\n# source: https:\/\/gist.github.com\/suma\/8134207\n# source: http:\/\/stackoverflow.com\/questions\/34094792\/autossh-pid-is-not-equal-to-the-one-in-pidfile-when-using-start-stop-daemon\n\n### BEGIN INIT INFO\n# Provides:          autossh\n# Required-Start:    $remote_fs $syslog\n# Required-Stop:     $remote_fs $syslog\n# Default-Start:     2 3 4 5\n# Default-Stop:      0 1 6\n# Short-Description: autossh initscript\n# Description:       establish a tunnelled connexion for remote access\n### END INIT INFO\n\n. \/etc\/environment\n. \/lib\/init\/vars.sh\n. \/lib\/lsb\/init-functions\n\nTUNNEL_HOST=iam.perdu.com\nTUNNEL_USER=toto\nTUNNEL_PORT=42000\nMONITOR_PORT=42001\nKEY_PATH=\/home\/toto\/.ssh\/id_rsa\n\nNAME=autossh\nDAEMON=\/usr\/lib\/autossh\/autossh\nAUTOSSH_ARGS=\"-M $MONITOR_PORT -f\"\nSSH_ARGS=\"-nNTv -o ServerAliveInterval=60 -o ServerAliveCountMax=3 -o IdentitiesOnly=yes -o StrictHostKeyChecking=no \\\n         -i $KEY_PATH -R $TUNNEL_PORT:localhost:22 $TUNNEL_USER@$TUNNEL_HOST\"\n\nDESC=\"autossh for reverse ssh\"\nSCRIPTNAME=\/etc\/init.d\/$NAME\nDAEMON_ARGS=\" $AUTOSSH_ARGS $SSH_ARGS\"\n\n# Export PID for autossh\nAUTOSSH_PIDFILE=\/var\/run\/$NAME.pid\nexport AUTOSSH_PIDFILE\n\ndo_start() {\n\tstart-stop-daemon --start --background --name $NAME --exec $DAEMON --test &gt; \/dev\/null || return 1\n\tstart-stop-daemon --start --background --name $NAME --exec $DAEMON -- $DAEMON_ARGS    || return 2\n}\n\ndo_stop() {\n\tstart-stop-daemon --stop --name $NAME --retry=TERM\/5\/KILL\/9 --pidfile $AUTOSSH_PIDFILE --remove-pidfile\n\tRETVAL=\"$?\"\n\t&#91; \"$RETVAL\" = 2 ] &amp;&amp; return 2\n\tstart-stop-daemon --stop --oknodo --retry=0\/5\/KILL\/9 --exec $DAEMON\n\t&#91; \"$?\" = 2 ] &amp;&amp; return 2\n\treturn \"$RETVAL\"\n}\n\ncase \"$1\" in\n  start)\n\tlog_daemon_msg \"Starting $DESC\" \"$NAME\"\n\tdo_start\n\tcase \"$?\" in\n\t\t0|1) log_end_msg 0 ;;\n\t\t2) log_end_msg 1 ;;\n\tesac\n\t;;\n  stop)\n\tlog_daemon_msg \"Stopping $DESC\" \"$NAME\"\n\tdo_stop\n\tcase \"$?\" in\n\t\t0|1) log_end_msg 0 ;;\n\t\t2) log_end_msg 1 ;;\n\tesac\n\t;;\n  status)\n\tstatus_of_proc \"$DAEMON\" \"$NAME\" &amp;&amp; exit 0 || exit $?\n\t;;\n  *)\n\techo \"Usage: $SCRIPTNAME {start|stop|status|restart}\" &gt;&amp;2\n\texit 3\n\t;;\nesac<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Here are the install instructions:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">#<code>mv ~\/Downloads\/autossh.init.sh \/etc\/init.d\/autossh <\/code>\n#<code>chmod +x \/etc\/init.d\/autossh <\/code>\n#<code>update-rc.d -f autossh defaults 90 90 &gt; \/dev\/null 2&gt;&amp;1<\/code> <\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is the part I\u2019ve modified:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2020\/01\/autossh.jpg\" alt=\"\" class=\"wp-image-410\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The monitor port, which is 9923 in our case should also be opened in VPS.<br>Autossh will send test data on the monitoring port and receive it back on the port above. In our case, autossh will set up forwards so that it can send data on port 9923 and receive it back on 9924.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The autossh script should start at boot and re-connect in case the connection is dropping.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Update1: <\/strong>Please note in the previous screenshot that I\u2019m opening actually two ports for reverse tunneling, one is for ssh (port 22 ) and the other one is for node-red (port 1880). So if I open a browser and access the IP address of the VPS on port 8080, then the connection will be forwarded to the Raspberry Pi port 1880, and in this way I can access the node-red running on Pi.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If there is any Firewall running on the VPS or the provider of your VPS instance has a suplementary Firewall, you have to open that port (in our case port 8080).<br><br>More than that, if you have more Raspberry Pi boards in the same scenario, you can create a reverse tunelling on every Pi pointing to the same VPS, but using different ports on VPS. So if for the first Pi we used port 8080 of the VPS, for the second one we can use port 8081. When we acess http:\/\/VPS-IP:8080\/ui we\u2019ll land to the node-red dashboard of the first Pi, when access http:\/\/VPS-IP:8081\/ui we\u2019ll land to the node-red dashboard of the second Pi.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Update2<\/strong>: autossh will create the automatic connection with the help of ssh-keys. Please check <a href=\"https:\/\/www.it-react.com\/index.php\/2020\/01\/12\/create-and-install-ssh-keys-on-linux-and-windows\/\">here<\/a> how to generate the ssh-keys. Please see in script the path of the ssh-key: KEY_PATH=\/root\/.ssh\/id_rsa<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Step six \u2013 Troubleshooting Reverse SSH Tunnel<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You can check if the Reverse SSH Tunnel is started by issuing this command on VPS:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># netstat -lt<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">Active Internet connections (only servers)<br>\nProto Recv-Q Send-Q Local Address           Foreign Address         State<br>\ntcp        0      0 localhost:domain        0.0.0.0:*               LISTEN<br>\ntcp        0      0 0.0.0.0:ssh             0.0.0.0:*               LISTEN<br>\ntcp        0      0 0.0.0.0:9923            0.0.0.0:*               LISTEN<br>\ntcp        0      0 0.0.0.0:3333            0.0.0.0:*               LISTEN<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If you see here port 3333 and 9923 this means that Reverse SSH Tunnel is up and running.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If the ports above are not listening on VPS , you have to further investigate the problem on Raspberry side and check if the autossh service is running with the command:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># systemctl status autossh.service<\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2020\/01\/autossh-notrunning.jpg\" alt=\"\" class=\"wp-image-475\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">As you can see the service is active, but exited. This means that tunnel is not running. In this situation it helps to restart the service with the following command:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> # systemctl restart autossh.service <\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2020\/01\/autossh-running-1024x295.jpg\" alt=\"\" class=\"wp-image-478\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">We have suceeded, now the service is running and our tunnel is up.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>There are cases when a remote computer could be hard to reach. This computer could be placed behind a firewall or a router (NAT) whose rules or settings cannot be changed. An easy and practical solution that could help us in this case is to set up a reverse SSH [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[5],"tags":[],"class_list":["post-397","post","type-post","status-publish","format-standard","hentry","category-debian"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.aran.cat\/wp\/index.php?rest_route=\/wp\/v2\/posts\/397","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.aran.cat\/wp\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.aran.cat\/wp\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.aran.cat\/wp\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.aran.cat\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=397"}],"version-history":[{"count":1,"href":"https:\/\/www.aran.cat\/wp\/index.php?rest_route=\/wp\/v2\/posts\/397\/revisions"}],"predecessor-version":[{"id":398,"href":"https:\/\/www.aran.cat\/wp\/index.php?rest_route=\/wp\/v2\/posts\/397\/revisions\/398"}],"wp:attachment":[{"href":"https:\/\/www.aran.cat\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=397"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.aran.cat\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=397"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.aran.cat\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=397"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}