aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/_posts/2020-02-24-ssh-tunnel-windows.md
diff options
context:
space:
mode:
Diffstat (limited to '_posts/2020-02-24-ssh-tunnel-windows.md')
-rw-r--r--_posts/2020-02-24-ssh-tunnel-windows.md154
1 files changed, 154 insertions, 0 deletions
diff --git a/_posts/2020-02-24-ssh-tunnel-windows.md b/_posts/2020-02-24-ssh-tunnel-windows.md
new file mode 100644
index 0000000..8b947e4
--- /dev/null
+++ b/_posts/2020-02-24-ssh-tunnel-windows.md
@@ -0,0 +1,154 @@
+---
+title: Persistent SSH tunnel
+excerpt: ... using Cygwin.
+---
+SSH tunneling is awesome.
+For some reason, I've only recently learned about this feature, but I've been
+immediately blown away by how useful it can be.
+
+Basically, to use SSH tunneling (a.k.a. port forwarding) you need to have a SSH
+client (`ssh`) with an access to a SSH server.
+You can then access any port on any host your SSH server has access to.
+It works like this:
+
+* your SSH client establishes a connection with the SSH server,
+* the client asks the server to forward incoming requests to the destination
+host,
+* the client listens to the proxy port on the local machine, and forwards
+requests to the SSH server.
+
+Say, you have access to SSH server `gateway` on port 22, and you want to gain
+access to HTTPS server `dest` on port 443, which is only accessible from the
+network both it and the SSH server belong to.
+You can the run something like
+
+```
+ssh -L 4433:dest:443 gateway -p 22
+```
+
+And now you can access `dest` at `https://localhost:4433/`.
+That's brilliant, really.
+
+But there's more.
+You can make a _reverse_ tunnel, allowing you to give access to any host your
+client computer has access to, via a remote SSH server.
+It works like this:
+
+* your SSH client establishes a connection with the SSH server,
+* the client asks the server to listen to a port of your choosing and forward
+incoming requests to the client,
+* the client forwards incoming requests to the destination host.
+
+This, as I've recently learned, is a common pattern to subvert corporate
+firewalls, which frequently forbid incoming connections.
+Say, you want to access your work computer from home via RDP.
+Both your home and your work computers have access to a SSH server `gateway` on
+port 22 (you might want to change it to port 80 or 443 if your outside
+connections are filtered).
+
+You can then run something like (notice the `-R`)
+
+```
+ssh -R 13389:127.0.0.1:3389 gateway -p 22
+```
+
+and now you can connect to `gateway:13389` from your home computer using a RDP
+client.
+Even more brilliant!
+
+You might need to set the `GatewayPorts` setting to `yes` or `clientspecified`
+on your SSH server (typically in "/etc/ssh/sshd_config").
+
+Batch mode
+----------
+
+If you want to establish a reverse SSH tunnel automatically, some tweaking is
+required.
+First, set some SSH client options:
+
+* `-F /dev/null` to disregard the user config,
+* `-oBatchMode=yes` to run non-interactively,
+* `-oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null` to disable server
+verification (optional),
+* `-oExitOnForwardFailure=yes` to exit if port forwarding fails,
+* `-oServerAliveCountMax=3 -oServerAliveInterval=15` to break the connection if
+the server or the network is down,
+* `-N -n -T` to only forward the ports and not execute the shell or any
+additional commands.
+
+Thus, the full command would be something like
+
+```
+ssh \
+ -F /dev/null \
+ -oBatchMode=yes \
+ -oStrictHostKeyChecking=no \
+ -oUserKnownHostsFile=/dev/null \
+ -oExitOnForwardFailure=yes \
+ -oServerAliveCountMax=3 \
+ -oServerAliveInterval=15 \
+ -N -n -T \
+ -R 13389:127.0.0.1:3389 \
+ user@gateway -p 22 \
+ -i ~/.ssh/tunnel
+```
+
+Adjust the `user@gateway -p 22` part accordingly.
+
+Notice also `-i ~/.ssh/tunnel`.
+It's the path to the SSH key used to authenticate with the server.
+It can't have a passphrase, since the command will be run non-interactively,
+and the public key must be in the server's authorized_keys file.
+
+For best results, you should also adjust some settings on the SSH server.
+Namely, you should enable client keep-alives on the server using something like
+
+```
+ClientAliveCountMax 3
+ClientAliveInterval 15
+```
+
+Unless you do that, even if the client breaks the connection, you won't be able
+to re-establish it for a long-ish time, since the server wouldn't know that the
+original connection is no longer valid.
+
+As a service
+------------
+
+Cygwin is awesome.
+I've been using for 10+ years, and it has never failed me.
+It comes with a SSH server, a client (you need to install the `openssh` package
+for both of these), and a service manager, `cygrunsrv`.
+`cygrunsrv` is similar to [NSSM], as it allows to wrap any executable into a
+native Windows service.
+
+[NSSM]: https://nssm.cc/
+
+Using `cygrunsrv`, you can create a Windows service to establish a reverse SSH
+tunnel automatically.
+
+```
+cygrunsrv \
+ -I ssh_tunnel \
+ -p /usr/bin/ssh \
+ --args '-F /dev/null -oBatchMode=yes -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oExitOnForwardFailure=yes -oServerAliveCountMax=3 -oServerAliveInterval=15 -N -n -T -R 13389:127.0.0.1:3389 user@gateway -p 22 -i ~/.ssh/tunnel' \
+ --disp 'Reverse SSH tunnels' \
+ --user user \
+ --neverexits \
+ --preshutdown
+```
+
+Adjust the `--user` and the `--args` values accordingly.
+
+You can then run `services.msc` and adjust the recovery settings for the
+service to restart if `ssh` fails:
+
+<div class="row">
+ <div class="col-xs-12 col-sm-8 col-md-6">
+ <a href="{{ site.baseurl }}/img/ssh_tunnel_services.png" class="thumbnail">
+ <img class="img-responsive" alt="services.msc" src="{{ site.baseurl }}/img/ssh_tunnel_services.png">
+ </a>
+ </div>
+</div>
+
+And voilĂ , you have an automatic reverse SSH tunnel on Windows for you!