99

I want to copy a file from my machine A to server C, but only have access to server C through server B.

Instead of first transferring to server B, log in and then transfer to server C, Is is possible to transfer the file directly with SCP or similar programs?

(Emacs tramp-mode has this feature for editing files remotely).

kmario23
  • 103
  • 4
sverrejoh
  • 1,091
  • 1
  • 8
  • 4

10 Answers10

55

You can add -o options to scp instead of .ssh/config.

scp -o ProxyCommand="ssh $jump_host nc $host 22" $local_path $host:$destination_path

$jump_host is your "server B" in this case.

John Tantalo
  • 541
  • 4
  • 2
  • Thi si my preferred way to do this. Messing up .ssh/config for multihopping is not the best solution if you access the same host either from a gateway and directly. – GabrieleV Aug 26 '11 at 22:57
  • How this would be with different/custom usernames and ports? – Pablo A Aug 27 '18 at 20:33
  • I try this and must input password. How can I fix this. Thanks. – hqt Jul 01 '19 at 20:38
  • 1
    In my case, with help of this info https://stackoverflow.com/questions/22635613/what-is-the-difference-between-ssh-proxycommand-w-nc-exec-nc, another option worked: `scp -o ProxyCommand="ssh $jump_host -W $host:22" $local_path $host:$destination_path` – Askar Kalykov Aug 17 '20 at 13:05
  • 1
    What if I need one more hop? Is there any chainable syntax/solution? – Ben Aug 13 '21 at 06:42
  • I understand by `local_path` you mean `source_path`? – user1079505 Oct 25 '21 at 17:28
  • How to save ssh keys with ssh-copy-id? – zicjin Jun 02 '22 at 05:37
46

Assuming OpenSSH, add to your SSH configuration in .ssh/config

Host distant
ProxyCommand ssh near nc distant 22

This will cause SSH to be able to connect "directly" to the machine named distant by proxying through the machine named near. It can then use applications like scp and sftp to the distant machine.

For this to work you need 'nc' aka netcat installed on the machine named near. But a lot of modern systems will have it already.

towo's tar solution is more effective for one-shot problems, assuming you've memorised tar's syntax and rules of operation.

tialaramex
  • 709
  • 4
  • 3
  • This is the same method I use ... In this example 'distant' would be server C and 'near' would be server B for clarification... – Jeremy Bouse Jul 08 '09 at 13:19
  • A lot of modern machines don't have 'nc': it's normally available only to Linux machines and only by request (not part of the standard install). – Mei Jul 08 '09 at 14:32
  • 1
    ssh has now -W option, that does this automatically without 'nc', but I wonder why there isn't scp -W – kubanczyk Dec 04 '11 at 11:13
  • 3
    In case I'm not the only one this wasn't obvious to: if the username on `near` is different from the username on `distant`, the near user goes into `ProxyCommand ssh nearuser@near...`, and the distant user goes into a separate `User distantuser` line. – Mu Mind Feb 18 '12 at 04:30
  • Just type ssh multi hope and press Google I am feeling lucky – chandank Mar 20 '13 at 01:15
  • Do you know how to make it work with globbing? Like, if I have a cluster with machines node1, node2, node3 -- in SSH config, I can usually write "Host node*", but this seems to require the exact names hardcoded? – gatoatigrado Mar 20 '13 at 22:40
  • Current versions of ssh have `nc` built-in so now you only need to use the `-W` command line option: `ProxyCommand ssh near -W distant:22` – Dave Apr 30 '13 at 21:13
  • Thanks! BTW, my `near` server uses a non-standard SSH port (123) and username, e.g. `ProxyCommand ssh nearusername@near -p 123 nc distant 22` – Jeff Ward Nov 12 '15 at 04:27
23

With more recent versions of ssh on the server near (B) machine the following will work without netcat:

Host distant
    ProxyCommand ssh near -W distant:22

It will however require AllowTcpForwarding to be yes (the default) on the near (B) machine

edit: requires OpenSSH 5.4+ on B

danblack
  • 1,179
  • 10
  • 14
19

You can ssh to server B using something like

ssh -L 5022:<server C IP>:22 <user_serverB>@<server B IP>

Then you can ssh to server C using

ssh -p 5022 <user_serverC>@localhost 

Similarly scp would work using

scp -P 5022 foo.txt <user_serverc>@localhost:

Remember to use correct case of p with scp and ssh

user1146334
  • 103
  • 3
Saurabh Barjatiya
  • 4,643
  • 2
  • 29
  • 34
7

It's possible and relatively easy, even when you need to use certificates for authentication (typical in AWS environments).

The command below will copy files from a remotePath on server2 directly into your machine at localPath. Internally the scp request is proxied via server1.

scp -i user2-cert.pem -o ProxyCommand="ssh -i user1-cert.pem -W %h:%p user1@server1" user2@server2:/<remotePath> <localpath>

The other way around also works (upload file):

scp -i user2-cert.pem -o ProxyCommand="ssh -i user1-cert.pem -W %h:%p user1@server1" <localpath> user2@server2:/<remotePath>

If you use password authentication instead, try with

scp -o ProxyCommand="ssh -W %h:%p user1@server1" user2@server2:/<remotePath> <localpath>

If you use the same user credentials in both servers:

scp -o ProxyCommand="ssh -W %h:%p commonuser@server1" commonuser@server2:/<remotePath> <localpath>

The -W option is built into new(er) versions of OpenSSH, so this will only work on machines that have the minimum version (5.4, unless your distro back-ported any features; e.g., RHEL6 OpenSSH 5.3p1 includes this feature). Per the release notes: http://www.openssh.com/txt/release-5.4

Added a 'netcat mode' to ssh(1): "ssh -W host:port ..." This connects stdio on the client to a single port forward on the server. This allows, for example, using ssh as a ProxyCommand to route connections via intermediate servers.

%h and %p are placeholders for the host and port.

donhector
  • 181
  • 1
  • 4
6

This isn't scp (which OP requested), but I found it super simple to use rsync to copy from local to remote over a single hop with:

rsync -v -e 'ssh -A -t user@jumpserver ssh -A -t user@destinationserver' /path/to/sourcefile :/path/to/destination

Source: http://mjbright.blogspot.com/2012/09/using-rsync-over-multi-hop-ssh.html

I had tried the -o ProxyPass suggestion above and didn't want to change config for my changing needs. As the author in the link above states, the destination file preceding colon (:) is important to indicate the specified path is on the destination server. Also, using rsync, you have the options of date compare, folder sync, etc. I hope this helps someone!

texas-bronius
  • 61
  • 1
  • 2
  • You're right. It is easier to just use `rsync` rather than deal with `scp`'s oddities. – JS. Feb 23 '21 at 18:29
3

If you want to be really wicked, you could chain ssh and tar, something like tar c mydir | ssh server "ssh otherserver | tar x", but this can run into all hands of problems.

The easier way would be just to set up an SSH tunnel with the built-in methods of SSH; look at the -D switch in the manpage and just forward some port to the other server's ssh port.

towo
  • 1,887
  • 14
  • 12
2

You can also do this in reverse and is maybe easier.

Supposing you have an ssh session opened with the machine you want to send the file to. This farthest-hop PC, we'll call this hop2. Your "proxy" host will be hop1. The PC that is file-origin, we'll call that origin.

origin:~/asdf.txt  --> hop1 --> hop2:~/asdf.txt

You can build tunnels making a local port available on a remote PC. We're thereby defining a port to open on the remote PC, which will be a redirect to the port you pulled over with you when you built the tunnel.

On hop2:

ssh -R 5555:127.0.0.1:22 <hop1_user>@<hop1_IP>
#this has the effect of building a tunnel from hop2 to hop1, making hop2's port 22 available on hop1 as port 5555

Now in that opened tunnel session, you can do the same from hop1 to file_origin.

On hop1:

ssh -R 6666:127.0.0.1:5555 <origin_user>@<origin_IP>
#this has the effect of building a tunnel from hop1 to origin while also pulling the active tunnel with it, making hop1's port 5555 (hop2's port 22) available on origin as port 6666.

You are now tunneled from hop2 to hop1 to origin. Coincidentally, now both port 5555 and 6666 are open on origin, which are redirects to hop2's port 22. Within this session, both of the following are valid scp routes to hop2:

On origin:

scp -P 6666 ~/asdf.txt <hop2_user>@<127.0.0.1>:~/asdf.txt

In this way, you can have some arbitrary number of hops in between, and it's easier to work with in terms of chaining together more than two hops.

SYANiDE
  • 121
  • 4
1

Try adapring the following example openssh config for a setup that can be used for multiple hosts:

Host uat-*
     ProxyCommand ssh bastion-uat nc %h %p

This presumes a set of servers that begin with "uat-" that are only accessible via the jumpbox/gateway server "bastion-uat". You probably also want to add ForwardAgent yes if you are using a key to login.

  • Don't use `ForwardAgent yes` for this. Agent forwarding is not necessary in this case because no ssh client will be running on the bastion, and forwarding an agent when it isn't needed is just going to reduce security. And I think `ssh` is missing from your command. If a recent `ssh` version is being used you don't need `nc`, you can type `ssh -W %h:%p bastion-uat` instead. – kasperd Jun 25 '14 at 12:10
  • @kasperd Edited to include ssh . Re ForwardAgent; a ssh client will run in order to run the nc command itself. Perhaps you are referring to the shell? – Benjamin Goodacre Jun 25 '14 at 12:55
-1

scp -o 'ProxyJump jumpboxname' somefilename.txt finaldestinationhost:/tmp/.

IanD
  • 21
  • 4
    Don't just dump a bit of unexplained code here. There are plenty of good answers to this question and you need to add value to the existing ones - this doesn't. – Sven Nov 14 '18 at 13:57