Come posso eseguire comandi arbitrariamente complessi usando sudo su ssh?


80

Ho un sistema a cui posso accedere solo con il mio nome utente (myuser), ma devo eseguire i comandi come altro utente (scriptuser). Finora, ho elaborato quanto segue per eseguire i comandi di cui ho bisogno:

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

Se tuttavia, quando provo ad eseguire un comando più complesso, come ad esempio [[ -d "/tmp/Some directory" ]] && rm -rf "/tmp/Some directory"mi metto rapidamente nei guai con la citazione. Non sono sicuro di come potrei passare questo comando di esempio complesso bash -c, quando \"delimita già i limiti del comando che sto passando (e quindi non so come citare / tmp / Some directory, che include uno spazio.

Esiste una soluzione generale che mi consente di passare qualsiasi comando, non importa quanto sia complessa / folle la citazione, o è una sorta di limitazione che ho raggiunto? Esistono altre soluzioni possibili e forse più leggibili?


11
Copia uno script su $ remote, quindi eseguilo.
user9517

Funzionerebbe, ma trovo che una soluzione che non lasci dietro i file di script (nel caso qualcosa fallisca ecc.) Sarebbe un po 'più pulita.
VoY

9
avere lo script rm stesso alla fine di ogni esecuzione
thanasisk

3
Valuta di usare fabric fabfile.org . Può renderti la vita molto più semplice se devi fare molto da remoto.
Nils Toedtmann,

2
Se hai installato Ansible questo comando mostra la documentazione per il tuo caso d'uso: ansible-doc script
bbaassssiiee

Risposte:


146

Un trucco che uso a volte è usare base64 per codificare i comandi e reindirizzarlo per bash sull'altro sito:

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

Questo codificherà lo script, con eventuali virgole, barre rovesciate, virgolette e variabili all'interno di una stringa sicura e lo invierà all'altro server. ( -w0è necessario per disabilitare il ritorno a capo, che si verifica nella colonna 76 per impostazione predefinita). Dall'altro lato, $(base64 -d)decodificherà lo script e lo alimenterà a bash per essere eseguito.

Non ho mai avuto problemi con esso, non importa quanto fosse complessa la sceneggiatura. Risolve il problema con la fuga, perché non è necessario sfuggire a nulla. Non crea un file sull'host remoto e puoi eseguire facilmente script estremamente complicati.


2
O anche:ssh user@remotehost "echo `base64 -w0 script.sh` | base64 -d | sudo bash"
Niklas B.

1
Vale la pena notare che nella maggior parte dei casi è possibile sostituire lzop o gzip con base64 per tempi di trasferimento più rapidi. Tuttavia, potrebbero esserci casi limite. YMMV.
CodeGnome

1
Una buona nota, ma se si tratta di uno script, non mi aspetto che alcun trasferimento sia superiore a pochi kb.
TorioBR

7
Anche qui c'è un uso inutile di eco. base64 script | ssh remotehost 'base64 -d | sudo bash'basterebbe :)
hobbs il

Anche se non ho testato questa soluzione, produrrà il risultato dello script, ad esempio "yum check-update" nello stdout? Volevo chiedere perché se qualcuno pensa che stia facendo qualcosa di ovviamente ingenuo, posso raccogliere alcune cose.
Soham Chakraborty,

34

Vedi l' -ttopzione? Leggi il ssh(1)manuale

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

Una cosa che faccio spesso è usare vim e usare il :!cat % | ssh -tt somemachinetrucco.


3
Naturalmente si :!cat % | (command)può fare come :w !(command)o :!(command) < %.
Scott,

2
Sì. La mia linea è un abuso di gatto
moebius_eye,

in esecuzione ssh -ttfa sì che il mio ssh non si interrompa dopo l'esecuzione di alcuni comandi (l'aggiunta exitalla fine non aiuta)

10

Penso che la soluzione più semplice risieda in una modifica del commento di @ thanasisk.

Crea uno script, scpeseguilo sulla macchina, quindi eseguilo.

Avere lo script rmstesso all'inizio. La shell ha aperto il file, quindi è stato caricato e può quindi essere rimosso senza problemi.

Facendo le cose in questo ordine ( rmprima, altre cose secondo) verrà anche rimosso quando fallisce ad un certo punto.


8

Puoi utilizzare l' %qidentificatore di formato con printfper occuparti della variabile escape per te:

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

printf -vscrive l'output in una variabile (in questo caso, $cmd_str). Penso che questo sia il modo più semplice per farlo. Non è necessario trasferire alcun file o codificare la stringa di comando (per quanto mi piaccia il trucco).

Ecco un esempio più complesso che mostra che funziona anche con parentesi quadre e e commerciali:

$ 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

Non l'ho provato con, sudoma dovrebbe essere semplice come:

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

Se lo desideri, puoi saltare un passaggio ed evitare di creare la variabile intermedia:

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

O anche semplicemente evitare di creare una variabile interamente:

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

1
questa è una soluzione fantastica. In realtà ho dovuto usare printf prima per una situazione simile, ma me ne dimentico sempre. Grazie per aver pubblicato questo :-)
Jon L.

8

Ecco il modo "corretto" (sintattico) di eseguire qualcosa di simile in 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
)"

Per una spiegazione approfondita di come funziona, consultare https://stackoverflow.com/a/21761956/111948


5

Sai che puoi usare sudoper darti una shell in cui puoi eseguire comandi come l'utente scelto?

-i, --login

Eseguire la shell specificata dalla voce del database delle password dell'utente di destinazione come shell di accesso. Ciò significa che i file di risorse specifici per l'accesso come .profile o .login verranno letti dalla shell. Se viene specificato un comando, viene passato alla shell per l'esecuzione tramite l'opzione -c della shell. Se non viene specificato alcun comando, viene eseguita una shell interattiva. sudo tenta di passare alla home directory dell'utente prima di eseguire la shell. Il comando viene eseguito con un ambiente simile a quello che un utente riceverebbe al momento del login. La sezione Ambiente di comando nei documenti manuali sudoers (5) in che modo l'opzione -i influenza l'ambiente in cui viene eseguito un comando quando la politica sudoers è in uso.


questa mi sembra la soluzione ovvia. > sudo -i -u brian
code_monk

Funziona meglio se combinato con "ssh -t" in caso di accesso remoto. Il che è incluso nella domanda, ma vale la pena ripetere per la gente "Non riesco a leggere questa / intera / pagina". :)
dannysauer,

4

È possibile definire lo script sul computer locale e quindi catinviarlo al computer remoto:

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 è possibile utilizzare <
user9517

Puoi modificare l'esempio da includere sudo -u scriptuser? Heredoc sarebbe utilizzabile se avessi bisogno di avere delle variabili nello script che avrei inviato alla macchina?
VoY

Ci stavo provando: echo "sudo `cat fileForSsh.txt`" | ssh ...ma continuo a ricevere sudo: sorry, you must have a tty to run sudo.
jas_raj,

Sebbene questa domanda possa essere utile se è possibile ottenere il /etc/sudoersfile modificato
jas_raj

1
Questo per me funziona:ssh -tq user@host "sudo bash -s" < test.sh
Guarda il

3

Semplice e facile.

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


1

Se usi una shell Bash sufficientemente moderna, puoi mettere i tuoi comandi in una funzione e stampare quella funzione come stringa usando export -p -f function_name. Il risultato di quella stringa può quindi contenere comandi arbitrari che non devono necessariamente essere eseguiti come root.

Un esempio usando le pipe dalla mia risposta su 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"

Un esempio che recupera salva il file per l'utente di accesso e installa un programma come 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"

Se si desidera eseguire l'intero comando utilizzando sudo, è possibile utilizzare questo:

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

Ecco la mia soluzione scadente (non realmente testata) per questo:

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

Non puoi fare:

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

Il prossimo passo è creare uno bettersshscript che già lo fa:

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

1

Per quelli di voi che hanno bisogno di passare parametri allo script dovrebbe essere il seguente:

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

1

Innanzitutto, crea uno script locale e quindi eseguilo in remoto utilizzando il seguente comando:

cat <Local Script.sh> | ssh user@server "cat - | sudo -u <user> /bin/bash"
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.