Назад | Перейти на главную страницу

Как я могу запустить произвольно сложную команду, используя sudo поверх ssh?

У меня есть система, в которую я могу войти только под своим именем пользователя (myuser), но мне нужно запускать команды от имени другого пользователя (scriptuser). До сих пор я придумал следующее, чтобы запускать нужные мне команды:

ssh -tq myuser@hostname "sudo -u scriptuser bash -c \"ls -al\""

Однако, если я попытаюсь запустить более сложную команду, например [[ -d "/tmp/Some directory" ]] && rm -rf "/tmp/Some directory" У меня быстро возникают проблемы с цитированием. Я не уверен, как передать этот пример сложной команды в bash -c, когда \" уже разграничивает границы передаваемой мной команды (поэтому я не знаю, как указать в кавычках / tmp / Some каталог, который включает пробелы.

Есть ли общее решение, позволяющее мне передавать любую команду независимо от того, насколько сложным / сумасшедшим является цитирование, или это какое-то ограничение, которого я достиг? Есть ли другие возможные и, возможно, более читаемые решения?

Иногда я использую уловку: использовать base64 для кодирования команд и передавать их в bash на другом сайте:

MYCOMMAND=$(base64 -w0 script.sh)
ssh user@remotehost "echo $MYCOMMAND | base64 -d | sudo bash"

Это закодирует сценарий с любыми запятыми, обратными косыми чертами, кавычками и переменными внутри безопасной строки и отправит его на другой сервер. (-w0 требуется для отключения переноса строк, что по умолчанию происходит в столбце 76). С другой стороны, $(base64 -d) расшифрует скрипт и передаст его в bash для выполнения.

У меня никогда не возникало проблем с этим, каким бы сложным ни был сценарий. Решает проблему с побегом, потому что вам ничего не нужно убегать. Он не создает файл на удаленном хосте, и вы можете легко запускать очень сложные сценарии.

Увидеть -tt вариант? Прочтите ssh(1) руководство.

ssh -tt root@host << EOF
sudo some # sudo shouldn't ask for a password, otherwise, this fails. 
lines
of
code 
but be careful with \$variables
and \$(other) \`stuff\`
exit # <- Important. 
EOF

Я часто использую vim и использую :!cat % | ssh -tt somemachine уловка.

Я думаю, что самое простое решение заключается в модификации комментария @ thanasisk.

Создайте сценарий, scp его к машине, затем запустите его.

Есть сценарий rm сам в начале. Оболочка открыла файл, поэтому он загружен и может быть удален без проблем.

Выполняя действия в этом порядке (rm во-первых, во-вторых, другие вещи) он даже будет удален, если в какой-то момент он выйдет из строя.

Вы можете использовать %q спецификатор формата с printf позаботиться об экранировании переменной за вас:

cmd="ls -al"
printf -v cmd_str '%q' "$cmd"
ssh user@host "bash -c $cmd_str"

printf -v записывает вывод в переменную (в этом случае $cmd_str). Я считаю, что это самый простой способ сделать это. Нет необходимости передавать какие-либо файлы или кодировать командную строку (насколько мне нравится этот трюк).

Вот более сложный пример, показывающий, что это работает и с квадратными скобками и амперсандами:

$ ssh user@host "ls -l test"
-rw-r--r-- 1 tom users 0 Sep  4 21:18 test
$ cmd="[[ -f test ]] && echo 'this really works'"
$ printf -v cmd_str '%q' "$cmd"
$ ssh user@host "bash -c $cmd_str"
this really works

Я не тестировал это с sudo но это должно быть так просто, как:

ssh user@host "sudo -u scriptuser bash -c $cmd_str"

Если хотите, можете пропустить шаг и не создавать промежуточную переменную:

$ ssh user@host "bash -c $(printf '%q' "$cmd")"
this really works

Или даже просто не создавать переменную полностью:

ssh user@host "bash -c $(printf '%q' "[[ -f test ]] && echo 'this works as well'")"

ОБНОВЛЕНИЕ: в примерах теперь явно используется sudo.

Вот способ использовать синтаксис Bash с составными присваиваниями для выполнения произвольно сложных команд через SSH с помощью sudo:

CMD=$( cat <<'EOT'
echo "Variables like '${HOSTNAME}' and commands like $( whoami )"
echo "will be interpolated on the server, thanks to the single quotes"
echo "around 'EOT' above."
EOT
) \
SSHCMD=$(
  printf 'ssh -tq myuser@hostname sudo -u scriptuser bash -c %q' "${CMD}" ) \
bash -c '${SSHCMD}'

CMD=$( cat <<EOT
echo "If you want '${HOSTNAME}' and $( whoami ) to be interpolated"
echo "on the client instead, omit the the single quotes around EOT."
EOT
) \
SSHCMD=$(
  printf 'ssh -tq myuser@hostname sudo -u scriptuser bash -c %q' "${CMD}" ) \
bash -c '${SSHCMD}'

Правильное форматирование команд для их динамического выполнения в другом месте (например, вложенные оболочки, удаленные оболочки SSH и т. Д.) Может быть очень сложным - см. https://stackoverflow.com/a/53197638/111948 для хорошего описания того, почему. Сложные структуры bash, такие как синтаксис выше, могут помочь этим командам работать правильно.

Для получения дополнительной информации о том, как cat и << объединить, чтобы сформировать встроенный здесь документы, посмотри пожалуйста https://stackoverflow.com/a/21761956/111948

Вы знаете, что можете использовать sudo чтобы предоставить вам оболочку, в которой вы можете запускать команды от имени выбранного пользователя?

-i, --login

Запустите оболочку, указанную в записи базы данных паролей целевого пользователя, в качестве оболочки входа. Это означает, что файлы ресурсов для входа в систему, такие как .profile или .login, будут прочитаны оболочкой. Если команда указана, она передается в оболочку для выполнения с помощью параметра -c оболочки. Если команда не указана, выполняется интерактивная оболочка. sudo пытается перейти в домашний каталог этого пользователя перед запуском оболочки. Команда запускается в среде, аналогичной той, которую пользователь получит при входе в систему. В разделе Command Environment в руководстве sudoers (5) описано, как параметр -i влияет на среду, в которой выполняется команда, когда используется политика sudoers.

Вы можете определить сценарий на своем локальном компьютере, а затем cat и подключите его к удаленному компьютеру:

user@host:~/temp> echo "echo 'Test'" > fileForSsh.txt
user@host:~/temp> cat fileForSsh.txt | ssh localhost

Pseudo-terminal will not be allocated because stdin is not a terminal.
stty: standard input: Invalid argument
Test

Просто и легко.

ssh user @ servidor "bash -s" <script.sh

Если вы используете достаточно современную оболочку Bash, вы можете поместить свои команды в функцию и распечатать эту функцию в виде строки, используя export -p -f function_name. Результат этой строки может затем содержать произвольные команды, которые не обязательно должны запускаться от имени пользователя root.

Пример использования труб из мой ответ на Unix.SE:

#!/bin/bash
remote_main() {
   local dest="$HOME/destination"

   tar xzv -C "$dest"
   chgrp -R www-data "$dest"
   # Ensure that newly written files have the 'www-data' group too
   find "$dest" -type d -exec chmod g+s {} \;
}
tar cz files/ | ssh user@host "$(declare -pf remote_main); remote_main"

Пример, который извлекает, сохраняет файл для пользователя, вошедшего в систему, и устанавливает программу в корневом каталоге:

remote_main() {
    wget https://example.com/screenrc -O ~/.screenrc
    sudo apt-get update && sudo apt-get install screen
}
ssh user@host "$(declare -pf remote_main); remote_main"

Если вы хотите запустить всю команду, используя sudo, вы можете использовать это:

remote_main() {
    wget https://example.com/screenrc -O ~user/.screenrc
    apt-get update && apt-get install screen
}
ssh user@host "$(declare -pf remote_main);
    sudo sh -c \"\$(declare -pf remote_main); remote_cmd\""
# Alternatively, if you don't need stdin and do not want to log the command:
ssh user@host "$(declare -pf remote_main);
    (declare -pf remote_main; echo remote_cmd) | sudo sh"

Вот мое (не совсем проверенное) дерьмовое решение для этого:

#!/usr/bin/env ruby
# shell-escape: Escape each argument.
ARGV.each do|a|
  print " '#{a.gsub("'","\'\\\\'\'")}' "
end

Не умеешь:

ssh -tq myuser@hostname "$(shell-escape sudo -u scriptuser bash -c "$(shell-escape ls -al)")"

Следующим шагом будет создание betterssh скрипт, который это уже делает:

betterssh -tq myuser@hostname sudo -u scriptuser bash -c "$(shell-escape ls -al)"

Для тех из вас, кому нужно передать параметры скрипту, это должно быть следующим:

ssh user@remotehost "echo `base64 -w0 script.sh` | base64 -d | sudo bash -s <param1> <param2> <paramN>"

Сначала создайте локальный сценарий, а затем выполните его удаленно с помощью следующей команды:

cat <Local Script.sh> | ssh user@server "cat - | sudo -u <user> /bin/bash"