138

I have access to 3 machines, A, B, and C. The only possible (ssh) connections are:

A -> B
B <-> C

I need to get files from A to C, so I could scp the files from A to B, and then scp them from B to C. However, B doesn't have much disk space, so this is not an option. Is there a way to scp files from A to C via B? Note, I don't have root access on any of the machines, so don't think I can set up any persistent tunnels, but correct me if I'm wrong!

1
  • 9
    I know this doesn't answer the question, but for those who don't know about rsync, or don't know how to use it to hop through a host, this could be a useful tip: use the '-e' option with rsync like this: A$ rsync <options> -e 'ssh B ssh' source C:destination
    – Eddified
    Apr 1, 2014 at 21:44

6 Answers 6

160

ProxyJump

New in OpenSSH 7.3:

A$ scp -oProxyJump=B thefile C:destination

(Behind the scenes, this just uses ProxyCommand and ssh -W.)

ProxyCommand

Updated to include -W from other answers:

A$ scp -oProxyCommand="ssh -W %h:%p B" thefile C:destination

If A has a very old SSH client installed (without -W support), or if B is configured to disallow TCP forwarding (but still allows shell commands), use alternatives:

A$ scp -oProxyCommand="ssh B socat stdio tcp:%h:%p" thefile C:destination
A$ scp -oProxyCommand="ssh B nc %h %p" thefile C:destination

Pipes

A$ tar cf - thefile anotherfile | ssh B "ssh C \"cd destination && tar xvf -\""
A$ (echo thefile; echo anotherfile) | cpio -o | ssh B "ssh C \"cd destination && cpio -i\""

For just one file:

A$ ssh B "ssh C \"cd destination && cat > thefile\"" < thefile

"Tunnel" through B

A$ ssh -f -N -L 4567:C:22 B
(continues running in background)

A$ scp -P 4567 thefile localhost:destinationPath

When you're done, don't forget to kill the previously started ssh process (which has dropped to background due to -f -N).

  • -f Requests ssh to go to background just before command execution. This is useful if ssh is going to ask for passwords or passphrases, but the user wants it in the background. This implies -n.
  • -N Do not execute a remote command. This is useful for just forwarding ports.

Reverse "tunnel" through B to A

Doesn't always work though:

A$ ssh -f -N -R 4567:localhost:22 B
(now you can reach A from B, by using localhost:4567)

B$ scp -P 4567 localhost:thefile C:destination
  • -R Specifies that connections to the given TCP port or Unix socket on the remote (server) host are to be forwarded to the given host and port, or Unix socket, on the local side.
5
  • Thanks so much for these examples @grawity. One question - is it possible to invert `tar c thefile anotherfile | ssh B "ssh C \"cd destination && tar xv\""´ in order to copy download from C to A (being on A)
    – dmeu
    Oct 8, 2015 at 9:04
  • @dmeu: Yeah, it's possible. Oct 8, 2015 at 13:14
  • How to "kill" the previously started ssh process if say I am using a MacOS? Aug 30, 2017 at 2:43
  • 1
    Note: As usual, if you want to scp from C, though B, to A, you can do A$ scp -oProxyJump=B C:destination thefile.
    – jvriesem
    Sep 26, 2017 at 23:20
  • @Pablo Better use -S, then, and -O exit. Or... at least pkill -f. Feb 8, 2018 at 5:01
40

Versions of scp from early 2011 and later may have a "-3" option:

 -3      Copies between two remote hosts are transferred through the local
         host.  Without this option the data is copied directly between
         the two remote hosts.  Note that this option disables the
         progress meter.

If you have this you can just run:

B$ scp -3 A:file C:file
3
  • In my case host A was only accessible from B (which was VPNed). Host C was on the same LAN as B. I wanted to get a file from A to C and scp -3 solved it brilliantly.
    – Joe
    Aug 7, 2016 at 17:34
  • I had trouble with this when both hosts requested a password. It would seem to ask for both at once (two password prompts appeared on same line) and then it failed to accept my password. I could eventually get it to work by typing my password repeatedly (same password on both hosts), but it was hard to figure out.
    – Colin D
    May 9, 2017 at 15:00
  • Interesting I could not find -3 in my man page but it still worked.
    – Nils
    May 7, 2021 at 18:47
11

Nearly all have been already said but here is my last penny: I use ProxyCommand variant without nc nor soc. Based on OpenSSH Proxies and Jumphost Cookbook I crafted a following configuration:

  1. So we have following players:

    • HOME_HOST: it is from where we copy a file to the target host
    • HOP_HOST: we copy through this host (logged as HOP_USER)
    • TARGET_HOST: it is our destination (authenticated as TARGET_USER)
  2. First I added my local public key from my home host .ssh/id_dsa.pub to .ssh/authorized_keys at both hop and target hosts. Yes, the same public key from the home host to both of them. Usually you would expect it is the HOP public key you have to add to the TARGET one.

  3. Then I tweaked .ssh/config a little by adding following entry:

    Host TARGET_HOST
       User TARGET_USER
       ProxyCommand ssh -W %h:%p HOP_USER@HOP_HOST
    
  4. After that the copy operation is as simple as: scp FILE TARGET_HOST:. It displays double banners from both the hop and target nodes but it works.

Of course you may use above to ssh directly to the target: ssh TARGET_HOST. It works with scp and ssh.

Another more general option might be sshuttle utility which appears to be a kind of transparent proxy (vpn over ssh). So in your case of A->B<->C it allows to connect to each node at C's network: A->B-[CDEFG]. It does not need admin but it requires Python 2.7 (3.5 also OK) which is not always what we have. It is worth of trying it.

9
ssh -L 4321:hostC:22 youruser@hostB

in another shell:

scp -P 4321 localfile [email protected]

This is using port forwarding. The only limitation here is host B needs to be configured to allow port forwarding. Otherwise this should work fine.

In the way of explanation, -L and -R allow you to forward ports. In -L, the first port given is the port ssh will begin listening on the originating machine (host A), and it'll forward anything it receives on that port over your SSH connection to host B, then route to host C on port 22.

edit

I messed up the syntax slightly. It sets up a forward on your LOCAL machine.

1
  • @astrofrog - if one of our answers satisfies your needs, you should probably accept one of them. May 2, 2011 at 22:28
5

Grawity's ProxyCommand answer worked for me but since I am less familiar with SSH it took some experimenting. I thought I would just spellout Grawity's answer with more detail to help out any other newbies to SSH like myself. Here are the definitions for more explicit notation:

Machine A: the machine you are on

Server B: [email protected] (the jump host or middle server)

Server C: [email protected] (the remote server you want to copy to)

ProxyCommnad

    A$ scp -oProxyCommand="ssh -W %h:%p [email protected]" thefile [email protected]:destination

Concrete Example

So for a concrete example, say you have access to a server with IP 0.0.1.2 with a user account named bar (Server C). But to get to it you have to first login to a server with IP 0.0.1.1 with user account named foo (Server B). Now you want to copy file baz.txt located on your current machine (Machine A) to server 0.0.1.2's /home/bar/ directory. To use the above ProxyCommand for this example you would execute the following:

    A$ scp -oProxyCommand="ssh -W %h:%p [email protected]" baz.txt [email protected]:/home/bar/

You can also just as easily copy a file from Server C by switching the order of the file and destination. So for example, if baz.txt was already on server 0.0.1.2 located at /home/bar/ then you could copy it to your machine using:

    A$ scp -oProxyCommand="ssh -W %h:%p [email protected]" [email protected]:/home/bar/baz.txt /destination/path/on/A

Hope this helps people that need things spelled out for them a bit more than others.

0

Since v7.3 OpenSSH supports a -J. To send to C from A logged in on A:

tar cf - file1 file_n | ssh -J userB@B:Bport userB@C -p Cport 'tar -C destDir xvf -'
  • Use as many hops as you want with ssh's -J option.
  • Omit the -C to leave the files on home folder.
  • Send any files at once (text or binary).
  • If necessary, compress the stream with tar's -z or ssh's -C.
  • If you get "open terminal failed: not a terminal", try to temporarily disable tmux/screen, probably on your .bashrc.

Inspired on Florian Fida and Dan Garthwaite's answers.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .