Làm thế nào tôi có thể chạy lệnh phức tạp tùy ý bằng cách sử dụng sudo trên ssh?


80

Tôi có một hệ thống mà tôi chỉ có thể đăng nhập dưới tên người dùng (myuser), nhưng tôi cần chạy các lệnh như người dùng khác (scriptuser). Cho đến nay, tôi đã đưa ra các cách sau để chạy các lệnh tôi cần:

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

Tuy nhiên, nếu tôi cố chạy một lệnh phức tạp hơn, chẳng hạn như [[ -d "/tmp/Some directory" ]] && rm -rf "/tmp/Some directory"tôi nhanh chóng gặp rắc rối với việc trích dẫn. Tôi không chắc làm thế nào tôi có thể chuyển lệnh phức tạp ví dụ này đến bash -c, khi \"đã phân định ranh giới của lệnh tôi đang chuyển (và vì vậy tôi không biết cách trích dẫn / tmp / Một số thư mục, bao gồm một khoảng trắng.

Có một giải pháp chung cho phép tôi vượt qua bất kỳ lệnh nào cho dù trích dẫn có phức tạp / điên rồ đến mức nào, hay đây là một giới hạn nào đó mà tôi đã đạt được? Có những giải pháp khác có thể và có lẽ dễ đọc hơn?


11
Sao chép một tập lệnh vào $ remote sau đó thực thi nó.
dùng9517

Điều đó sẽ làm việc, nhưng tôi tìm thấy một giải pháp không để lại các tập lệnh phía sau (trong trường hợp có lỗi, v.v.) sẽ sạch hơn một chút.
VoY

9
có tập lệnh rm chính nó vào cuối mỗi lần chạy
thanasisk

3
Xem xét sử dụng vải fabfile.org . Có thể làm cho cuộc sống của bạn dễ dàng hơn nhiều nếu bạn phải sudo từ xa rất nhiều.
Nils Toedtmann

2
Nếu bạn đã cài đặt Ansible, lệnh này sẽ hiển thị tài liệu cho trường hợp sử dụng của bạn: tập lệnh ansible-doc
bbaassssiiee

Câu trả lời:


146

Một mẹo mà tôi sử dụng đôi khi là sử dụng base64 để mã hóa các lệnh và đặt nó vào bash trên trang web khác:

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

Điều này sẽ mã hóa tập lệnh, với bất kỳ dấu phẩy, dấu gạch chéo ngược, dấu ngoặc kép và biến trong chuỗi an toàn và gửi nó đến máy chủ khác. ( -w0được yêu cầu để vô hiệu hóa gói dòng, xảy ra ở cột 76 theo mặc định). Mặt khác, $(base64 -d)sẽ giải mã tập lệnh và đưa nó vào bash để được thực thi.

Tôi không bao giờ có bất kỳ vấn đề với nó, cho dù kịch bản phức tạp như thế nào. Giải quyết vấn đề với việc trốn thoát, bởi vì bạn không cần phải trốn thoát bất cứ điều gì. Nó không tạo một tập tin trên máy chủ từ xa và bạn có thể chạy các tập lệnh phức tạp vô cùng dễ dàng.


2
Hoặc thậm chí:ssh user@remotehost "echo `base64 -w0 script.sh` | base64 -d | sudo bash"
Niklas B.

1
Điều đáng chú ý là trong hầu hết các trường hợp, bạn có thể thay thế lzop hoặc gzip cho base64 để có thời gian chuyển nhanh hơn. Tuy nhiên, có thể có trường hợp cạnh. YMMV.
CodeGnome

1
Lưu ý tốt, nhưng nếu đó là một tập lệnh, tôi không mong đợi bất kỳ chuyển khoản nào nhiều hơn một vài kb.
ThoriumBR

7
Có một cách sử dụng tiếng vang vô dụng ở đây quá. base64 script | ssh remotehost 'base64 -d | sudo bash'sẽ là đủ :)
hobbs

Trong khi tôi chưa thử nghiệm giải pháp này, liệu nó có đưa ra kết quả của tập lệnh hay không, ví dụ như 'yum check-update' trong thiết bị xuất chuẩn? Tôi muốn hỏi bởi vì nếu ai đó nghĩ rằng tôi đang làm điều gì đó rõ ràng là ngây thơ, tôi có thể chọn một vài thứ.
Soham Chakraborty

34

Xem -tttùy chọn? Đọc ssh(1)hướng dẫn.

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

Một điều tôi thường làm là sử dụng vim và sử dụng :!cat % | ssh -tt somemachinemánh khóe.


3
Tất nhiên :!cat % | (command)có thể được thực hiện như :w !(command)hoặc :!(command) < %.
Scott

2
Đúng. Dòng của tôi là một con mèo lạm dụng
moebius_eye

việc chạy ssh -ttkhiến ssh của tôi không kết thúc sau khi một số lệnh nhất định được chạy (thêm exitvào cuối không giúp được gì)

10

Tôi nghĩ rằng giải pháp đơn giản nhất nằm ở việc sửa đổi nhận xét của @ thanasisk.

Tạo một kịch bản, scpnó vào máy, sau đó chạy nó.

Có kịch rmbản ngay từ đầu. Shell đã mở tệp, vì vậy nó đã được tải và sau đó có thể được gỡ bỏ mà không gặp vấn đề gì.

Bằng cách thực hiện mọi thứ theo thứ tự này ( rmthứ nhất, thứ khác thứ hai), nó thậm chí sẽ bị xóa khi thất bại tại một số điểm.


8

Bạn có thể sử dụng công %qcụ xác định định dạng printfđể chăm sóc biến thoát cho bạn:

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

printf -vghi đầu ra vào một biến (trong trường hợp này $cmd_str). Tôi nghĩ rằng đây là cách đơn giản nhất để làm điều đó. Không cần chuyển bất kỳ tệp nào hoặc mã hóa chuỗi lệnh (nhiều như tôi thích thủ thuật).

Đây là một ví dụ phức tạp hơn cho thấy rằng nó cũng hoạt động cho những thứ như dấu ngoặc vuông và ký hiệu:

$ 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

Tôi đã không thử nghiệm nó sudonhưng nó sẽ đơn giản như:

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

Nếu bạn muốn, bạn có thể bỏ qua một bước và tránh tạo biến trung gian:

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

Hoặc thậm chí chỉ cần tránh tạo một biến hoàn toàn:

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

1
đây là một giải pháp tuyệt vời Tôi thực sự đã phải sử dụng printf trước đây cho một tình huống tương tự, nhưng tôi luôn quên nó. Cảm ơn bạn đã đăng bài này :-)
Jon L.

8

Đây là cách "chính xác" (cú pháp) để thực hiện một cái gì đó như thế này trong 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
)"

Để được giải thích kỹ lưỡng về cách thức hoạt động của nó, vui lòng xem https://stackoverflow.com/a/21761956/111948


5

Bạn có biết rằng bạn có thể sử dụng sudođể cung cấp cho bạn một trình bao nơi bạn có thể chạy các lệnh như người dùng đã chọn không?

-i, -

Chạy shell được chỉ định bởi mục nhập cơ sở dữ liệu mật khẩu của người dùng đích làm shell đăng nhập. Điều này có nghĩa là các tệp tài nguyên dành riêng cho đăng nhập như .profile hoặc .login sẽ được đọc bởi trình bao. Nếu một lệnh được chỉ định, nó được chuyển đến shell để thực thi thông qua tùy chọn -c của shell. Nếu không có lệnh nào được chỉ định, shell tương tác được thực thi. sudo cố gắng thay đổi thư mục chính của người dùng đó trước khi chạy shell. Lệnh được chạy với một môi trường tương tự như môi trường mà người dùng sẽ nhận được khi đăng nhập. Phần Môi trường lệnh trong tài liệu hướng dẫn sudoers (5) cho biết tùy chọn -i ảnh hưởng đến môi trường mà lệnh được chạy khi chính sách sudoers đang được sử dụng.


đây dường như là giải pháp rõ ràng cho tôi > sudo -i -u brian
code_monk

Điều đó hoạt động tốt nhất khi kết hợp với "ssh -t" trong trường hợp truy cập từ xa. Điều này được bao gồm trong câu hỏi, nhưng gấu lặp đi lặp lại cho folks "Tôi không thể đọc cái này / toàn bộ / trang". :)
dannysauer

4

Bạn có thể xác định tập lệnh trên máy cục bộ của mình và sau đó chuyển catnó sang máy từ xa:

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 bạn có thể sử dụng <
user9517

Bạn có thể sửa đổi ví dụ để bao gồm sudo -u scriptuser? Heredoc có thể sử dụng được không nếu tôi cần có các biến trong tập lệnh mà tôi sẽ chuyển sang máy?
VoY

Tôi đã cố gắng: echo "sudo `cat fileForSsh.txt`" | ssh ...nhưng tôi tiếp tục nhận được sudo: sorry, you must have a tty to run sudo.
jas_raj

Mặc dù câu hỏi này có thể hữu ích nếu bạn có thể /etc/sudoersthay đổi tệp
jas_raj

1
Điều này làm việc cho tôi:ssh -tq user@host "sudo bash -s" < test.sh
AVee

3

Đơn giản và dễ dàng.

người dùng ssh @ servidor "bash -s" <script.sh


1

Nếu bạn sử dụng trình bao Bash đủ hiện đại, bạn có thể đặt các lệnh của mình vào một hàm và in hàm đó dưới dạng một chuỗi bằng cách sử dụng export -p -f function_name. Kết quả của chuỗi đó sau đó có thể chứa các lệnh tùy ý mà không nhất thiết phải chạy như root.

Một ví dụ sử dụng ống dẫn từ câu trả lời của tôi trên 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"

Một ví dụ truy xuất lưu tệp cho người dùng đăng nhập và cài đặt chương trình gốc:

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"

Nếu bạn muốn chạy toàn bộ lệnh bằng cách sử dụng sudo, bạn có thể sử dụng lệnh này:

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

Đây là giải pháp crappy (chưa thực sự được thử nghiệm) của tôi cho việc này:

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

Không phải bạn có thể làm:

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

Bước tiếp theo là tạo một bettersshtập lệnh đã thực hiện điều này:

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

1

Đối với những người bạn cần truyền tham số cho tập lệnh, nó sẽ như sau:

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

1

Đầu tiên, tạo một tập lệnh cục bộ và sau đó thực thi nó từ xa bằng lệnh follow:

cat <Local Script.sh> | ssh user@server "cat - | sudo -u <user> /bin/bash"
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.