28

How can I rewrite the following command with ProxyCommand?

ssh -l username1 -t jumphost1 \
ssh -l username2 -t jumphost2 \
ssh -l username3 -t jumphost3 \
ssh -l username4    server

This doesn't work

ssh -o ProxyCommand="\
ssh -l username1 -t jumphost1  \
ssh -l username2 -t jumphost2  \
ssh -l username3 -t jumphost3" \
    -l username4    server

username1@jumphost1's password:
Pseudo-terminal will not be allocated because stdin is not a terminal.
Permission denied, please try again.
Permission denied, please try again.
Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).
ssh_exchange_identification: Connection closed by remote host

I'm aware of its use with nc, but I'm searching for way to use it with 3+ hops, and also use this option with scp. I checked ssh_config man page, but the information is quite scarce, for me at least.

EDIT

I tried using ProxyCommand nested in another ProxyCommand as suggested below but I always get something along the following lines

debug3: ssh_init_stdio_forwarding: 192.17.2.2:2222
debug1: channel_connect_stdio_fwd 192.17.2.2:2222
debug1: channel 0: new [stdio-forward]
debug2: fd 4 setting O_NONBLOCK
debug2: fd 5 setting O_NONBLOCK
debug1: getpeername failed: Bad file descriptor
debug3: send packet: type 90
debug2: fd 3 setting TCP_NODELAY
debug3: ssh_packet_set_tos: set IP_TOS 0x10
debug1: Requesting no-more-sessions@openssh.com
debug3: send packet: type 80
debug1: Entering interactive session.

Fortunately, since 7.3 -J or ProxyJump serves my purpose — although I still to have to work around my keys setup.

ssh -q -J user1@jumphost1,user2@jumphost2,user3@jumphost3 user@server
1.61803
  • 1,241

2 Answers2

36

use a simple ssh config file

The nc version is not recommended with newer versions of ssh. Instead use the -W switch from more recent versions of OpenSSH. Also, you don't need to copy the config to other hosts! All of the config needs to be done on your host and it does not interfere with the scp in any way.

Here is an example ~/.ssh/config file for your given example:

Host jumphost1
    User username1
Host jumphost2
    User username2
    ProxyCommand ssh -W %h:%p jumphost1
Host jumphost3
    User username3
    ProxyCommand ssh -W %h:%p jumphost2
Host server
    User username4
    ProxyCommand ssh -W %h:%p jumphost3

finally: connect using ssh server or scp file server:path/ or rsync.


p.s.

For those of you who prefer a drawing instead of text in a ssh config file, here is a signal flow diagram:

localhost
        ||
        \/
username1@jumphost1
        ||
        \/
username2@jumphost2
        ||
        \/
username3@jumphost3
        ||
        \/
username4@server


p.p.s. for fun here is a oneline but please note this approach is hard to type and hard to read:

ssh -oProxyCommand= \
  'ssh -W %h:%p -oProxyCommand= \
    \'ssh -W %h:%p -oProxyCommand= \
      \\\'ssh -W %h:%p username1@jumphost1\\\' \
    username2@jumphost2\' \
  username3@jumphost3' \
username4@server

You basically need to go from inside.

Jakuje
  • 21,357
  • Thanks. Tried the one-liner, didn't work. I don't know if it's only the escaping. Can you double-check, please? Also, can you explain why nesting is necessary and chaining doesn't work? – 1.61803 Oct 21 '16 at 16:41
  • 1
    Well, What does "does not work!" mean? Chaining works, but not with scp, because SCP is expecting SCP control messages, but instead gets SSH control messages and fails. On the other hand, the ProxyCommand does it transparently and therefore the outermost ssh (or scp) will get the messages directly from the other end. – Jakuje Oct 21 '16 at 17:33
  • I get the > prompt where is waiting to break the line continuation, I guess — ie, the command never executes. – 1.61803 Oct 22 '16 at 14:31
  • I got it to run with double quotes but it's not working. I added a log for ProxyCommand and the ProxyJump option to my post. I also read about Old Methods of Passing Through Jump Hosts and there's not mention nor example of nested ProxyCommand at runtime. – 1.61803 Nov 13 '16 at 22:12
  • The problem with the example is that this is not how embedded single quotes work. See https://stackoverflow.com/a/1250279/1099103 . Replacing ' with '"'"' (see link) made it work for me. I only tried 2 jumphosts though. I'm not sure how you could do a 3rd jumphost. – senorsmile Dec 15 '17 at 00:08
  • @senorsmile thank you for the comment and feel free to propose edit to this answer that worked for you. – Jakuje Dec 16 '17 at 10:48
  • re "the nc version is not recommended anymore": why isn't "the nc version" recommended anymore? – Trevor Boyd Smith Jul 04 '19 at 13:14
  • 1
    @TrevorBoydSmith Because it requires you to have some external program (nc) installed on all the proxy hosts. The IO redirect using -W does not need that and only requirement is to have openssh client installed on your computer. – Jakuje Jul 04 '19 at 13:34
  • @Jakuje thanks for answering the question. less dependencies is always better. – Trevor Boyd Smith Jul 04 '19 at 14:35
  • @Jakuje how do you make escaping of the %h:%p expansion? What I see when I use ssh -vvv is that even for my nested commands the -W %h:%p expands to the target host:port – Roman Dodin Nov 20 '19 at 10:30
  • 3
    @RomanDodin double % should work: %%h in your case – Jakuje Nov 20 '19 at 11:54
  • 4
    Thanks @Jakuje

    for the sake of completeness, I provide the full command here for the src->jmp1->jmp2->dev path, where the command is issued on src and the target machine is dev

    ssh -o "ProxyCommand=ssh -W %h:%p -o 'ProxyCommand=ssh -W %%h:%%p root@jmp1' root@jmp2" admin@dev

    – Roman Dodin Nov 21 '19 at 12:10
  • Next step would be: ssh -oProxyCommand="ssh -W %h:%p -oProxyCommand=\"ssh -W %%h:%%p -oProxyCommand=\\\"ssh -W %%h:%%p root@jmp1\\\" root@jmp2 \" root@jmp3" root@jmp4. – Matthias Altmann Sep 02 '22 at 14:45
  • @RomanDodin The double percent character %%h does work - I'm curious is this a general escape? (google'd the topic briefly without much success) – dtmland Feb 14 '23 at 23:06
-1

I have done this with two hops, but it should work for three. The simplest way is to have the ~/.ssh/config file set on each host. So, if you want to be on hosta and get to hostd via hostb and hostc`, you could set your configurations thusly:

In hosta:~/.ssh/config:

Host hostd
    User username
    ProxyCommand ssh hostb nc %h %p 2> /dev/null

In hostb:~/.ssh/config:

Host hostd
    User username
    ProxyCommand ssh hostc nc %h %p 2> /dev/null

In hostc:~/.ssh/config:

Host hostd
    User username
    ProxyCommand ssh hostd nc %h %p 2> /dev/null

You can then ssh hostd on any of the hosts in the chain and you'll make your way to hostd.

Using netcat for the proxy does not interfere with scp.

If for some reason you really don't want to use local ~/.ssh/config files, you can do this on hosta:

ssh -oProxyCommand='ssh -oProxyCommand=\'ssh -o ProxyCommand=\\\'ssh username@hostd nc %h %p 2>/dev/null\\\' username@hostc nc %h %p 2> /dev/null' username@hostb nc %h %p 2> /dev/null' username@hostd
DopeGhoti
  • 76,081
  • As I said, I'm aware of nc's use. Can you actually rewrite the command I posted with ProxyCommand as an option? – 1.61803 Oct 19 '16 at 17:33
  • You mention "you're aware of its use but want to use scp". It doesn't interfere with scp. But I will give you the comically long command as an edit shortly. – DopeGhoti Oct 19 '16 at 17:40
  • Thanks. Could you just rewrite your answer focusing on ProxyCommand nesting? If you reread my post you'll see that's the gist. I thought I could, instead of nesting like you did, chain all the middle nodes in ProxyCommand. I still wonder if that's somehow possible. – 1.61803 Oct 19 '16 at 20:13
  • That is in fact what is being done; it's just a lot harder to read than in situ configurations because of all the nested (and increasingly escaped) quotes. You have to do it that way because you are carrying all of the ProxyCommand directives along with you to the next host in the chain. – DopeGhoti Oct 19 '16 at 21:28
  • 1
    This is wrong. The configuration is only on your local computer, not distributed among the hosts on the way as you try to propose. – Jakuje Jan 26 '18 at 14:09
  • "wrong" is, in my opinion, a stronger word than perhaps you should use. You can put the entire proxy chain on the local machine but, as mentioned, you then have to deal with increasingly deep chains of properly-escaped quotes in the local configuration. Distributing the links in the chain also means that you can ssh to the endpoint from any link in the chain, amongst other arguable benefits. – DopeGhoti Jan 26 '18 at 15:21
  • Certainly I have no idea why you have on hostc the jump to hostd through the hostd again. Also, if the hostb does not see the hostd, the nc will fail for you in the first jump. – Jakuje Jan 26 '18 at 15:45