Preliminary notes
I assume you can ssh userB@B_seen_from_A from A to reach B; and you can ssh userB@B_seen_from_C from C to reach B. I also assume there are SSH servers on A and on C as well (although for now you cannot reach them from the outside).
scp uses ssh directly, I mean it crafts an ssh … command (and this is not as clean as we may want, see "some insight" in this answer of mine). Setting things up for scp means setting things up for ssh in the first place.
When following this answer, if you encounter an error or unexpected message, please see "Notes" down below. Some of them address few possible obstacles.
Basic setup for invoking scp on A
You did not tell us your tries to establish "some sort of reverse SSH tunnel" when you "couldn't make it work". I don't know what you tried and how it failed. You are probably able to do this:
# on C
ssh -R 1111:localhost:22 -N userB@B_seen_from_C
(Note localhost will be resolved on C, so the tunnel will lead to C.)
This is the remote tunnel you need. The number 1111 is quite arbitrary (you most likely are not allowed to use a port with number lower than 1024 and you may want to avoid ephemeral ports; and of course the port must be available). 22 is the standard port for SSH (use the port your SSH server on C uses). If you do this right then ssh -p 1111 userC@localhost invoked on B will let you reach C.
Still you won't be able to use this tunnel from A. If on C you used … -R :1111:localhost:22 … (note the additional colon) then you might or might not be able to ssh -p 1111 userC@B_seen_from_A from A to reach C, depending on the firewall and the settings of the SSH server on B (compare this answer of mine). Let's not do this. Let's create another tunnel, from A to B, that allows you to reach the first tunnel from A. This is the other tunnel:
# on A
ssh -L 2222:localhost:1111 -N userB@B_seen_from_A
(Note this localhost will be resolved on B, so the tunnel will lead to B.)
Now you can reach C from A by connecting via the two chained tunnels:
# on A
ssh -p 2222 userC@localhost
(And this localhost is resolved on A, it leads to A; we're connecting to the A's end of the chained tunnels, the other end is on C.)
Then you can copy files between A and C by invoking scp on A:
# on A
# copying from A to C
scp -P 2222 /local/path/to/file userC@localhost:/remote/destination
# or copying from C to A
scp -P 2222 userC@localhost:/remote/path/to/file /local/destination
(Note with scp you need -P to specify the port, while with ssh you need -p.)
Basic setup for invoking scp on C
There's a symmetry between A and C in your original setup. To be able to copy files between A and C by invoking scp on C, just swap A and C in the above solution.
Basic setup for invoking scp on A or C
You can merge the two above solutions, so copying files can be done by invoking scp on A or C. Terminate (Ctrl+c) all previous ssh commands we used and start anew:
# on C
ssh -L 3333:localhost:3333 -R 1111:localhost:22 -N userB@B_seen_from_C
# on A
ssh -L 1111:localhost:1111 -R 3333:localhost:22 -N userB@B_seen_from_A
Note it's 1111 where previously we had 2222. The idea is to use the same port number to reach C from A or from B (and the number is 1111); and another number to reach A from C or from B (and the number is 3333). So 1111 is associated with two chained tunnels in one direction, 3333 is associated with two chained tunnels in the opposite direction. The SSH server on B will listen on both ports, therefore the numbers must differ.
Then on A use scp -P 1111 … and refer to C as userC@localhost; or on C use scp -P 3333 … and refer to A as userA@localhost.
How about invoking scp on B?
The two ssh commands from the previous section will allow you to reach C from B (by connecting to B's localhost on port 1111) and to reach A from B (by connecting to B's localhost on port 3333). In theory this should be enough for scp on B to easily transfer a file from A to C. In practice scp is somewhat limited.
In general, when scp is about to copy a file between two remote hosts, there are two ways:
Traditionally scp tells one host to reach the other and transfer the file. The machine where you invoke scp may be unable to reach the other host and the method will still work, if only the first host can reach the other. The invoked scp executable does not act as a relay.
With scp -3 the invoked scp executable acts as a relay. The machine where you invoke scp needs to be able to reach the two hosts, but neither host needs to be reachable from the other.
One way or another, if you need to specify two different ports (and you do in our setup) then it won't work with scp -P because scp -P allows you to specify a single value.
It should work if you pass the ports in a different way. Example:
# on B
# copying from A to C
scp -3 scp://userA@localhost:3333//A_path/to/file \
scp://userC@localhost:1111//C_path/to/destination
Another solution exists, please read the next section.
More elegant and powerful setup
ssh (and related tools) consult config files (~/.ssh/config for user's and /etc/ssh/ssh_config for server-wide config). Instead of specifying things on the command line, specify them in the config (note: near the beginning).
On A:
Host B
Hostname B_seen_from_A
User userB
Host C
Hostname localhost
Port 1111
User userC
On B:
Host A
Hostname localhost
Port 3333
User userA
Host C
Hostname localhost
Port 1111
User userC
On C:
Host A
Hostname localhost
Port 3333
User userA
Host B
Hostname B_seen_from_C
User userB
The commands that create tunnels simplify to:
# on C
ssh -L 3333:localhost:3333 -R 1111:localhost:22 -N B
# on A
ssh -L 1111:localhost:1111 -R 3333:localhost:22 -N B
(The port forwardings can be specified in the configs as well, see LocalForward and RemoteForward in man 5 ssh_config; but this would affect any ssh … B … and you may not want this.)
An example scp command on A is simple:
# on A
scp /path/to/file C:/some/destination
And now you can do this also from B:
# on B
scp A:/path/to/file C:/some/destination
# or
scp -3 A:/path/to/file C:/some/destination
Or from C:
# on C
scp A:/path/to/file /some/destination
The point is now A and B can resolve C, each to a different address, but each address is exactly the right one in the context of the respective client. Similarly B and C can resolve A. And if you additionally make sure A recognizes A as itself and C recognizes C as itself then this command:
scp A:/path/to/file C:/some/destination
should work on any of the three hosts. It will establish some unnecessary connections (like from A to A) if invoked on A or on C, but in principle it will work; it's a universal command then.
There are possible problems, see below.
Notes
- If you get
administratively prohibited: open failed while creating a tunnel then see this question: SSH tunneling error: channel 1: open failed: administratively prohibited: open failed.
ExitOnForwardFailure may be handy.
- Instead of
ssh -L … -R … -N … on A consider autossh or a service to keep the tunnels open; the same for C.
- When using
scp on B without -3 password authentication may break things. Prefer key-based authentication.
- There are six or eight possible client-server pairs (A→B, A→C, B→A, B→C, C→A, C→B; and if you want "universal commands" to be universal then additionally A→A and C→C). If you set up key-based authentication for only few of them then some commands will fail or ask for password. Pay attention to what client needs to authenticate to what server, or simply set up key-based authentication for all possible pairs.
- The ports you open (i.e. the listening ends of the tunnels) will be available at least to other local users of respective systems (A, B or C). Each tunnel ultimately leads to an SSH server where one needs to authenticate. Even if a rogue user hijacks the port (by simply binding to it before you do) and your tunnel leads to their SSH server, you will be able to tell before you send your precious files or request possibly dangerous files. Still, just by keeping the port occupied, the rogue may prevent you from setting things up (especially if your setup is automated and not supervised). Unix sockets instead of TCP ports on B may help, but I won't elaborate.
- IMO
scp is a quick and dirty hack that stuck (see near the end of this already linked answer). The tunnels should allow you to use any tool that uses SSH as transport, especially after you move the details to config files.