Pregunta ¿Cómo puedo ejecutar un comando arbitrariamente complejo utilizando sudo sobre ssh?


Tengo un sistema en el que solo puedo iniciar sesión con mi nombre de usuario (mi usuario), pero necesito ejecutar comandos como otro usuario (usuario del script). Hasta ahora, he creado lo siguiente para ejecutar los comandos que necesito:

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

Sin embargo, si intento ejecutar un comando más complejo, como [[ -d "/tmp/Some directory" ]] && rm -rf "/tmp/Some directory" Rápidamente me meto en problemas con la cita. No estoy seguro de cómo podría pasar este ejemplo de comando complejo a bash -c, cuando \" ya delimita los límites del comando que estoy pasando (y, por lo tanto, no sé cómo citar / tmp / algún directorio, que incluye espacios).

¿Hay una solución general que me permita pasar cualquier comando sin importar cuán compleja / loca sea la cita, o es esta una limitación que he alcanzado? ¿Hay otras soluciones posibles y quizás más legibles?


76
2017-09-02 09:21


origen


Copie un script a $ remote y luego ejecútelo. - Iain
Eso funcionaría, pero encuentro una solución que no deja ningún archivo de script (en caso de que algo falle, etc.) sería un poco más limpio. - VoY
tener el script rm al final de cada ejecución - thanasisk
Considera usar tela fabfile.org. Puede hacer tu vida mucho más fácil si tienes que sudo remotamente mucho. - Nils Toedtmann
Si tiene instalado Ansible, este comando muestra la documentación para su caso de uso: ansible-doc script - bbaassssiiee


Respuestas:


Un truco que uso a veces es usar base64 para codificar los comandos y canalizarlo para golpear en el otro sitio:

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

Esto codificará el script, con comas, barras diagonales inversas, comillas y variables dentro de una cadena segura, y lo enviará al otro servidor. (-w0 es necesario para deshabilitar el ajuste de línea, lo que sucede en la columna 76 de forma predeterminada). Por otro lado, $(base64 -d) decodificará el script y lo alimentará a bash para ser ejecutado.

Nunca tuve ningún problema con eso, no importa lo complejo que fuera el script. Resuelve el problema de escapar, porque no necesitas escapar de nada. No crea un archivo en el host remoto, y puede ejecutar scripts muy complicados con facilidad.


138
2017-09-02 13:10



O incluso: ssh user@remotehost "echo `base64 -w0 script.sh` | base64 -d | sudo bash" - Niklas B.
Vale la pena señalar que en la mayoría de los casos puede sustituir a lzop o gzip por base64 para tiempos de transferencia más rápidos. Sin embargo, puede haber casos de borde. YMMV. - CodeGnome
Buena nota, pero si se trata de un script, no espero que ninguna transferencia sea más que unos pocos kb. - ThoriumBR
Hay un uso inútil de eco aquí también. base64 script | ssh remotehost 'base64 -d | sudo bash' seria suficiente :) - hobbs
Probado hoy y perfectamente trabajado. Gracias amigos. - Soham Chakraborty


Ver el -tt ¿opción? Leer el ssh(1) manual.

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 que hago a menudo es usar vim y usar el :!cat % | ssh -tt somemachine truco.


32
2017-09-02 13:01



Por supuesto :!cat % | (command) se puede hacer como :w !(command) o :!(command) < %. - Scott
Sí. Mi linea es una abuso de gato - moebius_eye


Creo que la solución más fácil radica en una modificación del comentario de @ thanasisk.

Crear un guión, scp A la máquina, luego ejecútalo.

Tener el guion rm sí al inicio. El shell ha abierto el archivo, por lo que se ha cargado, y luego puede eliminarse sin problemas.

Al hacer las cosas en este orden (rm primero, otras cosas segundo) incluso se eliminará cuando falle en algún momento.


9
2017-09-02 11:26





Puedes usar el %q especificador de formato con printf Para cuidar de la variable que escapa por ti:

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

printf -v escribe la salida a una variable (en este caso, $cmd_str). Creo que esta es la forma más sencilla de hacerlo. No es necesario transferir ningún archivo ni codificar la cadena de comandos (por mucho que me guste el truco).

Aquí hay un ejemplo más complejo que muestra que también funciona con corchetes y símbolos:

$ 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

No lo he probado con sudo pero debería ser tan simple como:

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

Si lo desea, puede omitir un paso y evitar crear la variable intermedia:

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

O incluso simplemente evitar crear una variable por completo:

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

7
2017-09-04 20:10



Esta es una solución impresionante. De hecho, tuve que usar printf antes para una situación similar, pero siempre me olvido. Gracias por publicar esto :-) - Jon L.


Aquí está la forma "correcta" (sintáctica) de ejecutar algo como esto en 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
)"

Para obtener una explicación detallada de cómo funciona esto, consulte https://stackoverflow.com/a/21761956/111948


6
2018-05-28 17:48





¿Eres consciente de que puedes usar? sudo para darle un shell donde puede ejecutar comandos como el usuario elegido?

-i, --login

Ejecute el shell especificado por la entrada de la base de datos de contraseñas del usuario de destino como shell de inicio de sesión. Esto significa que los archivos de recursos específicos de inicio de sesión, como                    .profile o .login serán leídos por el shell. Si se especifica un comando, se pasa al shell para su ejecución a través de la opción -c del shell. Si no                    Se especifica el comando, se ejecuta un shell interactivo. sudo intenta cambiar al directorio de inicio de ese usuario antes de ejecutar el shell. El COM-                    mand se ejecuta con un entorno similar al que un usuario recibiría al iniciar sesión. La sección de Entorno de Comando en los documentos de sudoers (5) manual                    menta cómo la opción -i afecta al entorno en el que se ejecuta un comando cuando la política sudoers está en uso.


4
2017-09-03 09:22



Esto me parece la solución obvia para mí. > sudo -i -u brian - code_monk
Eso funciona mejor cuando se combina con "ssh -t" en el caso de acceso remoto. Que se incluye en la pregunta, pero se repite para la gente de "No puedo leer esta / totalidad / página". :) - dannysauer


Puede definir el script en su máquina local y luego cat y canalizarlo a la máquina remota:

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

3
2017-09-02 09:39



UUOC puedes usar < - Iain
¿Puedes modificar el ejemplo para incluir? sudo -u scriptuser? ¿Se podría utilizar heredoc considerando que necesito tener variables en el script que estaría transmitiendo a la máquina? - VoY
Estaba intentando: echo "sudo `cat fileForSsh.txt`" | ssh ... pero sigo recibiendo sudo: sorry, you must have a tty to run sudo. - jas_raj
A pesar de que esta pregunta puede ayudar si puede obtener el /etc/sudoers archivo cambiado - jas_raj
Esto funciona para mí: ssh -tq user@host "sudo bash -s" < test.sh - AVee


Simple y fácil.

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


1
2017-09-03 20:15