Как работает SCP
Внутренняя работа scp
зависит от двух недокументированных форм scp
- scp -f
и scp -t
. В основном это удаленные серверы, которые прослушивают стандартный ввод и генерируют данные через стандартный вывод. Эти процессы используют протокол, аналогичный тому, что делал бы sftp. Более подробную информацию можно найти в блоге Oracle .
Когда запускается scp
, он сначала запускает сессию ssh для запуска scp -t
или scp -f
. Затем ваш локальный процесс scp
будет связываться с удаленным процессом scp
через stdout и stdin, переданные по каналу ssh (полностью зашифрованные), и будет служить stdin/stdout для вашего удаленного сеанса scp
.
Как настроить
Команда scp
предоставляет параметр -s
который позволяет вам настроить программу, используемую для настройки удаленного сеанса scp
. Ожидается, что он обработает все параметры, типичные для ssh
. Чтобы помочь отладке, как это может выглядеть, мы устанавливаем фиктивную программу, которая просто выводит параметры:
/tmp/scp-dump.sh:
echo $0 $*
Мы выполним это следующим образом:
scp -S /tmp/scp-dump.sh /tmp/dump.txt me@server.com:/tmp
И соответственно мы получаем следующий вывод:
/home/me/dump.sh \
-x -oForwardAgent=no \
-oPermitLocalCommand=no \
-oClearAllForwardings=yes \
-l me -- server.com scp -t /tmp
Для удобства чтения добавлены разрывы строк с обратной косой чертой.
Это дает нам возможность изменить параметры и команды, прежде чем мы отправим их в команду ssh
.
Настройка sudo
Теперь мы можем написать программу для изменения параметров по мере необходимости, запустить ssh и использовать измененную форму для запуска scp -t
. Мы будем в основном собирать параметры, перехватывать те, которые мы хотим использовать внутри, только добавлять дополнительные и вносить изменения.
/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
Теперь мы можем использовать модифицированную форму scp:
scp -oSudoUser=other \
-i /tmp/me-id_rsa \
/tmp/scp-sudo.sh \
/tmp/dump.txt \
me@server.com:/tmp
Это прекрасно работает для отправки файла на удаленный. Однако, когда я получаю удаленный файл, он как-то зависает. Когда я прерываю сеанс (control-c), я вижу, что передача прошла успешно, но у меня есть какой-то дополнительный файл с именем "0". Интересно, кто-нибудь может мне помочь с этим?
Лучший вариант: sftp
Однако sftp намного лучше.
- у него есть опция
-S
позволяющая установить всю командную строку ssh. У меня были проблемы с выяснением и заставить его работать. Я подозреваю, что я не понимаю, как правильно связать stdin/stdout из оболочки shell-ssh.
- он имеет опцию
-s
(нижний регистр) - позволяет установить только подкоманду, отвечающую только за настройку удаленного сервера. Я нашел это проще, чтобы заставить его работать.
- Вы можете установить удаленный путь, права доступа к удаленным файлам, сделать как put/get, и так далее в соответствии с man-страницами для sftp.
В этом случае мы просто
sftp -s "sudo -u sudo-user -- /usr/libexec/openssh/sftp-server" me@server.com
Это дало мне счастливую подсказку " sftp>
"
Обратите внимание, что подпрограмма иногда находится по разным путям, например /usr/libexec/openssh/sftp-server
. Я хотел бы предложить синтаксический анализ строки " Subsystem sftp
" из файла /etc/ssh/sshd_conf
?