Як я можу запустити довільно складну команду за допомогою sudo над ssh?


80

У мене є система, до якої я можу входити лише під своїм ім'ям користувача (myuser), але мені потрібно запускати команди як інший користувач (сценарій). Поки що я придумав наступне для запуску потрібних мені команд:

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 та виконайте його.
користувач9517

Це спрацювало б, але я знаходжу рішення, яке не залишатиме позаду файлів сценаріїв (у випадку, якщо щось виходить з ладу тощо) було б трохи чистішим.
VoY

9
мати сценарій rm у кінці кожного запуску
thanasisk

3
Подумайте про використання тканини fabfile.org . Може значно полегшити вам життя, якщо вам доведеться багато судо віддалено.
Нілс Тодтманн

2
Якщо у вас встановлено Ansible, ця команда показує документацію для вашого випадку використання: скрипт ansible-doc
bbaassssiiee

Відповіді:


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'вистачило б :)
hobbs

Поки я не перевіряв це рішення, чи буде він виводити результат сценарію, скажімо, наприклад, "yum check-update" в stdout? Я хотів запитати, бо якщо хтось думає, що я роблю щось, очевидно, наївне, я можу підібрати кілька речей.
Сохам Чакраборті

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оболонкою, де можна виконувати команди як обраний користувач?

-і, --логін

Запустіть оболонку, вказану вводом бази даних пароля цільового користувача, як оболонку для входу. Це означає, що оболонки зчитуються специфічними для входу файлами ресурсів, такими як .profile або .login. Якщо вказана команда, вона передається в оболонку для виконання через опцію -c оболонки. Якщо команда не вказана, виконується інтерактивна оболонка. sudo намагається змінити домашній каталог цього користувача перед запуском оболонки. Команда виконується з оточенням, подібним до того, яке отримував би користувач під час входу. Розділ «Команда довкілля» в ручному документі «Судори» (5) визначає, як опція -i впливає на середовище, в якому виконується команда, коли застосовується політика судорів.


це здається очевидним рішенням для мене. > sudo -i -u brian
code_monk

Це найкраще працює в поєднанні з "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. Результат цього рядка може містити довільні команди, які не обов'язково повинні виконуватись як корінь.

Приклад використання труб з моєї відповіді на 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"

Приклад, який отримує, зберігає файл для користувача, який входить у систему, і встановлює програму root:

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

Спочатку створіть локальний скрипт, а потім виконайте його віддалено, використовуючи команду follow:

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