For this we need SSH client, server with public IP address (any computer with public IP and OpenSSH daemon), shell account on this server and administrator willing to change default SSH daemon configuration :)
We almost certainly need to set this tunnel up from script and therefore we need to generate a key with ssh-keygen
utility (distributed along with OpenSSH client). It is recommended to generate a new key for any script.
ssh -R *:2222:127.0.0.1:22 -f -g -n -N user@server.tld -o PreferredAuthentications=publickey
This command will forward traffic on port 2222 of public server to our computer. user@server.tld
is our shell account. Not all arguments in the command above are necessary, you can look them up in manual and tweak it.
To actually make it work you need to change one option in /etc/ssh/sshd_config
:
GatewayPorts yes
Here’s explanation from manual (sshd_config(5)
):
GatewayPorts
Specifies whether remote hosts are allowed to connect to ports
forwarded for the client. By default, sshd binds remote port
forwardings to the loopback address. This prevents other remote
hosts from connecting to forwarded ports. GatewayPorts can be
used to specify that sshd should allow remote port forwardings to
bind to non-loopback addresses, thus allowing other hosts to con‐
nect. The argument may be “no” to force remote port forwardings
to be available to the local host only, “yes” to force remote
port forwardings to bind to the wildcard address, or
“clientspecified” to allow the client to select the address to
which the forwarding is bound. The default is “no”.
It is needed to enable this to allow connections from computers other than server. Anyway, you can always connect through shell account.
This setup is also working with server running Windows using CopSSH (standalone Cygwin OpenSSH) or Cygwin with OpenSSH.
I have written a small Perl script which keeps the tunnels alive (use with cron):
#!/usr/bin/perl -w
# keeps configured tunnels alive
# from http://wiki.disorder.sk/
@tunnels = (
'ssh -R *:2222:127.0.0.1:22 -f -g -n -N user@server.tld -o PreferredAuthentications=publickey',
'ssh -R *:8080:127.0.0.1:80 -f -g -n -N user@server.tld -o PreferredAuthentications=publickey'
);
# start all
sub start
{
for ($i=0; $i<@tunnels; $i++) {
system $tunnels[$i] . ' \#magic-comment';
}
}
# kill all
sub killem
{
my @pids = qx 'ps ax | grep \'magic-comment\' | grep -v grep | awk \'{ print $1 }\'';
foreach $i (@pids) {
system("kill $i");
}
}
# if server is not running, kill them and don't bother
if (system('echo | nc server.tld 22') != 0) {
&killem;
exit;
} else {
# so the server is up, are all tunnels alive?
if (qx 'ps ax | grep \'magic-comment\' | grep -v grep -c' != @tunnels) {
&killem;
&start;
}
# custom hack - now tunnel to our ssh never dies...
if (system('echo | nc server.tld 2222') != 0) {
&killem;
&start;
}
}