मैं ssh पर sudo का उपयोग करके मनमाने ढंग से जटिल कमांड कैसे चला सकता हूं?


80

मेरे पास एक प्रणाली है जिसे मैं केवल अपने उपयोगकर्ता नाम (myuser) के तहत लॉग इन कर सकता हूं, लेकिन मुझे अन्य उपयोगकर्ता (स्क्रिप्टर) के रूप में कमांड चलाने की आवश्यकता है। अब तक, मुझे निम्नलिखित आदेशों को चलाने की आवश्यकता है जो मुझे चाहिए:

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

हालांकि, जब मैं अधिक जटिल कमांड चलाने की कोशिश करता [[ -d "/tmp/Some directory" ]] && rm -rf "/tmp/Some directory"हूं , जैसे कि मैं जल्दी से उद्धृत करने में परेशानी में पड़ जाता हूं। मुझे यकीन नहीं है कि मैं इस उदाहरण को जटिल कमांड से कैसे पारित कर सकता हूं bash -c, जब \"पहले से ही मैं जिस कमांड को पारित कर रहा हूं उसकी सीमाओं को परिसीमित करता है (और इसलिए मुझे नहीं पता कि कैसे / tmp / कुछ निर्देशिका को उद्धृत करना है, जिसमें रिक्त स्थान शामिल हैं।

क्या कोई सामान्य समाधान है जो मुझे किसी भी आदेश को पारित करने की अनुमति देता है, चाहे वह उद्धरण कितना भी जटिल / पागल क्यों न हो, या क्या यह किसी प्रकार की सीमा तक पहुँच गया है? क्या अन्य संभव और शायद अधिक पठनीय समाधान हैं?


11
स्क्रिप्ट को $ रिमोट पर कॉपी करें और फिर निष्पादित करें।
user9517

यह काम करेगा, लेकिन मैं एक ऐसा समाधान ढूंढता हूं जो कोई स्क्रिप्ट फ़ाइलों को पीछे छोड़ देता है (यदि कुछ विफल रहता है आदि) तो थोड़ा क्लीनर होगा।
वीओ

9
प्रत्येक रन के अंत में स्क्रिप्ट rm ही है
thanasisk

3
फैब्रिक fabfile.org का उपयोग करने पर विचार करें । यदि आपको बहुत दूर से सूडो करना है तो आप जीवन को बहुत आसान बना सकते हैं।
नेल्स टॉड्टमन

2
यदि आपके पास Ansible स्थापित है, तो यह कमांड आपके उपयोग के मामले के लिए दस्तावेज़ीकरण दिखाता है: ansible-doc script
bbaassssiiee

जवाबों:


146

एक तरकीब जो मैं कभी-कभी इस्तेमाल करता हूं, वह है कमांडों को एनकोड करने के लिए बेस 64 का इस्तेमाल करना, और इसे दूसरी साइट पर बैश करने के लिए पाइप करना:

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

यह किसी भी अल्पविराम, बैकस्लैश, उद्धरण और चर के साथ एक सुरक्षित स्ट्रिंग के अंदर स्क्रिप्ट को एन्कोड करेगा, और इसे अन्य सर्वर पर भेज देगा। ( -w0लाइन रैपिंग को अक्षम करने के लिए आवश्यक है, जो डिफ़ॉल्ट रूप से कॉलम 76 पर होता है)। दूसरी तरफ, $(base64 -d)स्क्रिप्ट को डिकोड करेगा और इसे निष्पादित करने के लिए बैश करने के लिए फीड करेगा।

मुझे इससे कभी कोई समस्या नहीं हुई, चाहे वह स्क्रिप्ट कितनी भी जटिल क्यों न हो। समस्या को भागने से हल करता है, क्योंकि आपको कुछ भी बचने की आवश्यकता नहीं है। यह दूरस्थ होस्ट पर फ़ाइल नहीं बनाता है, और आप आसानी से जटिल जटिल स्क्रिप्ट चला सकते हैं।


2
या यहां तक ​​कि:ssh user@remotehost "echo `base64 -w0 script.sh` | base64 -d | sudo bash"
निकोल्स बी।

1
यह ध्यान देने योग्य है कि ज्यादातर मामलों में आप तेजी से स्थानांतरण समय के लिए बेस 64 के लिए लोज़ॉप या गज़िप स्थानापन्न कर सकते हैं। हालाँकि, किनारे मामले हो सकते हैं। YMMV।
कोडनेम

1
अच्छी बात है, लेकिन अगर यह एक स्क्रिप्ट है, तो मैं किसी भी हस्तांतरण को कुछ केबी से अधिक होने की उम्मीद नहीं करता हूं।
थोरियम बीआर

7
यहाँ भी प्रतिध्वनि का एक बेकार उपयोग है। base64 script | ssh remotehost 'base64 -d | sudo bash'पर्याप्त होगा :)
hobbs

हालांकि मैंने इस समाधान का परीक्षण नहीं किया है, क्या यह स्क्रिप्ट का आउटपुट परिणाम होगा, उदाहरण के लिए स्टैडआउट में 'यम चेक-अपडेट'? मैं पूछना चाहता था क्योंकि अगर किसी को लगता है कि मैं स्पष्ट रूप से अनुभवहीन कुछ कर रहा हूं, तो मैं कुछ चीजें उठा सकता हूं।
सोहम चक्रवर्ती

34

-ttविकल्प देखें ? ssh(1)मैनुअल पढ़ें ।

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

एक चीज जो मैं अक्सर करता हूं वह है विम का उपयोग करना और :!cat % | ssh -tt somemachineट्रिक का उपयोग करना ।


3
के :!cat % | (command)रूप में :w !(command)या निश्चित रूप से किया जा सकता है :!(command) < %
स्कॉट

2
हाँ। मेरी लाइन एक बिल्ली का दुरुपयोग है
moebius_eye

चल रहा है ssh -ttका कारण बनता है मेरी ssh के बाद कुछ आदेश चलाए जा रहे हैं समाप्त नहीं करने के लिए (जोड़ने exitअंत में मदद नहीं करता है)

10

मुझे लगता है कि सबसे आसान समाधान @ थानिसिस्क की टिप्पणी के संशोधन में निहित है।

scpमशीन के लिए एक स्क्रिप्ट बनाएं , फिर उसे चलाएं।

rmशुरुआत में ही स्क्रिप्ट लें । शेल ने फ़ाइल को खोल दिया है, इसलिए इसे लोड किया गया है, और फिर समस्याओं के बिना हटाया जा सकता है।

इस क्रम में चीजें करने से ( rmपहला, अन्य सामान दूसरा) यह तब भी हटा दिया जाएगा जब यह किसी बिंदु पर विफल हो जाए।


8

आप के लिए चर भागने का ख्याल रखने के लिए %qप्रारूप विनिर्देशक का उपयोग कर सकते हैं printf:

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

printf -vएक चर (इस मामले में $cmd_str) के लिए आउटपुट लिखता है । मुझे लगता है कि यह इसे करने का सबसे सरल तरीका है। किसी भी फाइल को ट्रांसफर करने की या कमांड स्ट्रिंग को एनकोड करने की कोई जरूरत नहीं है (जितना मुझे ट्रिक पसंद है)।

यहां एक और अधिक जटिल उदाहरण दिखाया गया है कि यह वर्ग कोष्ठक और 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

मैंने इसका परीक्षण नहीं किया है sudoलेकिन यह उतना ही सरल होना चाहिए:

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

यदि आप चाहें, तो आप एक कदम छोड़ सकते हैं और मध्यवर्ती चर बनाने से बच सकते हैं:

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

या यहां तक ​​कि पूरी तरह से एक चर बनाने से बचें:

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

1
यह एक भयानक समाधान है। मुझे वास्तव में इसी तरह की स्थिति के लिए प्रिंटफ का उपयोग करना था, लेकिन मैं हमेशा इसके बारे में भूल जाता हूं। इसे पोस्ट करने के लिए धन्यवाद :-)
जॉन एल।

8

यहाँ "सही" (सिंटैक्टिक) तरीका है, जो इस तरह से किसी चीज़ को अंजाम देता है:

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

यह कैसे काम करता है, इसकी गहन व्याख्या के लिए, कृपया https://stackoverflow.com/a/21761956/111948 देखें


5

क्या आप जानते हैं कि आप sudoएक शेल देने के लिए उपयोग कर सकते हैं जहाँ आप चुने हुए उपयोगकर्ता के रूप में कमांड चला सकते हैं?

-आई, -लोगिन

लक्ष्य उपयोगकर्ता के पासवर्ड डेटाबेस प्रविष्टि द्वारा निर्दिष्ट शेल को एक लॉगिन शेल के रूप में चलाएं। इसका मतलब यह है कि लॉगिन-विशिष्ट संसाधन फाइलें जैसे कि .profile या .login शेल द्वारा पढ़ी जाएंगी। यदि कोई कमांड निर्दिष्ट है, तो इसे शेल के -c विकल्प के माध्यम से निष्पादन के लिए शेल में पास किया जाता है। यदि कोई कमांड निर्दिष्ट नहीं है, तो एक इंटरैक्टिव शेल निष्पादित किया जाता है। शेल चलाने से पहले sudo उस उपयोगकर्ता की होम डायरेक्टरी को बदलने का प्रयास करता है। Com The मंड एक ऐसे वातावरण के साथ चलाया जाता है, जिसे एक उपयोगकर्ता लॉग इन के दौरान प्राप्त करता है। sudoers में कमांड एनवायरनमेंट सेक्शन (5) मेनुअल डोनो मंट करता है कि -i विकल्प उस वातावरण को कैसे प्रभावित करता है जिसमें एक कमांड चलाया जाता है sudoers नीति उपयोग में है।


यह मेरे लिए स्पष्ट समाधान की तरह लगता है। >
सूडो -आई

रिमोट एक्सेस के मामले में "ssh -t" के साथ संयुक्त होने पर यह सबसे अच्छा काम करता है। जो प्रश्न में शामिल है, लेकिन "मैं इस / पूरे / पृष्ठ नहीं पढ़ सकता" लोगों के लिए दोहराता है। :)
dannysauer

4

आप अपनी स्थानीय मशीन पर स्क्रिप्ट को परिभाषित कर सकते हैं और फिर catउसे दूरस्थ मशीन पर पाइप कर सकते हैं:

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 का उपयोग आप <
user9517

क्या आप शामिल करने के लिए उदाहरण को संशोधित कर सकते हैं sudo -u scriptuser? क्या मुझे लगता है कि मुझे मशीन में पाइपिंग करनी होगी स्क्रिप्ट में वेरिएबल की जरूरत है, इस पर विचार करने में हेरेडोक उपयोगी होगा।
वीओ

मैं कोशिश कर रहा था: echo "sudo `cat fileForSsh.txt`" | ssh ...लेकिन मुझे मिलता रहा sudo: sorry, you must have a tty to run sudo
jas_raj

यद्यपि यह प्रश्न मदद कर सकता है यदि आप /etc/sudoersफ़ाइल को बदल सकते हैं
jas_raj

1
यह मेरे लिए काम करता है:ssh -tq user@host "sudo bash -s" < test.sh
AVee

3

सरल और आसान।

ssh उपयोगकर्ता @ नौकर "bash -s" <script.sh


1

यदि आप एक पर्याप्त आधुनिक बैश शेल का उपयोग करते हैं, तो आप अपने कमांड को एक फ़ंक्शन में रख सकते हैं और उस फ़ंक्शन को एक स्ट्रिंग के रूप में प्रिंट कर सकते हैं export -p -f function_name। फिर उस स्ट्रिंग के परिणाम में मनमाने आदेश हो सकते हैं जो जरूरी नहीं कि रूट के रूप में चलें।

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"

एक उदाहरण जो पुनर्प्राप्त करता है लॉगिन उपयोगकर्ता के लिए फ़ाइल को बचाता है और प्रोग्राम को रूट स्थापित करता है:

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"

यदि आप पूरी कमांड का उपयोग करके चलाना चाहते हैं sudo, तो आप इसका उपयोग कर सकते हैं:

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

इसके लिए मेरा (वास्तव में परीक्षण नहीं किया गया) भद्दा समाधान है:

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

आप ऐसा नहीं कर सकते:

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

अगला कदम एक bettersshस्क्रिप्ट बनाना है जो पहले से ही ऐसा करती है:

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

1

आप में से जो स्क्रिप्ट के लिए मापदंडों को पारित करने की आवश्यकता है उसके लिए यह निम्नानुसार होना चाहिए:

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

1

सबसे पहले, एक स्थानीय स्क्रिप्ट बनाएं और फिर फॉलो कमांड का उपयोग करके इसे दूरस्थ रूप से निष्पादित करें:

cat <Local Script.sh> | ssh user@server "cat - | sudo -u <user> /bin/bash"
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.