SSH ProxyCommand

July 3rd, 2008

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.

Posted on Thursday, July 3rd, 2008 at 9:25 pm and filed under Internet, Linux, Open Source, Programming.

20 Responses to “SSH ProxyCommand”

  1. Matthew Says:

    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!

  2. BobPaul Says:

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

  3. qsf Says:

    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.

  4. Philipp Says:

    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

  5. qsf Says:

    Thanks, Philipp – a useful hint.

  6. qsf Says:

    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
    
  7. Brooks Says:

    @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.

  8. Simon Says:

    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.

  9. Goher Says:

    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

  10. qsf Says:

    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

  11. Goher Says:

    Thanks very much for your help, it worked

  12. dmz Says:

    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?

  13. Lowe Thiderman Says:

    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.

  14. qsf Says:

    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.

  15. Josh Says:

    I get a

    bash: nc: command not found

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

  16. qsf Says:

    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…

  17. Josh Says:

    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)?

  18. ksb Says:

    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.

  19. qsf Says:

    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!

  20. Bryce Says:

    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 …

Leave a Reply

Connect with: