Vraag Hoe kan ik een willekeurig complexe opdracht uitvoeren met sudo via ssh?


Ik heb een systeem dat ik alleen kan aanmelden onder mijn gebruikersnaam (myuser), maar ik moet opdrachten uitvoeren als andere gebruiker (scriptuser). Tot nu toe heb ik het volgende bedacht om de opdrachten uit te voeren die ik nodig heb:

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

Als ik echter een complexere opdracht probeer uit te voeren, zoals [[ -d "/tmp/Some directory" ]] && rm -rf "/tmp/Some directory" Ik kom snel in de problemen met het citeren. Ik weet niet zeker hoe ik dit complexe voorbeeldcommando aan zou kunnen geven bash -c, wanneer \" definieert al de grenzen van de opdracht die ik passeer (en dus weet ik niet hoe ik / tmp / Some directory moet citeren, die spaties bevat.

Is er een algemene oplossing waardoor ik elke opdracht kan doorgeven, ongeacht hoe complex / gek de aanhaling is, of is dit een soort van beperking die ik heb bereikt? Zijn er andere mogelijke en misschien beter leesbare oplossingen?


76
2017-09-02 09:21


oorsprong


Kopieer een script naar $ remote en voer het vervolgens uit. - Iain
Dat zou werken, maar ik vind een oplossing die geen scriptbestanden achterlaat (voor het geval iets faalt enz.) Een beetje schoner zou zijn. - VoY
laat het script zichzelf aan het einde van elke run rmnen - thanasisk
Overweeg om stof te gebruiken fabfile.org. Kan je het leven veel gemakkelijker maken als je veel op afstand moet sudo. - Nils Toedtmann
Als u Ansible hebt geïnstalleerd, geeft dit bevel de documentatie voor uw use case weer: ansible-doc script - bbaassssiiee


antwoorden:


Een truc die ik soms gebruik, is om base64 te gebruiken om de commando's te coderen, en het naar bash te pipen op de andere site:

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

Dit codeert het script, met komma's, backslashes, aanhalingstekens en variabelen binnen een veilige reeks, en stuurt het naar de andere server. (-w0 is vereist om lijnomloop uit te schakelen, wat standaard gebeurt in kolom 76). Aan de andere kant, $(base64 -d) zal het script decoderen en het naar bash sturen om te worden uitgevoerd.

Ik heb er nooit problemen mee gehad, hoe ingewikkeld het script ook was. Lost het probleem met ontsnappen op, omdat je nergens aan hoeft te ontsnappen. Er wordt geen bestand op de externe host gemaakt en u kunt eenvoudig ingewikkelde scripts uitvoeren.


138
2017-09-02 13:10



Of zelfs: ssh user@remotehost "echo `base64 -w0 script.sh` | base64 -d | sudo bash" - Niklas B.
Het is vermeldenswaard dat in de meeste gevallen u lzop of gzip kunt vervangen door base64 voor snellere overdrachtstijden. Er kunnen echter randgevallen zijn. YMMV. - CodeGnome
Goede opmerking, maar als het een script is, verwacht ik niet dat een overdracht meer dan een paar kb zal zijn. - ThoriumBR
Er is hier ook een nutteloos gebruik van echo. base64 script | ssh remotehost 'base64 -d | sudo bash' zou genoeg zijn :) - hobbs
Vandaag getest en perfect gewerkt. Bedankt mensen. - Soham Chakraborty


Zie de -tt keuze? Lees de ssh(1) met de hand.

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

Een ding dat ik vaak doe, is vim gebruiken en het gebruiken :!cat % | ssh -tt somemachine truc.


32
2017-09-02 13:01



Natuurlijk :!cat % | (command) kan als gedaan worden :w !(command) of :!(command) < %. - Scott
Ja. Mijn lijn is a kattenmishandeling - moebius_eye


Ik denk dat de eenvoudigste oplossing ligt in een wijziging van de opmerking van @ thanasisk.

Maak een script, scp het naar de machine en voer het vervolgens uit.

Heb het script rm zelf aan het begin. De shell heeft het bestand geopend, dus het is geladen en kan vervolgens zonder problemen worden verwijderd.

Door dingen in deze volgorde te doen (rm eerst andere dingen als tweede) het wordt zelfs verwijderd als het op een bepaald moment faalt.


9
2017-09-02 11:26





U kunt de %q format specifier met printf om ervoor te zorgen dat de variabele voor u ontsnapt:

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

printf -v schrijft de uitvoer naar een variabele (in dit geval $cmd_str). Ik denk dat dit de eenvoudigste manier is om het te doen. Het is niet nodig om bestanden over te dragen of de opdrachtstring te coderen (zo goed als ik de truc wel leuk vind).

Hier is een meer complex voorbeeld dat laat zien dat het ook werkt voor dingen zoals vierkante haken en ampersands:

$ 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

Ik heb het niet getest met sudo maar het zou zo simpel moeten zijn als:

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

Als u wilt, kunt u een stap overslaan en voorkomen dat u de tussenvariabele maakt:

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

Of vermijd zelfs om een ​​variabele volledig te maken:

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

7
2017-09-04 20:10



dit is een geweldige oplossing. Ik heb PrintF eerder moeten gebruiken voor een soortgelijke situatie, maar ik vergeet het altijd. Bedankt voor het plaatsen van dit :-) - Jon L.


Dit is de "juiste" (syntactische) manier om iets als dit in bash uit te voeren:

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
)"

Zie voor een uitgebreide uitleg over hoe dit werkt https://stackoverflow.com/a/21761956/111948


6
2018-05-28 17:48





Weet je dat je kunt gebruiken sudo om je een shell te geven waar je opdrachten kunt uitvoeren als de gekozen gebruiker?

-i, --login

Voer de shell uit die is opgegeven door het wachtwoorddatabase-item van de doelgebruiker als een login-shell. Dit betekent dat inlogspecifieke bronbestanden zoals                    .profile of .login worden door de shell gelezen. Als een opdracht is opgegeven, wordt deze doorgegeven aan de shell voor uitvoering via de shell -c optie. Als Nee                    opdracht is opgegeven, wordt een interactieve shell uitgevoerd. sudo probeert te wijzigen naar de basismap van die gebruiker voordat de shell wordt uitgevoerd. De                    mand wordt uitgevoerd met een omgeving die lijkt op de omgeving die een gebruiker zou krijgen bij het inloggen. Het gedeelte Opdrachtomgeving in de handleiding sudoers (5) handleiding                    hoe de optie -i de omgeving beïnvloedt waarin een opdracht wordt uitgevoerd wanneer het sudoers-beleid in gebruik is.


4
2017-09-03 09:22



dit lijkt mij de voor de hand liggende oplossing. > sudo -i -u brian - code_monk
Dat werkt het best in combinatie met "ssh -t" in het geval van externe toegang. Die is opgenomen in de vraag, maar wordt herhaald voor de "Ik kan deze / hele / pagina" -groepen niet lezen. :) - dannysauer


U kunt het script op uw lokale computer definiëren en vervolgens cat en pijp het naar de externe machine:

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 die u kunt gebruiken < - Iain
Kunt u het voorbeeld aanpassen om op te nemen sudo -u scriptuser? Zou heredoc bruikbaar zijn gezien het feit dat ik variabelen nodig heb in het script dat ik naar de machine zou leiden? - VoY
Ik was aan het proberen: echo "sudo `cat fileForSsh.txt`" | ssh ... maar ik blijf het krijgen sudo: sorry, you must have a tty to run sudo. - jas_raj
Hoewel deze vraag kan helpen als je de /etc/sudoers bestand veranderd - jas_raj
Dit werkt voor mij: ssh -tq user@host "sudo bash -s" < test.sh - AVee


Eenvoudig en gemakkelijk.

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


1
2017-09-03 20:15





Als u een voldoende moderne Bash-shell gebruikt, kunt u uw opdrachten in een functie plaatsen en die functie als een tekenreeks met behulp van export -p -f function_name. Het resultaat van die reeks kan dan willekeurige opdrachten bevatten die niet noodzakelijkerwijs als root hoeven te worden uitgevoerd.

Een voorbeeld met pijpen uit mijn antwoord op 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"

Een voorbeeld dat wordt opgehaald, slaat het bestand op voor de inloggebruiker en installeert een programma een hoofdmap:

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"

Als u het hele commando wilt gebruiken met sudo, je zou dit kunnen gebruiken:

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"

0
2017-09-04 09:59





Hier is mijn (niet echt geteste) waardeloze oplossing voor dit:

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

Niet wat je kunt doen:

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

De volgende stap is het maken van een betterssh script dat dit al doet:

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

0
2017-09-06 08:19