58

I need to show somebody a website running on my local machine tomorrow. Normally I'd accomplish this by port forwarding on my local router but thanks to failing hardware and its replacement being awful, my current router doesn't let me do port forwarding.

So stuck with this delay and not wanting to push the whole thing onto a proper server, I had a crazy idea: Can I just forward my port to an external server over SSH?

I've done port tunnelling before but I usually do it the right way around:

  • I connect to a remote box and ask that port 12345 shows up on my local machine at port 12345.
  • I start something on P12345 on the remote machine
  • I can access it via localhost:12345

What I want to do:

  • Connect to a remote PC and ask that that its local P12345 fetch things from my local P12345 (over the tunnel)
  • I start something on my local computer on P12345
  • Other people can access remote:12345 and see my localhost:12345
95

The command for forwarding port 80 from your local machine (localhost) to the remote host on port 8000 is:

ssh -R 8000:localhost:80 oli@remote-machine

This requires an additional tweak on the SSH server, add the lines to /etc/ssh/sshd_config:

Match User oli
   GatewayPorts yes

Next, reload the configuration by server executing sudo reload ssh.

The setting GatewayPorts yes causes SSH to bind port 8000 on the wildcard address, so it becomes available to the public address of remote-machine (remote-machine:8000).

If you need to have the option for not binding everything on the wildcard address, change GatewayPorts yes to GatewayPorts clientspecified. Because ssh binds to the loopback address by default, you need to specify an empty bind_address for binding the wildcard address:

ssh -R :8000:localhost:80 oli@remote-machine

The : before 8000 is mandatory if GatewayPorts is set to clientspecified and you want to allow public access to remote-machine:8000.

Relevant manual excerpts:

ssh(1)

-R [bind_address:]port:host:hostport
Specifies that the given port on the remote (server) host is to be forwarded to the given host and port on the local side. This works by allocating a socket to listen to port on the remote side, and whenever a connection is made to this port, the connection is forwarded over the secure channel, and a connection is made to host port hostport from the local machine. By default, the listening socket on the server will be bound to the loopback interface only. This may be overridden by specifying a bind_address. An empty bind_address, or the address ‘*’, indicates that the remote socket should listen on all interfaces. Specifying a remote bind_address will only succeed if the server's GatewayPorts option is enabled (see sshd_config(5)).

sshd_config(5)

GatewayPorts
Specifies whether remote hosts are allowed to connect to ports forwarded for the client. 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 'clientspecified' to allow the client to select the address to which the forwarding is bound. The default is 'no'.

See also:

  • 7
    GatewayPorts was the magic bullet here. I like that you've found a version that can allow me to limit this fairly powerful technique to certain users. – Oli Jun 22 '11 at 21:57
  • 1
    What was confusing to me was that the GatewayPorts must be set on the machine that runs the actual ssh command to initiate the reverse tunnel (local). In my mind it was somehow more logical that the other end (remote) would deal with where to accept connections from, since thats where you connect to and are redirected from. Took me ages to get around that. – Ars Magika Feb 6 '14 at 13:21
  • 2
    @ArsMagika I am not sure if I am understand you. GatewayPorts must be set on the SSH server, not on your local machine that runs the ssh command. It configures whether other clients can communicate to the forwarded ports on the server. – Lekensteyn Feb 6 '14 at 14:10
  • Downside of this solution: you lose the ability to determine the client's IP address in the web server's access log. Any way to solve that nuisance as well? – Twonky Apr 23 '17 at 13:25
  • @Twonky If you do not need to know the exact IP address of the client (just that it was a remote one and not "localhost"), and your local server is listening on the wildcard address (i.e. accepting connections from any address), then you could try forwarding to -R :8000:127.0.1.1:80 (or any other 127.x.x.x address). Otherwise, no, you won't be able to learn the remote IP address. – Lekensteyn Apr 23 '17 at 21:56
14

If the server has GatewayPorts no, you can achieve the same result by executing ssh -g -L 8001:localhost:8000 oli@remote-machine on the server once you have executed ssh -R command on the client. This will make loopback port 8000 on the server accessible on all interfaces on port 8001.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.