SSH ProxyCommand

Here’s an exceedingly useful feature of SSH which I only discovered recently.

Imagine that you have a single ‘gateway’ machine on your network which you can connect to from outside using SSH; I do this all the time. You can then use that machine to connect to other machines inside your network in a variety of ways: using the port-forwarding abilities of SSH (the -L and -R options), for example, or simply by running another SSH command from the gateway machine once you’ve connected to it.

But there’s a much tidier way to do it, using the ProxyCommand option.

To connect to internalmachine.mynet.com, just add something like the following to your ~/.ssh/config:

Host internalmachine.mynet.com
     ProxyCommand ssh gateway.mynet.com exec nc %h %p

then you can ssh directly to internalmachine.mynet.com from outside. SSH will connect to the gateway machine and run ‘nc’ to forward the SSH session to the internal machine.

And, of course, you can use it for things layered over SSH, like checkouts from Git or Subversion repositories. Very tidy! I also sometimes add -C to the ssh command so that any access done this way is automatically compressed, even in situations where it was hard to specify that explicitly.

If you’re unlucky enough to find yourself stuck behind a web proxy with no other outgoing access, one very nice-looking use of ProxyCommand is the Corkscrew utility by Pat Padgett.

Hope this is helpful to someone!

Update: there are a few useful extra tips in the comments.

Enjoyed this post? Why not sign up to receive Status-Q in your inbox?

20 Comments

The *was* useful to someone :-). I knew about the ProxyCommand option, and that it was possible to use it to tunnel like this, but didn’t know how to do it; I didn’t know the bit about Netcat!

Thanks!

So just to clarify, the nc command expands to “nc internalmachine.mynet.com 22”, granting your local ssh access over stdin and stdout?

Yes, that’s right – the nc runs on the gateway machine you’re ssh-ing to, and effectively forwards the connection to the ssh port on the internal machine, so you appear to be connecting to that internal machine directly, especially if you have the appropriate keys installed on those machines so you don’t need passwords.

Great!

Small improvement: add “-e none”
That makes the first ssh connection fully transparent so it won’t break on control sequences.

i.e.
“ProxyCommand ssh -e none gateway.mynet.com exec nc %h %p”

Philipp

Thanks, Philipp – a useful hint.

It’s probably also useful to add a timeout to the nc command, otherwise, when the ssh goes away, the nc can be left lying around on the gateway machine.

I now use

Host internalmachine.mynet.com
     ProxyCommand ssh -e none gateway.mynet.com exec nc -w 5 %h %p

@qsf: Thanks! I knew about the problem (at one time, my sysadmin grumpily pointed out to me that I had over a hundred stray netcats lying around!), but not the solution.

Many thanks for these clear, well written instructions. I have been trying to work how to do this all day. After reading this I had it cracked in 5 mins.

can you please help, I have pc1 , sshed into mac (ssh server) and pc2 sshed into same mac (ssh server) I want to vnc pc1 from pc2, how do i do that, I have only putty on pc1 and pc2, thanks in advance

Goher – you need to use the port-forwarding/tunneling facilities of SSH. I don’t remember exactly how you do that in putty, but on a command-line ssh you would do something like this:

On pc1:

ssh -R 5901:localhost:5900 mymac

(connect to mymac and also open a tunnel so that the remote port 5901 (on mymac) will connect to port 5900 (the default VNC port) on localhost (ie pc1)

Then on pc2:

ssh -L 5901:localhost:5901 mymac

(connect to mymac and open a tunnel for the local port 5901 on pc2 which forwards it to the port 5901 on the localhost at the other end, which should connect it to the other tunnel we created earlier and forward it to pc1 port 5900)

Then you need to tell your VNC viewer on pc2 to connect to port 5901, also known as VNC display 1 (the default is display 0 on port 5900). How you do that will depend on your VNC viewer, but you probably want to tell it to connect to something like:

:1

Or

localhost:1

Or

localhost:5901

However…

if mymac can see and connect to pc1 directly, you don’t need to have two ssh sessions, you can just goto pc2 and say

ssh -L 5901:pc1:5900 mymac

(connect to mymac, and listen at this end on port 5901, and send any traffic down the tunnel and ask mymac to forward it to pc1 port 5900)

Then run the VNC viewer connecting to localhost:1 as above.

You may need to use IP addresses instead of names in some of the above if you don’t have full DNS set up.

Hope this helps a bit!
Quentin

Thanks very much for your help, it worked

Well this is a good thing if u wanna centralize ssh connections. Is there a possibility to log the commands (which go through the gateway) ? For security and history reasons. For example all admin can ssh to the gateway with separate users and ssh through with only one user (root) to the real servers but all the commands will logged. Is there any idea?

Incredibly nice tip that has proven very useful! Thanks alot!

Out of curiosity:
Is there some way to change the username between the gateway host and the internal host? The boxen I set this up on had differing usernames, and I never got it to work; The username sent to the gateway host is automatically passed on onto the internal host.
Since it wasn’t a critical issue, I just started using the same usernames on both hosts, and it works like a charm. I can’t help but wonder if you could change it, though.

Lowe –

Yes, I think that’ll work fine. Just make the ProxyCommand include the user for the gateway machine.

Host internalmachine.mynet.com
     ProxyCommand ssh gwuser@gateway.mynet.com exec nc %h %p

and then specify the internal username on the command line:

ssh intuser@internalmachine.mynet.com

Even more conveniently, you can specify the default user name for a host in the config file:

Host internalmachine.mynet.com
     User intuser
     ProxyCommand ssh gwuser@gateway.mynet.com exec nc %h %p

I haven’t tested all of this, but I’m pretty sure you can then just ssh to internalmachine.mynet.com and not worry about the username on either remote machine.

I get a

bash: nc: command not found

So this doesn’t work for me. Puty bexcause it looks quite elegant.

    Hi Josh –

    It’s possible that ‘nc’ isn’t on your gateway machine, or that it just isn’t on your PATH.

    If the former, ‘nc’ is the short name for ‘netcat’, so on most Unixy package-management systems, that’s what you want to look for, or you can get it here.

    If you have got it installed, but it isn’t being found, you may need to specify ‘/usr/local/bin/nc’ or something similar.

    It depends a bit on your gateway machine’s operating system…

Thanks for the answer, qsf, but i’m not really allowed to install stuff on my work ssh gateway I’m afraid. I could just put it in my personal bin folder ofc if there is really no alternative.

I was wondering: why would ssh gateway exec ssh mybox not work (obviously I tried and it indeed doesn’t work, just trying to find out why)?

Why the exec nc? It works just fine without the exec.

Also, I found that adding a -w N to the nc command caused the session to die after N secs, unless I also had ServerAliveInterval M in my ~/.ssh/config where M < N.

Thanks, ksb – interesting stuff.

An ‘exec’ would normally replace the current process with the one you’re executing. I *think* (though I admit I haven’t checked) that without it you’ll have one extra shell process – and the nc will be running within that shell. No problem with that, I just find this a bit tidier!

I answered my own question. I was about to ask how you could set this up for multiple different internal machines. Looks like you can easily specify wildcards on the Host line. I thought I’d share in case others find it useful too.

Host *.mynet.com
ProxyCommand …

Got Something To Say:

Your email address will not be published. Required fields are marked *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

*

© Copyright Quentin Stafford-Fraser