如何使用sudo通过ssh运行任意复杂的命令?

我有一个系统,我只能login到我的用户名(myuser),但我需要以其他用户(scriptuser)运行命令。 到目前为止,我已经拿出以下来运行我需要的命令:

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

但是,如果我尝试运行一个更复杂的命令,比如[[ -d "/tmp/Some directory" ]] && rm -rf "/tmp/Some directory"我很快就会遇到引用问题。 我不知道我怎么能通过这个例子复杂的命令bash -c ,当\"已经界定了我传递的命令的边界(所以我不知道如何引用/ tmp /某些目录,这包括一个空格。

是否有一个通用的解决scheme可以让我通过任何命令,无论引用是多么复杂/疯狂,还是这种限制,我已经达到了? 还有其他可能的,也许更可读的解决scheme?

    我使用的一个技巧有时是使用base64编码命令,并将其pipe道打到另一个站点上:

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

    这将用一个安全string中的任何逗号,反斜杠,引号和variables对脚本进行编码,并将其发送到其他服务器。 ( -w0是禁用换行的必要条件,默认情况下会发生在第76列)。 另一方面, $(base64 -d)将解码脚本并将其提供给bash执行。

    我从来没有遇到任何问题,不pipe脚本有多复杂。 解决逃逸的问题,因为你不需要逃脱任何东西。 它不会在远程主机上创build文件,而且可以轻松运行极其复杂的脚本。

    看到-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 

    我经常做的一件事就是使用vim并使用:!cat % | ssh -tt somemachine :!cat % | ssh -tt somemachine把戏。

    我认为最简单的解决scheme是修改@ thanasisk的评论。

    创build一个脚本, scp到机器,然后运行它。

    有一个脚本rm本身在开始。 shell已经打开了这个文件,所以它已经被加载,然后可以被移除而没有问题。

    按照这个顺序( rm首先,其他的东西),它甚至会在某个点失败时被删除。

    你可以使用printf%q格式说明符来照顾你的variables转义:

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

    printf -v将输出写入一个variables(在本例中$cmd_str )。 我认为这是最简单的方法。 没有必要传输任何文件或编码命令string(尽可能多的我喜欢的技巧)。

    下面是一个更复杂的例子,它显示了它对方括号和&符号的作用:

     $ 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 

    我没有用sudotesting过,但应该如下简单:

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

    如果你愿意,你可以跳过一步,避免创build中间variables:

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

    甚至可以避免完全创build一个variables:

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

    下面是在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 )" 

    有关如何工作的详细说明,请参阅https://stackoverflow.com/a/21761956/111948

    你知道你可以使用sudo来给你一个shell,你可以像select的用户一样运行命令吗?

    -i,–login

    将目标用户的密码数据库条目指定的shell作为loginshell运行。 这意味着特定于login的资源文件(如.profile或.login)将被shell读取。 如果指定了一个命令,它会通过shell的-c选项传递给shell执行。 如果没有指定命令,则执行交互式shell。 在运行shell之前,sudo尝试更改为该用户的主目录。 命令的运行环境类似于用户在login时收到的环境。sudoers(5)手册中的“命令环境”部分logging了-i选项如何影响运行命令的环境sudoers政策正在使用中。

    您可以在本地机器上定义脚本,然后将其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 

    简单和容易。

    ssh user @ servidor“bash -s”<script.sh

    如果您使用的是足够现代的Bash shell,则可以将您的命令放入函数中,并使用export -p -f function_name将该函数作为string打印。 然后该string的结果可以包含任意的命令,不一定必须以root身份运行。

    在Unix.SE上使用pipe道的例子:

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

    一个检索的例子保存login用户的文件,并以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" 

    如果你想使用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" 

    这是我的(没有真正testing)糟糕的解决scheme:

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

    下一步是创build一个已经这样做的betterssh脚本:

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

    对于需要将parameter passing给脚本的人员,应该如下所示:

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

    首先,创build一个本地脚本,然后使用以下命令远程执行它:

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