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


80

У меня есть система, в которую я могу войти только под своим именем пользователя (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 каталог, который содержит пробелы.

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


11
Скопируйте скрипт в $ remote и выполните его.
user9517

Это бы сработало, но я нашел решение, которое не оставляло бы никаких файлов скриптов (на случай, если что-то не работает и т. Д.) Было бы немного чище.
VoY

9
есть сценарий rm в конце каждого запуска
thanasisk

3
Рассмотрите возможность использования ткани fabfile.org . Может сделать вашу жизнь намного проще, если вам приходится много судить удаленно.
Нильс Тёдтманн

2
Если у вас установлен Ansible, эта команда показывает документацию для вашего
варианта

Ответы:


146

Уловка, которую я иногда использую, состоит в том, чтобы использовать base64 для кодирования команд и передать его в bash на другом сайте:

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

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

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


2
Или даже:ssh user@remotehost "echo `base64 -w0 script.sh` | base64 -d | sudo bash"
Никлас Б.

1
Стоит отметить, что в большинстве случаев вы можете заменить lzop или gzip на base64 для ускорения передачи данных. Тем не менее, могут быть крайние случаи. YMMV.
CodeGnome

1
Хорошая заметка, но если это скрипт, я не ожидаю, что какой-либо перевод будет больше, чем несколько КБ.
ThoriumBR

7
Здесь тоже бесполезное использование эха. base64 script | ssh remotehost 'base64 -d | sudo bash'было бы достаточно :)
Хоббс

Хотя я не тестировал это решение, будет ли он выводить результат скрипта, например, 'yum check-update' в стандартный вывод? Я хотел спросить, потому что, если кто-то думает, что я делаю что-то явно наивное, я могу взять несколько вещей.
Сохам Чакраборти

34

Видишь -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трюк.


3
Конечно :!cat % | (command)можно сделать как :w !(command)или :!(command) < %.
Скотт

2
Да. Моя линия - жестокое обращение
moebius_eye

выполнение ssh -ttзаставляет мой ssh ​​не завершать работу после выполнения определенных команд (добавление exitв конце не помогает)

10

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

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

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

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


8

Вы можете использовать %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'")"

1
это потрясающее решение. Я действительно должен был использовать printf раньше для аналогичной ситуации, но я всегда забываю об этом. Спасибо за публикацию этого сообщения :-)
Джон Л.

8

Вот «правильный» (синтаксический) способ выполнить что-то подобное в bash:

ssh user@server "$( cat <<'EOT'
echo "Variables like '${HOSTNAME}' and commands like $( uname -a )"
echo "will be interpolated on the server, thanks to the single quotes"
echo "around 'EOT' above.
EOT
)"

ssh user@server "$( cat <<EOT
echo "If you want '${HOSTNAME}' and $( uname -a ) to be interpolated"
echo "on the client instead, omit the the single quotes around EOT."
EOT
)"

Подробное объяснение того, как это работает, см. На странице https://stackoverflow.com/a/21761956/111948.


5

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

-i, --login

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


это кажется очевидным решением для меня. > Судо -и

Это лучше всего работает в сочетании с «ssh -t» в случае удаленного доступа. Что входит в вопрос, но стоит повторить для людей "Я не могу прочитать эту / всю страницу". :)
dannysauer

4

Вы можете определить скрипт на локальном компьютере, а затем 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

5
UUOC вы можете использовать <
user9517

Можете ли вы изменить пример, чтобы включить sudo -u scriptuser? Можно ли использовать heredoc, учитывая, что в сценарии, который я бы передавал на машину, должны быть переменные?
VoY

Я пытался: echo "sudo `cat fileForSsh.txt`" | ssh ...но я продолжаю получать sudo: sorry, you must have a tty to run sudo.
jas_raj

Хотя этот вопрос может помочь, если вы можете /etc/sudoersизменить файл
jas_raj

1
Это работает для меня:ssh -tq user@host "sudo bash -s" < test.sh
AVee


1

Если вы используете достаточно современную оболочку 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"

1

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

#!/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)"

1

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

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

1

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

cat <Local Script.sh> | ssh user@server "cat - | sudo -u <user> /bin/bash"
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.