Bagaimana saya bisa menjalankan perintah kompleks sewenang-wenang menggunakan sudo over ssh?

80

Saya memiliki sistem yang hanya bisa saya masuki di bawah nama pengguna (pengguna saya), tetapi saya harus menjalankan perintah sebagai pengguna lain (pengguna skrip). Sejauh ini, saya telah datang dengan yang berikut untuk menjalankan perintah yang saya butuhkan:

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

Namun jika, ketika saya mencoba menjalankan perintah yang lebih kompleks, seperti [[ -d "/tmp/Some directory" ]] && rm -rf "/tmp/Some directory"saya dengan cepat mendapat masalah dengan mengutip. Saya tidak yakin bagaimana saya bisa mengirimkan contoh perintah kompleks ini bash -c, ketika \"sudah membatasi batas-batas perintah yang saya lewati (dan jadi saya tidak tahu bagaimana cara mengutip / tmp / Direktori tertentu, yang mencakup spasi.

Apakah ada solusi umum yang memungkinkan saya untuk meloloskan perintah apa pun, tidak peduli seberapa kompleks / gila penawarannya, atau apakah ini semacam batasan yang telah saya capai? Apakah ada solusi lain yang mungkin dan mungkin lebih mudah dibaca?

VoY
sumber
11
Salin skrip ke $ remote lalu jalankan.
user9517
Itu akan berhasil, tetapi saya menemukan solusi yang tidak meninggalkan file skrip di belakang (kalau-kalau ada sesuatu yang gagal dll) akan menjadi sedikit lebih bersih.
VoY
9
memiliki skrip rm sendiri di akhir setiap run
thanasisk
3
Pertimbangkan untuk menggunakan fabric fabfile.org . Dapat membuat hidup Anda jauh lebih mudah jika Anda harus sudo dari jauh.
Nils Toedtmann
2
Jika Anda telah menginstal Ansible, perintah ini menunjukkan dokumentasi untuk kasus penggunaan Anda: skrip ansible-doc
bbaassssiiee

Jawaban:

146

Trik yang saya gunakan kadang-kadang adalah menggunakan base64 untuk menyandikan perintah, dan mengirimnya ke bash di situs lain:

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

Ini akan menyandikan skrip, dengan koma, garis miring terbalik, tanda kutip dan variabel di dalam string yang aman, dan mengirimkannya ke server lain. ( -w0diperlukan untuk menonaktifkan pembungkus baris, yang terjadi pada kolom 76 secara default). Di sisi lain, $(base64 -d)akan memecahkan kode skrip dan mengumpankannya ke bash untuk dieksekusi.

Saya tidak pernah mendapat masalah dengan itu, tidak peduli seberapa kompleks skrip itu. Memecahkan masalah dengan melarikan diri, karena Anda tidak perlu melarikan diri apa pun. Itu tidak membuat file pada host jarak jauh, dan Anda dapat menjalankan skrip yang sangat rumit dengan mudah.

ToriumBR
sumber
2
Atau bahkan:ssh user@remotehost "echo `base64 -w0 script.sh` | base64 -d | sudo bash"
Niklas B.
1
Perlu dicatat bahwa dalam kebanyakan kasus, Anda dapat mengganti lzop atau gzip dengan base64 untuk waktu transfer yang lebih cepat. Namun, mungkin ada kasus tepi. YMMV.
CodeGnome
1
Catatan bagus, tetapi jika ini adalah skrip, saya tidak mengharapkan transfer apa pun menjadi lebih dari beberapa kb.
ThoriumBR
7
Ada penggunaan gema yang tidak berguna di sini juga. base64 script | ssh remotehost 'base64 -d | sudo bash'akan cukup :)
hobbs
Walaupun saya belum menguji solusi ini, apakah ini akan menghasilkan hasil skrip, katakan misalnya 'yum check-update' di stdout? Saya ingin bertanya karena jika seseorang berpikir saya melakukan sesuatu yang jelas naif, saya dapat mengambil beberapa hal.
Soham Chakraborty
34

Lihat -ttopsi? Baca ssh(1)manualnya.

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

Satu hal yang sering saya lakukan adalah menggunakan vim dan menggunakan :!cat % | ssh -tt somemachinetriknya.

moebius_eye
sumber
3
Tentu saja :!cat % | (command)bisa dilakukan seperti :w !(command)atau :!(command) < %.
Scott
2
Iya. Baris saya adalah penyalahgunaan kucing
moebius_eye
menjalankan ssh -ttmenyebabkan ssh saya tidak berakhir setelah perintah tertentu dijalankan (menambahkan exitpada akhirnya tidak membantu)
10

Saya pikir solusi termudah terletak pada modifikasi dari komentar @ thanasisk.

Buat skrip, scpke mesin, lalu jalankan.

Miliki skrip rmitu sendiri di awal. Shell telah membuka file, sehingga telah dimuat, dan kemudian dapat dihapus tanpa masalah.

Dengan melakukan hal-hal dalam urutan ini ( rmpertama, hal-hal lain kedua) itu bahkan akan dihapus ketika gagal di beberapa titik.

dr. Sybren
sumber
8

Anda dapat menggunakan %qpenentu format dengan printfuntuk menjaga variabel yang keluar untuk Anda:

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

printf -vmenulis output ke variabel (dalam hal ini, $cmd_str). Saya pikir ini adalah cara paling sederhana untuk melakukannya. Tidak perlu mentransfer file atau menyandikan string perintah (sebanyak yang saya suka triknya).

Berikut adalah contoh yang lebih kompleks yang menunjukkan bahwa itu berfungsi untuk hal-hal seperti tanda kurung siku dan ampersand juga:

$ 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

Saya belum mengujinya sudotetapi harus sesederhana:

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

Jika mau, Anda dapat melewati satu langkah dan menghindari membuat variabel perantara:

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

Atau bahkan hanya menghindari membuat variabel sepenuhnya:

ssh user@host "bash -c $(printf '%q' "[[ -f test ]] && echo 'this works as well'")"
Tom Fenech
sumber
1
ini adalah solusi yang luar biasa. Saya sebenarnya harus menggunakan printf sebelumnya untuk situasi yang sama, tetapi saya selalu melupakannya. Terima kasih telah memposting ini :-)
Jon L.
8

Inilah cara "benar" (sintaksis) untuk mengeksekusi sesuatu seperti ini di 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
)"

Untuk penjelasan menyeluruh tentang cara kerjanya, silakan lihat https://stackoverflow.com/a/21761956/111948

Dejay Clayton
sumber
5

Apakah Anda sadar bahwa Anda dapat menggunakan sudountuk memberikan Anda shell di mana Anda dapat menjalankan perintah sebagai pengguna yang dipilih?

-i, --login

Jalankan shell yang ditentukan oleh entri basis data kata sandi pengguna target sebagai shell login. Ini berarti bahwa file sumber daya khusus masuk seperti .profile atau .login akan dibaca oleh shell. Jika suatu perintah ditentukan, perintah itu diteruskan ke shell untuk dieksekusi melalui opsi -c shell. Jika tidak ada perintah yang ditentukan, shell interaktif dijalankan. sudo mencoba mengubah ke direktori home pengguna tersebut sebelum menjalankan shell. Perintah dijalankan dengan lingkungan yang mirip dengan yang akan diterima pengguna saat login. Bagian Lingkungan Komando di sudoers (5) mendokumentasikan secara manual bagaimana opsi -i mempengaruhi lingkungan di mana perintah dijalankan ketika kebijakan sudoers sedang digunakan.

justinpc
sumber
ini sepertinya solusi yang jelas bagi saya. > sudo -i -u brian
code_monk
Itu bekerja paling baik bila dikombinasikan dengan "ssh -t" dalam kasus akses jarak jauh. Yang termasuk dalam pertanyaan, tetapi diulang untuk orang-orang "Saya tidak bisa membaca ini / seluruh / halaman". :)
dannysauer
4

Anda dapat menentukan skrip pada mesin lokal Anda dan kemudian catmengirimnya ke mesin jarak jauh:

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
jas_raj
sumber
5
UUOC Anda dapat menggunakan <
user9517
Bisakah Anda memodifikasi contoh untuk dimasukkan sudo -u scriptuser? Apakah heredoc dapat digunakan mengingat saya perlu memiliki variabel dalam skrip yang akan saya piping ke mesin?
VoY
Saya berusaha: echo "sudo `cat fileForSsh.txt`" | ssh ...tetapi saya terus berusaha sudo: sorry, you must have a tty to run sudo.
jas_raj
Meskipun pertanyaan ini dapat membantu jika Anda dapat /etc/sudoersmengubah file
jas_raj
1
Ini berfungsi untuk saya:ssh -tq user@host "sudo bash -s" < test.sh
AVee
3

Sederhana dan mudah.

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

rafaelrms
sumber
1

Jika Anda menggunakan Bash shell yang cukup modern, Anda bisa menempatkan perintah Anda dalam suatu fungsi dan mencetak fungsi itu sebagai string menggunakan export -p -f function_name. Hasil dari string itu kemudian dapat berisi perintah arbitrer yang tidak harus dijalankan sebagai root.

Contoh menggunakan pipa dari jawaban saya di 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"

Contoh yang mengambil menyimpan file untuk pengguna login dan menginstal program 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"

Jika Anda ingin menjalankan seluruh perintah menggunakan sudo, Anda bisa menggunakan ini:

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"
Lekensteyn
sumber
1

Inilah solusi jelek saya (tidak benar-benar diuji) untuk ini:

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

Tidak bisa Anda lakukan:

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

Langkah selanjutnya adalah membuat bettersshskrip yang sudah melakukan ini:

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

Untuk Anda yang perlu memberikan parameter ke skrip, harus sebagai berikut:

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

Pertama, buat skrip lokal dan kemudian jalankan dari jarak jauh menggunakan perintah follow:

cat <Local Script.sh> | ssh user@server "cat - | sudo -u <user> /bin/bash"
Franciscon Santos
sumber