Soru Sudo üzerinden sudo kullanarak keyfi karmaşık komutu nasıl çalıştırabilirim?


Sadece kullanıcı adımımın (myuser) altında oturum açabileceğim bir sistem var, fakat diğer kullanıcılarla (scriptuser) komutları çalıştırmam gerekiyor. Şimdiye kadar, ihtiyacım olan komutları çalıştırmak için aşağıdakileri buldum:

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

Ancak, daha karmaşık bir komut çalıştırmayı denediğimde [[ -d "/tmp/Some directory" ]] && rm -rf "/tmp/Some directory" Alıntı ile çabucak başımı belaya sokarım. Bu örnek karmaşık komutu nasıl geçeceğimi bilmiyorum bash -c, ne zaman \" zaten geçtiğim komutun sınırlarını daraltıyor (ve bu yüzden / tmp / quote / tmp / nasıl yazılacağını bilmiyorum.

Teklifin ne kadar karmaşık / çılgın olduğuna bakılmaksızın herhangi bir komutu iletmeme izin veren genel bir çözüm var mı, yoksa bu bir çeşit sınırlama mı? Başka olası ve belki de daha okunaklı çözümler var mı?


76
2017-09-02 09:21


Menşei


Bir komut dosyasını $ remote'a kopyalayın, ardından çalıştırın. - Iain
Bu işe yarayacak, ancak hiçbir komut dosyası bırakmayan bir çözüm buluyorum (bir şeylerin başarısızlığı durumunda vb.) Biraz daha temiz olacaktır. - VoY
her çalışmanın sonunda komut dosyasını kendiliğinden - thanasisk
Kumaş kullanmayı düşünün fabfile.org. Eğer uzaktan çok fazla sudo gerekiyorsa, hayatı daha kolaylaştırabilir. - Nils Toedtmann
Ansible kuruluysa, bu komut kullanım durumunuzun belgelerini gösterir: ansible-doc script - bbaassssiiee


Cevaplar:


Bazen kullandığım bir numara, komutları kodlamak için base64 kullanmak ve diğer siteye basmaktır.

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

Bu, komut dizisini, herhangi bir virgül, ters eğik çizgi, tırnak ve değişkenleri güvenli bir dizge içinde kodlayacak ve diğer sunucuya gönderecektir. (-w0 varsayılan olarak sütun 76'da gerçekleşen hat sarmalamayı devre dışı bırakmak için gereklidir). Diğer tarafta, $(base64 -d) betiğin kodunu çözecek ve yürütülmesi için bash'a besleyecektir.

Komut ne kadar karmaşık olursa olsun, onunla hiçbir sorunum yok. Sorunu kaçışla çözer, çünkü hiçbir şeyden kaçmanıza gerek yoktur. Uzak ana bilgisayarda bir dosya oluşturmaz ve çok karmaşık karmaşık betikleri kolaylıkla çalıştırabilirsiniz.


138
2017-09-02 13:10



Ya da: ssh user@remotehost "echo `base64 -w0 script.sh` | base64 -d | sudo bash" - Niklas B.
Çoğu durumda, daha hızlı aktarım süreleri için base64 için lzop veya gzip'in yerini alabilir. Ancak, kenar durumlarda olabilir. YMMV. - CodeGnome
İyi not, ama eğer bir betikse, herhangi bir transferin birkaç kb'den fazla olmasını beklemiyorum. - ThoriumBR
Burada da gereksiz bir yankı kullanımı var. base64 script | ssh remotehost 'base64 -d | sudo bash' Yeterli olacaktır :) - hobbs
Bugün test edildi ve mükemmel bir şekilde çalıştı. Teşekkürler millet. - Soham Chakraborty


Bakın -tt seçenek? Okumak ssh(1) Manuel.

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

Sık sık yaptığım bir şey vim kullanmak ve kullanmak :!cat % | ssh -tt somemachine hile.


32
2017-09-02 13:01



Tabii ki :!cat % | (command) olarak yapılabilir :w !(command) veya :!(command) < %. - Scott
Evet. Benim hattım kedi istismarı - moebius_eye


En kolay çözüm, @ thanasisk'in yorumunda bir değişiklik olduğunu düşünüyorum.

Bir komut dosyası oluştur scp makineye, sonra çalıştır.

Komut dosyasını al rm başlangıçta kendisi. Kabuk dosyayı açtı, bu yüzden yüklendi ve daha sonra sorunsuz bir şekilde kaldırılabilir.

Bu sırayla şeyler yaparak (rm İlk olarak, diğer şeyler ikinci) bir noktada başarısız olduğunda bile kaldırılacaktır.


9
2017-09-02 11:26





Kullanabilirsiniz %q belirteci ile biçimlendir printf sizin için kaçan değişkene dikkat çekmek için:

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

printf -v Çıktıyı bir değişkene yazar (bu durumda, $cmd_str). Bunun bunu yapmanın en basit yolu olduğunu düşünüyorum. Herhangi bir dosyayı aktarmaya veya komut dizesini kodlamaya gerek yok (hile gibi).

İşte burada köşeli parantez ve ampersanlar gibi şeyler için çalıştığını gösteren daha karmaşık bir örnek:

$ 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

İle test etmedim sudo ama şu kadar basit olmalı:

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

İsterseniz, bir adımı atlayabilir ve ara değişken oluşturmadan kaçabilirsiniz:

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

Ya da sadece bir değişken oluşturmadan kaçının:

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

7
2017-09-04 20:10



Bu harika bir çözüm. Benzer bir durum için daha önce printf kullanmam gerekiyordu, ama her zaman bunu unutuyorum. Bu :-) için teşekkürler - Jon L.


İşte bu gibi bir şeyi yürütmek için "doğru" (sözdizimsel) yolu:

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

Bunun nasıl çalıştığına dair kapsamlı bir açıklama için lütfen bkz. https://stackoverflow.com/a/21761956/111948


6
2018-05-28 17:48





Kullanabileceğinin farkında mısın sudo Seçtiğiniz kullanıcı olarak komutları çalıştırabileceğiniz bir kabuk vermek için?

-i, --login

Hedef kullanıcının parola veritabanı girdisi tarafından belirtilen kabuğu, oturum açma kabuğu olarak çalıştırın. Bu, girişe özgü kaynak dosyalarının                    .profile veya .login kabuk tarafından okunacaktır. Bir komut belirtilirse, kabuğun -c seçeneğiyle yürütmek için kabuğa geçirilir. Eğer hayırsa                    komut belirtilir, etkileşimli bir kabuk yürütülür. sudo, kabuğu çalıştırmadan önce kullanıcının ana dizini değiştirmeye çalışır. Com-                    mand, kullanıcının girişte alacağı bir ortam ile çalıştırılır. Sudoers'daki Command Environment bölümü (5) manual docu‐                    -i seçeneğinin, sudoers ilkesi kullanımdayken komutun çalıştırıldığı ortamı nasıl etkilediğini belirler.


4
2017-09-03 09:22



Bu benim için açık çözüm gibi görünüyor. > sudo -i -u brian - code_monk
Bu, uzaktan erişim durumunda "ssh -t" ile birleştirildiğinde en iyi şekilde çalışır. Hangi soruya dahil, ancak "Bu / tüm / sayfa okuyamıyorum" millet için tekrarlayan ayılar. :) - dannysauer


Komut dosyasını yerel makinenizde tanımlayabilir ve ardından cat ve bunu uzak makineye bağlayın:

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 kullanabilirsiniz < - Iain
Dahil edilecek örneği değiştirebilir misiniz? sudo -u scriptuser? Senaryoda değişkenlere ihtiyacım olduğunu düşünerek heredoc kullanabilir miyim? - VoY
Deniyordum: echo "sudo `cat fileForSsh.txt`" | ssh ... ama ben almaya devam ediyorum sudo: sorry, you must have a tty to run sudo. - jas_raj
olmasına rağmen bu soru eğer alabilirsiniz eğer yardımcı olabilir /etc/sudoers dosya değişti - jas_raj
Bu benim için çalışıyor: ssh -tq user@host "sudo bash -s" < test.sh - AVee


Basit ve kolay.

ssh kullanıcısı @ servidor "bash -s" <script.sh


1
2017-09-03 20:15





Yeterince modern bir Bash kabuğunu kullanırsanız, komutlarınızı bir işleve koyabilir ve bu işlevi bir dize olarak kullanabilirsiniz. export -p -f function_name. Bu dizenin sonucu, daha sonra kök olarak çalıştırması gerekmeyen keyfi komutlar içerebilir.

Boruları kullanan bir örnek Unix.SE'deki cevabım:

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

Alınan bir örnek, dosyayı oturum açan kullanıcı için kaydeder ve bir programa bir kök yükler:

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"

Tüm komutu kullanarak çalıştırmak istiyorsanız sudoBunu kullanabilirsiniz:

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





İşte bunun için (gerçekten test edilmemiş) berbat bir çözüm:

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

Yapabilecekleriniz:

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

Bir sonraki adım, bir betterssh zaten bunu yapan komut dosyası:

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

0
2017-09-06 08:19