{"@attributes":{"version":"2.0"},"channel":{"title":"DEV Community: Mark Blakeney","description":"The latest articles on DEV Community by Mark Blakeney (@bulletmark).","link":"https:\/\/dev.to\/bulletmark","image":{"url":"https:\/\/media2.dev.to\/dynamic\/image\/width=90,height=90,fit=cover,gravity=auto,format=auto\/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F182099%2F374004d8-e573-48ce-905d-7c9f97f9690a.jpeg","title":"DEV Community: Mark Blakeney","link":"https:\/\/dev.to\/bulletmark"},"language":"en","item":{"title":"Create a reverse SSH tunnel for remote access to a restricted Linux machine","pubDate":"Fri, 29 Jan 2021 06:30:20 +0000","link":"https:\/\/dev.to\/bulletmark\/create-a-reverse-ssh-tunnel-for-remote-access-to-a-restricted-machine-1ma0","guid":"https:\/\/dev.to\/bulletmark\/create-a-reverse-ssh-tunnel-for-remote-access-to-a-restricted-machine-1ma0","description":"<p>I did not find a clear modern description of this set-up so have written this post to help others.<\/p>\n\n<p>Say you have a Linux machine to which you want to <a href=\"https:\/\/www.openssh.com\/\">ssh<\/a> to but that machine is behind corporate or other firewalls etc which you have no control over, and thus you can not forward an external port for ssh etc. Let's call that the restricted machine and we assume you can make changes to it, e.g. locally at that machine. This post describes how you can set up a reverse ssh tunnel initiated and maintained from that restricted machine to a port on a Linux server elsewhere on the internet that you control. With the reverse tunnel in place, you can simply ssh to that port on your server to get a tunnelled ssh session back to the restricted machine through the restricted firewalls. Of course you should always check you have permission to do this with whoever controls the restricted network.<\/p>\n\n<p>Note that <a href=\"https:\/\/linux.die.net\/man\/1\/autossh\">autossh<\/a> was previously often used for this (and nearly all existing online tutorials about reverse ssh tunnels use <a href=\"https:\/\/linux.die.net\/man\/1\/autossh\">autossh<\/a>) but <a href=\"https:\/\/linux.die.net\/man\/1\/autossh\">autossh<\/a> is redundant nowadays since modern <a href=\"https:\/\/www.openssh.com\/\">openssh<\/a> versions can monitor link health and exit if the link fails, and <a href=\"https:\/\/systemd.io\/\">systemd<\/a> can then be used to restart the ssh tunnel. So <a href=\"https:\/\/linux.die.net\/man\/1\/autossh\">autossh<\/a> is not required, we can implement an automatically maintained ssh tunnel using standard <a href=\"https:\/\/www.openssh.com\/\">ssh<\/a> and <a href=\"https:\/\/systemd.io\/\">systemd<\/a> alone.<\/p>\n\n<p>In summary the steps are:<\/p>\n\n<ol>\n<li>Set up the restricted machine.<\/li>\n<li>Set up the server.<\/li>\n<li>Final set-up and test.<\/li>\n<\/ol>\n\n<h3>\n  \n  \n  Set up the restricted machine\n<\/h3>\n\n<p>On the restricted machine, create an ssh key pair in <code>\/etc\/sshtunnel\/<\/code> with an empty\/no pass-phrase:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>$ sudo mkdir -p \/etc\/sshtunnel\n$ sudo ssh-keygen -qN \"\" -f \/etc\/sshtunnel\/id_rsa\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Create\/paste the following <code>systemd<\/code> service file in <code>\/etc\/systemd\/system\/sshtunnel.service<\/code> and then edit it as needed for your specific server and ports:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>[Unit]\nDescription=Service to maintain an ssh reverse tunnel\nWants=network-online.target\nAfter=network-online.target\nStartLimitIntervalSec=0\n\n[Service]\nType=simple\nExecStart=\/usr\/bin\/ssh -qNn \\\n  -o ServerAliveInterval=30 \\\n  -o ServerAliveCountMax=3 \\\n  -o ExitOnForwardFailure=yes \\\n  -o StrictHostKeyChecking=no \\\n  -o UserKnownHostsFile=\/dev\/null \\\n  -i \/etc\/sshtunnel\/id_rsa \\\n  -R 9001:localhost:22 \\\n  sshtunnel@server.net -p 443\nRestart=always\nRestartSec=60\n\n[Install]\nWantedBy=multi-user.target\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>You may likely want to change some of the unlabelled <code>ssh<\/code> parameters above so they are described below:<\/p>\n\n<div class=\"table-wrapper-paragraph\"><table>\n<thead>\n<tr>\n<th>Parameter<\/th>\n<th>Description<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>9001<\/td>\n<td>Port on your server to which ssh clients will connect to.<\/td>\n<\/tr>\n<tr>\n<td>22<\/td>\n<td>Port you are running the ssh server on the restricted machine. Nearly always the default ssh port 22.<\/td>\n<\/tr>\n<tr>\n<td>server.net<\/td>\n<td>Internet hostname of your server. I actually use a dynamic DNS alias here (e.g. a hostname at <a href=\"http:\/\/www.duckdns.org\/\">duckdns.org<\/a>) so I can easily re-select a different target server.<\/td>\n<\/tr>\n<tr>\n<td>443<\/td>\n<td>Port on your server where the ssh server is running. Often the default 22 but 443 is recommended as outgoing connections are less likely to be blocked by the restricted firewalls. However, using 443 requires that your server does not have a https server running on that port. Of course, if you use 443 then you must change your sshd configuration to listen on that port.<\/td>\n<\/tr>\n<\/tbody>\n<\/table><\/div>\n\n<h3>\n  \n  \n  Set up the server\n<\/h3>\n\n<p>On the server, create an account for the sshtunnel user but restrict it so that shell logins and remote commands are not allowed. The server will only allow the remote sshtunnel user to set up port forwarding:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>$ sudo useradd -m -s \/bin\/true sshtunnel\n$ sudo mkdir -p ~sshtunnel\/.ssh\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Copy the public ssh key you created above on the restricted machine (<code>\/etc\/sshtunnel\/id_rsa.pub<\/code>) to the sshtunnel user's authorized keys file on the server (<code>~sshtunnel\/.ssh\/authorized_keys<\/code>).<\/p>\n\n<p>Ensure the owner\/permissions are strictly set as ssh requires them.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>$ sudo chown -R sshtunnel:sshtunnel ~sshtunnel\/.ssh\n$ sudo chmod 700 ~sshtunnel\/.ssh\n$ sudo chmod 600 ~sshtunnel\/.ssh\/authorized_keys\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<h3>\n  \n  \n  Final set-up and test\n<\/h3>\n\n<p>On the restricted machine, enable and start the service:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>$ sudo systemctl enable --now sshtunnel\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Check the status and logs with:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>$ sudo systemctl status sshtunnel\n$ sudo journalctl -u sshtunnel\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Then on your server test the tunnel to the restricted machine with the following:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>$ ssh -p 9001 localhost\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>To diagnose problems, check the <code>sshd<\/code> logs on both the server and the restricted machine:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>$ sudo journalctl -u sshd\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<h3>\n  \n  \n  External public access via server (optional)\n<\/h3>\n\n<p>For most users, the above configuration is completely sufficient. The tunnel is available by making an ssh connection to the port on the server localhost interface. Either you connect to this locally from within that server, or if you are connecting from another machine then you use the ssh <a href=\"https:\/\/man.openbsd.org\/ssh_config#ProxyJump\"><code>ProxyJump<\/code><\/a> configuration keyword to automatically redirect to the restricted machine via the server proxy, again via the localhost interface.<\/p>\n\n<p>Some users may want to make the tunnel port also externally accessible on the server public network interfaces, e.g. for use by users who don't have ssh access to the server itself. Be aware you are opening a public port directly connecting to the restricted machine if you do this so this approach should be avoided, or at least disable password authentication and only allow <a href=\"https:\/\/www.ssh.com\/ssh\/key\/\">ssh key authentication<\/a> on the restricted machine.<\/p>\n\n<p>There are 2 extra steps to add this:<\/p>\n\n<ol>\n<li><p>On the server, enable <code>GateWayPorts yes<\/code> in <code>\/etc\/ssh\/sshd_config<\/code>. Then <code>sudo systemctl reload sshd<\/code> if you changed this.<\/p><\/li>\n<li><p>On the restricted machine, prepend a colon (':') on the port number in the <code>\/etc\/systemd\/system\/sshtunnel.service<\/code> file, so the <code>ssh<\/code> option is <code>-R :9001:localhost:22<\/code> rather than <code>-R 9001:localhost:22<\/code>. Then do <code>sudo systemctl daemon-reload<\/code> and <code>sudo systemctl restart sshtunnel<\/code>.<\/p><\/li>\n<\/ol>\n\n<h3>\n  \n  \n  Removal\n<\/h3>\n\n<p>To remove all the changes described here do the following.<\/p>\n\n<p>On the restricted machine:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>sudo systemctl disable --now sshtunnel\nsudo rm \/etc\/systemd\/system\/sshtunnel.service\nsudo rm -rf \/etc\/sshtunnel\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>On the server:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>$ sudo userdel -r sshtunnel\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Disable <code>GateWayPorts yes<\/code> in <code>\/etc\/ssh\/sshd_config<\/code> if you added it then <code>sudo systemctl reload sshd<\/code>.<\/p>\n\n","category":"ssh"}}}