AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / server / 问题 / 625641
Accepted
VoY
VoY
Asked: 2014-09-03 01:21:12 +0800 CST2014-09-03 01:21:12 +0800 CST 2014-09-03 01:21:12 +0800 CST

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

  • 772

我有一个只能以我的用户名 (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/Some 目录,其中包含一个空格。

是否有一个通用的解决方案允许我通过任何命令,无论引用多么复杂/疯狂,或者这是我已经达到的某种限制?还有其他可能的,也许更易读的解决方案吗?

ssh
  • 12 12 个回答
  • 67245 Views

12 个回答

  • Voted
  1. Best Answer
    ThoriumBR
    2014-09-03T05:10:11+08:002014-09-03T05:10:11+08:00

    我有时使用的一个技巧是使用 base64 对命令进行编码,并将其通过管道传输到另一个站点上的 bash:

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

    这将使用安全字符串中的任何逗号、反斜杠、引号和变量对脚本进行编码,并将其发送到其他服务器。(-w0需要禁用换行,默认情况下发生在第 76 列)。另一方面,$(base64 -d)将解码脚本并将其提供给 bash 以执行。

    无论脚本多么复杂,我都没有遇到任何问题。解决了转义的问题,因为你不需要转义任何东西。它不会在远程主机上创建文件,您可以轻松运行极其复杂的脚本。

    • 155
  2. moebius_eye
    2014-09-03T05:01:02+08:002014-09-03T05:01:02+08:00

    看到-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技巧。

    • 36
  3. dr. Sybren
    2014-09-03T03:26:07+08:002014-09-03T03:26:07+08:00

    我认为最简单的解决方案在于修改@thanasisk 的评论。

    创建一个脚本,scp将它放到机器上,然后运行它。

    在开始时拥有脚本rm本身。shell 已经打开了文件,所以它已经被加载,然后可以毫无问题地删除。

    通过按此顺序执行操作(rm首先是其他内容),它甚至会在某些时候失败时被删除。

    • 10
  4. Tom Fenech
    2014-09-05T12:10:04+08:002014-09-05T12:10:04+08:00

    您可以使用%q格式说明符 withprintf为您处理变量转义:

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

    printf -v将输出写入变量(在本例中为$cmd_str)。我认为这是最简单的方法。无需传输任何文件或对命令字符串进行编码(尽管我喜欢这个技巧)。

    这是一个更复杂的示例,表明它也适用于方括号和与号之类的东西:

    $ 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'")"
    
    • 8
  5. Dejay Clayton
    2015-05-29T09:48:55+08:002015-05-29T09:48:55+08:00

    更新:示例现在明确使用sudo.

    这是一种使用带有复合赋值的 Bash 语法通过 sudo 通过 SSH 执行任意复杂命令的方法:

    CMD=$( cat <<'EOT'
    echo "Variables like '${HOSTNAME}' and commands like $( whoami )"
    echo "will be interpolated on the server, thanks to the single quotes"
    echo "around 'EOT' above."
    EOT
    ) \
    SSHCMD=$(
      printf 'ssh -tq myuser@hostname sudo -u scriptuser bash -c %q' "${CMD}" ) \
    bash -c '${SSHCMD}'
    
    CMD=$( cat <<EOT
    echo "If you want '${HOSTNAME}' and $( whoami ) to be interpolated"
    echo "on the client instead, omit the the single quotes around EOT."
    EOT
    ) \
    SSHCMD=$(
      printf 'ssh -tq myuser@hostname sudo -u scriptuser bash -c %q' "${CMD}" ) \
    bash -c '${SSHCMD}'
    

    正确格式化命令以在其他地方动态执行(例如嵌套 shell、远程 SSH shell 等)可能非常棘手 - 请参阅https://stackoverflow.com/a/53197638/111948以了解原因。复杂的 bash 结构,如上面的语法,可以帮助这些命令正常工作。

    有关如何cat和<<组合形成内联此处文档的更多信息,请参阅https://stackoverflow.com/a/21761956/111948

    • 8
  6. justinpc
    2014-09-04T01:22:00+08:002014-09-04T01:22:00+08:00

    您是否知道可以使用sudo为您提供一个 shell,您可以在其中以所选用户身份运行命令?

    -i,--登录

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

    • 5
  7. jas_raj
    2014-09-03T01:39:29+08:002014-09-03T01:39:29+08:00

    您可以在本地计算机上定义脚本,然后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
    
    • 4
  8. rafaelrms
    2015-09-04T12:15:51+08:002015-09-04T12:15:51+08:00

    简单易行。

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

    • 3
  9. Lekensteyn
    2014-09-05T01:59:18+08:002014-09-05T01:59:18+08:00

    如果您使用足够现代的 Bash shell,您可以将您的命令放在一个函数中,并使用export -p -f function_name. 然后,该字符串的结果可以包含不必以 root 身份运行的任意命令。

    使用我在 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
  10. ysdx
    2014-09-07T00:19:11+08:002014-09-07T00:19:11+08:00

    这是我的(未经真正测试的)糟糕的解决方案:

    #!/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 隧道以访问远程网络 (Linux)

  • SSH 和重定向

  • 通过 SSH 会话使用 NET USER 命令拒绝访问

  • SSH 服务器零日漏洞利用 - 保护自己的建议

  • ubuntu apt-get upgrade - 如何在 shell 中单击确定?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    新安装后 postgres 的默认超级用户用户名/密码是什么?

    • 5 个回答
  • Marko Smith

    SFTP 使用什么端口?

    • 6 个回答
  • Marko Smith

    命令行列出 Windows Active Directory 组中的用户?

    • 9 个回答
  • Marko Smith

    什么是 Pem 文件,它与其他 OpenSSL 生成的密钥文件格式有何不同?

    • 3 个回答
  • Marko Smith

    如何确定bash变量是否为空?

    • 15 个回答
  • Martin Hope
    Tom Feiner 如何按大小对 du -h 输出进行排序 2009-02-26 05:42:42 +0800 CST
  • Martin Hope
    Noah Goodrich 什么是 Pem 文件,它与其他 OpenSSL 生成的密钥文件格式有何不同? 2009-05-19 18:24:42 +0800 CST
  • Martin Hope
    Brent 如何确定bash变量是否为空? 2009-05-13 09:54:48 +0800 CST
  • Martin Hope
    cletus 您如何找到在 Windows 中打开文件的进程? 2009-05-01 16:47:16 +0800 CST

热门标签

linux nginx windows networking ubuntu domain-name-system amazon-web-services active-directory apache-2.4 ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve