How SCP works
The internal workings of scp
depends on two undocumented forms of scp
, which is scp -f
and scp -t
. They are basically remote servers listening to stdin and generating data over stdout. Those processes use a protocol similar to what sftp would do. More information can be found at an Oracle Blog.
When scp
starts, it will first start a ssh session to start scp -t
or scp -f
. Your local scp
process will then communicate with the remote scp
process through stdout and stdin piped to ssh (fully encrypted) and will serve as stdin/stdout for your remote scp
session.
How to Customize
The scp
command provides an option -s
that allows you to customize the program used to setup the remote scp
session. It is expected to handle all the options typical for ssh
. To help debug how that might look like, we setup a dummy program that just dumps the parameters:
/tmp/scp-dump.sh:
echo $0 $*
We will execute it as follows:
scp -S /tmp/scp-dump.sh /tmp/dump.txt me@server.com:/tmp
And appropriately we get the following output:
/home/me/dump.sh \
-x -oForwardAgent=no \
-oPermitLocalCommand=no \
-oClearAllForwardings=yes \
-l me -- server.com scp -t /tmp
Line-breaks with backslash were added for readability.
That allows us an opportunity to modify the parameters and commands before we send it to the ssh
command.
Setting up sudo
now we can write a program to modify the parameters as needed, start ssh, and use a modified form to start scp -t
. We will basically capture the parameters, trap those we want to use internally only add additional ones, and make changes.
/tmp/scp-sudo.sh
:
add_ssh_option() {
local option
if [ $# = 2 ]; then
option="$1 \"$2\""
else
option="$1"
fi
if [ -z "${ssh_options}" ]; then
ssh_options=${option}
else
ssh_options="${ssh_options} ${option}"
fi
}
parse_options() {
ssh_options=
local option
while [ $# > 0 ] ; do
case $1 in
-oSudoUser=* )
SSH_SUDO_USER=${option##*=}
shift
;;
-l ) ssh_user=$2 ; shift 2 ;;
-i ) add_ssh_option "$1" "$2" ; shift 2 ;;
-- ) shift ; break ;;
-* ) add_ssh_option "${1}" ; shift ;;
* ) break ;;
esac
done
ssh_host=$1
shift
ssh_command="$*"
}
parse_options "$@"
# To avoid intererence with Standard-In, we change this to
# Strict:
add_ssh_option "-oStrictHostKeyChecking=yes"
if [ -z "${SSH_SUDO_USER}" ]; then
# As before without sudo
cat | ssh $ssh_options -l $ssh_user $ssh_host $ssh_command
exit $?
else
# Send standard-in to the customized "scp -t" sink.
cat | ssh $ssh_options -l $ssh_user $ssh_host sudo -i -u ${SSH_SUDO_USER} $ssh_command
exit $?
fi
Now we can use the modified scp form:
scp -oSudoUser=other \
-i /tmp/me-id_rsa \
/tmp/scp-sudo.sh \
/tmp/dump.txt \
me@server.com:/tmp
This works fine for sending a file to remote. However when I retreive a remote file, it somehow hangs. When I break the session (control-c), I do see that the transfer was succesful, but I have somehow an extra file named "0". Wonder if anyone can help me with that?
Better Option: sftp
However sftp is much better.
- it has the
-S
option - allowing to set the entire ssh command line. I had trouble figuring it out and making it to work. I suspect that I do not understand how to correctly bind stdin/stdout from within a shell-ssh wrapper.
- it has a
-s
(lowercase) option - allowing to set just the sub-command responsible for setting up the remote server only. I found this easier to make it work.
- You can set the remote path, remote file permissions, do both put/get, etc as per the man pages for sftp.
In this case we just simply
sftp -s "sudo -u sudo-user -- /usr/libexec/openssh/sftp-server" me@server.com
That gave me the happy "sftp>
" prompt
Note that the subprogram is sometimes located in different paths, example /usr/libexec/openssh/sftp-server
. I would suggest parsing from the "Subsystem sftp
" string from within the /etc/ssh/sshd_conf
file?
Can you please elaborate by what you mean by "provide a sudo user"? Do you just mean that scp has managed to write to a specific location? You say
I supsect even the sudo happened succesfully.
Don't suspect. Know. If in Unix,ls -ltr /var/log
to show which log file has been most recently updated. sudo may log to /var/log/auth.log or /var/log/authlog (depending on operating system). That will confirm if it actually uses sudo, and what commandsudo
was asked to run. That may help to clarify what happens "under the hood"/"behind the scenes". – TOOGAM – 2016-02-17T14:48:42.4071
You speculate:
– TOOGAM – 2016-02-17T14:54:16.360almost sounds as if "scp" is not as feature complete as "sftp" is
. I confirm, with clarity, this is absolutely right. (I'm not necessarily endorsing all the details that led you to that conclusion. I am endorsing the specific resulting conclusion that I just quoted.) scp is a dead protocol, meaning that it has no development future. This is according to OpenSSH FAQ 2.10 which describes why " scp is not standardized", "New features are more likely in sftp"@TOOGAM the sudo user was not able to write, for that I need the subsystem to start and handle the incoming request. The subsystem did start and write, but again as the original user. I can see also that ssh started twice from duplicate welcome log written to the screen, hence I was stating that I got past the sudo. With limited access to system logs at this point, I had to ... Well ... Guess at this point. I will update as I learn more. – YoYo – 2016-02-17T17:57:25.560
What you do you mean "limited access to system logs"? You shouldn't be having limited access to system logs if you're sudo'ing. :) (This statement may be making assumptions, like sudo becoming root rather than a different user.) – TOOGAM – 2016-02-17T21:05:21.320