I'm posting this for the next person who searches the web for advice on how to do this, because when I needed to I didn't find a clear answer and it took me a while - reading documentation, experimenting with ssh commands - to figure it out.
You've got hosts on two private, protected networks, let's call them zimnet and girnet, and you'd like to securely copy some data from a zimhost to a girhost through an ssh tunnel. The data is sensitive so you'd like to reduce your risk by avoiding having any of it ever stored on a filesystem on any host in between, so you don't have to worry about file permissions, backups, automatic snapshots, and so on. Just a direct ssh tunnel all the way through.
[Edit: Also, you may want to allow yourself to do the actual copy noninteractively, to more easily included it in a script - perhaps something that generates the data, copies it, and deletes it, all in a rapid series. However, some of the hops may require an interactive login (in my case, the ssh from zimbastion to girbastion required entering a one time password). Setting up a temporary ssh tunnel allows you to do all the authentication steps in advance, then kick off whatever copies the data.]
In the simplest case, you can just run "scp file girhost:/path" on zimhost, if they could talk directly.
If they can't talk directly, but there's some intermediate host they can both talk to, it's pretty easy to set up a two-hop ssh tunnel with port forwarding, and there are a lot of examples on the web of how to do it. But what if...
- zimnet and girnet each has its own separate bastion host that can connect to hosts on that network
- hosts behind the firewall for each network cannot initiate tcp connections to their bastion hosts or any outside hosts
- zimbastion can ssh to zimhost, and to girbastion, but not to girhost
- girbastion can ssh to girhost, and to zimbastion, but not to zimhost
- neither zimhost nor girhost can ssh to any of the others
Here's one way to do it. There are other ways, but this is the one I came up with that worked for me.
On zimbastion, run:
ssh -f -L 2222:girhost:22 girbastion -N
ssh -f -R 2222:localhost:2222 zimhost -N
Now on zimhost, you can do:
scp -P 2222 -p /source/file localhost:/destination/path
Command #1 connects local port 2222 on zimbastion (the local host) to an ssh on girbastion that forwards to port 22 on girhost. It can do this because girbastion is allowed to ssh to girhost, and the forwarding is happening on girbastion. Command #2, intiated from zimbastion as well, ssh's to zimhost and connects remote port 2222 - that is, port 2222 on zimhost - to localhost:2222 (on zimbastion), which is the port where command #1 is listening. You've set up a three-hop ssh tunnel, all initiated from one of the intermediate hosts.