Reverse SSH tunneling, otherwise known as remote port forwarding via SSH. This is a way to connect to a machine by having the other computer call you first and then activating a second connection over the initial one in the opposite direction.
How SSH Remote Tunneling Works
To connect via SSH or some other service, such as HTTP, from the Internet into a machine behind a firewall, you need the machine in question to open an SSH connection to a machine outside the firewall and include an SSH -R tunnel whose “entry” point is the “remote” side of this connection.
This means, from the firewalled host, you need to execute something like:
ssh -f -N -T -R22000:localhost:22 yourpublichost.example.com
This instructs your client machine to establish a tunnel with an SSH -R remote entry point.
Anything that then attaches to port 22000 at the other end of the tunnel will be automatically forwarded to “localhost port 22”, where “localhost” is defined from the point of view of the exit point of the tunnel – namely your SSH client machine.
The other options here are:
-f instructs SSH to background itself after authentication. T
-N instructs SSH to create a connection, but without actually running any remote commands. This saves processing resources and time.
-T instructs SSH to disable pseudo-tty allocation, which is useful if you do not want an interactive command line shell.
Standard local port forwarding does not work if incoming SSH requests in the remote server are disabled. For security reasons, administrators might entirely block inbound SSH requests but allow outbound SSH requests.
In such situations, you can use remote port forwarding to create an outbound SSH connection and let the clients connect to the local port even if inbound connections are blocked.
Now, when users from distant internet visit port 80 of the remote server as http://<remote_server_ip>, the request is redirected back to the client’s local server (port 3000) via SSH tunnel where the local server handles the request and response.
By default, the remote port forwarding tunnel will bind to the localhost of the remote server. To enable it to listen on the public interface (for a scenario like above), set the SSH configuration GatewayPorts yes in sshd_config.
This is the command, set from the outgoing machine, in this case my laptop:
ssh -NR 8080:localhost:8081 kevin@kevwells.com
it will not return to the command line – that is correct behaviour.
To run a webserver over the tunnel you need to set it up to run on eg port 8081. You then set the tunnel to run on eg 8080.
Configure sshd_config
You need to set the following directives in /etc/ssh/sshd_config:
AllowTcpForwarding yes
GatewayPorts yes
Make sure you restart ssh service after editing the file: ie
systemctl restart ssh
You can then connect to the asus website using http://kevwells.com:8080
Make sure apache is running on 8081 on the localhost.
Points to Watch with SSH Reverse Tunneling
This only works here with http.
https will NOT work – since one cannot obtain an SSL/TLS certificate for a local NAT IP address – only for publicly accessible IPs ie domain names accessible without NAT.
By default, TCP listening sockets on the server are bound only to the loopback interface. This can be overridden by specifying a bind_address. An empty bind_address, or the address *, directs that the remote socket should listen on all interfaces.
If you specify a remote bind_address then this will only function if the server’s GatewayPorts option is enabled in sshd_config.
In addition, the GatewayPorts directive in the sshd_config on the server must be set.
To quote from the sshd_config man page:
This specifies whether remote hosts are allowed to connect to ports forwarded for the client. By default, sshd(8) 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 connect. 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 client specified to allow the client to select the address to which the forwarding is bound. The default is no.
Remember to restart the ssh server service after all config changes.
Make sure also that all ports in use are accessible through any firewall or routers.
Also if you are running a local apache webserver on the NAT machine, then make sure you have the respective port open in the ports.conf at /etc/apache2/ports.conf
in this example
LISTEN 8081
AND set a virtual hosts definition in your active sites-enabled config file for the port you are using for the webserver on the local machine ie:
(this definition is on the LOCAL NAT machine apache config)
<VirtualHost *.:8081>
You do NOT need any definition for this, nor for (in this case 8080 but it could be set differently) 8080 port on the receiving machine.
and be sure to restart the apache server on the 8081 port machine ie the local NAT machine after all config changes.
To test if a port is accessible, eg port 80:
you can use
nc -l 80
this starts a simple process to receive traffic on port 80:
root@gemini:/etc/ssh# nc -l 80
^C
root@gemini:/etc/ssh#
you can then test it from another machine:
root@asus:~# telnet kevwells.com 80
Trying 78.141.200.190…
Connected to kevwells.com.
Escape character is ‘^]’.
(you have to terminate the nc process in order to exit the telnet session)
You need root to listen on ports ≤1024, since you’re using -R and not -L the lack of permission is at the far end connection, thus running the SSH command as root won’t help.
If you use -R 8080:localhost:80 it will run, but obviously not listening on the standard http port.
However, attempting to do
ssh -R 80:127.0.0.1:8081 kevin@kevwells.com
will give the error message:
Warning: remote port forwarding failed for listen port 80
This is because you can’t bind on <1024 without using sudo. OpenSSH will refuse to bind to privileged ports unless the user id of the logged in user is 0 ie root.