【问题标题】:Sync local and remote folders using rsync from php script without typing password使用 rsync 从 php 脚本同步本地和远程文件夹,无需输入密码
【发布时间】:2011-06-14 02:20:30
【问题描述】:

如何在 php 脚本中使用 rsync 同步本地和远程文件夹而不提示输入密码?

我已经设置了一个公钥来为我的用户自动登录远程服务器。

所以这在 cli 中运行没有任何问题:

rsync -r -a -v -e "ssh -l user" --delete ~/local/file 111.111.11.111:~/remote/;

但是,当我尝试从 PHP 脚本(在我本地服务器的网页上)运行相同的脚本时:

$c='rsync -r -a -v -e "ssh -l user" --delete ~/local/file 111.111.11.111:~/remote/';
//exec($c,$data);
passthru($c,$data);
print_r($data);

这是我收到的:

255

并且没有文件从本地上传到远程服务器。

在网上搜索我找到了this clue

“你可以在这里使用 BAsh 和 Expect shell 代码的组合,但它不是很安全,因为这会自动进行 root 登录。为 nobodyapache 生成密钥(无论用户是哪个 Apache正在执行)。或者,安装 phpSuExec、Suhosin 或 suPHP,以便脚本以调用它们的用户身份运行。”

好吧,我不知道如何为作为“apache”运行的 PHP“自动化 root 登录”。也许让脚本以实际用户身份运行是一个更好的主意,我不知道...谢谢!

更新: - 因为这工作正常:

passthru('ssh user@111.111.11.111 | ls',$data);

返回一个home foder的列表,我可以肯定,自动登录没有问题。它主要是从 PHP 脚本运行 rsync 的东西。

  • 我还在本地和远程文件夹中设置了 chmod -R 0777 以防万一。但我还没明白。

更新:

所有问题都与“在命令行上运行时,ssh 使用 $HOME/.ssh/ 上的密钥文件,但在 PHP 下,它使用 Apache 的用户运行,因此它可能没有 $HOME;少一个 $HOME/.ssh/id_dsa。所以,要么你明确告诉它要使用哪个密钥文件,要么手动创建该目录及其内容。"

虽然我无法获得 rsync,但这就是我如何将文件从本地传输到远程:

if($con=ssh2_connect('111.111.11.111',22)) echo 'ok!';
if(ssh2_auth_password($con,'apache','xxxxxx')) echo ' ok!';
if(ssh2_scp_send($con,'localfile','/remotefolder',0755)) echo ' ok!';

本地文件需求:0644 远程文件夹需求:0775

我猜如果解决方案不是用相同的用户 bash 运行 php...

@Yzmir Ramirez 给出了这个建议:“我认为你不想“将密钥复制到 apache 可以访问它的地方”——这是违反安全性的。最好将脚本更改为安全运行用户,然后为服务器之间的无密码登录设置 .ssh 密钥。

这是我必须投入更多时间的事情。如果有人知道如何做到这一点,请,这将是很大的帮助。

【问题讨论】:

  • 如何执行 PHP 脚本? cron/从命令行手动/Apache 执行它/...
  • 来自我本地服务器 (Nginx) 中的一个 url。

标签: php ssh rsync openssh


【解决方案1】:

这里有一个简单但间接的解决方案,我正在使用它。

不要直接从 php 运行 rsync,它确实存在问题,并且这样做可能会带来安全风险。 相反,只需准备两个脚本。 在 php 脚本中,您只需将文件系统上的一个变量的值从 0 更改为 1。

现在,在另一边,制作一个 rsync 脚本,它将永远运行,并具有以下逻辑。 “” 如果文件中有0,则不运行rsync,如果为1,则运行rsync,运行rsync成功后将其值改回0; ""

我也在做。

【讨论】:

    【解决方案2】:

    当我在我们的应用程序中设置同样的东西时,我也遇到了 255 个错误,发现它们可能意味着各种各样的东西;这不是一个特别有用的错误代码。事实上,即使是现在,在解决方案顺利运行了一年多之后,我仍然偶尔会看到日志中出现 255。

    我还要说,让它工作可能有点痛苦,因为涉及到几个变量。我发现非常有用的一件事是在变量中构造 rsync 命令,然后记录它。这样,我可以获取正在使用的确切 rsync 命令并尝试手动运行它。您甚至可以 su 到 apache 用户并从与您的脚本相同的目录(或您的脚本设置为 cwd 的任何目录)运行它,这将使它的行为与以编程方式运行时相同;这使得调试 rsync 命令变得更加简单,因为您不必处理 Web 服务器。此外,当您手动运行它时,如果它由于某种未说明的原因而失败,请添加详细标志以加快错误输出。

    以下是我们正在使用的代码,为了安全起见稍作修改。请注意,我们的代码实际上支持 rsync'ing 到本地和远程服务器,因为目标是完全可配置的,以便于测试安装。

    try {
        if ('' != $config['publishSsh']['to']['remote']):
        //we're syncing with a remote server
            $rsyncToRemote = escapeshellarg($config['publishSsh']['to']['remote']) . ':';
            $rsyncTo = $rsyncToRemote . escapeshellarg($config['publishThemes']['to']['path']);
            $keyFile = $config['publishSsh']['to']['keyfile'];
            $rsyncSshCommand = "-e \"ssh -o 'BatchMode yes' -o 'StrictHostKeyChecking no' -q -i '$keyFile' -c arcfour\"";
        else:
        //we're syncing with the local machine
            $rsyncTo = escapeshellarg($config['publishThemes']['to']['path']);
            $rsyncSshCommand = '';
        endif;
    
        $log->Message("Apache running as user: " . trim(`whoami`), GLCLogger::level_debug);
        $deployCommand = "
            cd /my/themes/folder/; \
            rsync \
            --verbose \
            --delete \
            --recursive \
            --exclude '.DS_Store' \
            --exclude '.svn/' \
            --log-format='Action: %o %n' \
            --stats \
            $rsyncSshCommand \
            ./ \
            $rsyncTo \
             2>&1
        "; //deployCommand
        $log->Message("Deploying with command: \n" . $deployCommand, GLCLogger::level_debug);
        exec($deployCommand, $rsyncOutputLines, $returnValue);
        $log->Message("rsync status code: $returnValue", GLCLogger::level_debug);
        $log->Message("rsync output: \n" . implode("\n", $rsyncOutputLines), GLCLogger::level_debug);
        if (0 != $returnValue):
            $log->Message("Encountered error while publishing themes: <<<$returnValue>>>");
            throw new Exception('rsync error');
        endif;
    
        /* ... process output ... */
    } catch (Exception $e) {
        /* ... handle errors ... */
    }
    

    关于代码的几点注意事项:

    • 我正在使用 exec() 以便可以捕获变量中的输出。然后我对其进行解析,以便记录并报告添加、修改和删除文件的数量。

    • 我正在组合 rsync 的标准输出和标准错误流并返回两者。我也在捕获和检查返回结果代码。

    • 我在调试模式下记录所有内容,包括运行 Apache 的用户、rsync 命令本身以及 rsync 命令的输出。这样一来,就像我上面提到的,只需快速复制和粘贴,就可以轻松地以相同的用户运行相同的命令。

    • 如果您在使用 rsync 命令时遇到问题,可以调整其详细程度而不会产生影响,并在日志中查看输出。

    就我而言,我能够简单地指向一个适当的密钥文件,而不必过度担心安全性。也就是说,关于如何处理这个问题的一些想法:

    • 允许 Apache 访问该文件并不意味着它必须位于 web 目录中;您可以将文件放在 Apache 用户可以访问的任何地方,甚至是另一台网络机器上。根据您的其他安全层,这可能是也可能不是可接受的妥协。

    • 1234563造成伤害的能力。
    • 您可以使用 suEXEC 以 Apache 用户以外的用户身份运行脚本,从而允许您锁定其他用户对您的密钥的访问。因此,破坏 Apache 用户的人根本无法访问密钥。

    我希望这会有所帮助。

    【讨论】:

    • 非常感谢W先生。非常感谢您的努力。
    【解决方案3】:

    当您让 Web 服务器运行该命令时,它会以自己的用户(“nobody”或“apache”或类似用户)运行它,并在该用户的主目录/.ssh 中查找私钥,而私钥您为自己的用户设置的是“/home/you/.ssh/keyfile”,或者您命名它。

    使 ssh 使用带有 -i 参数的特定密钥:

    ssh -i /home/you/.ssh/keyfile
    

    它应该可以工作

    【讨论】:

    • 这就是我所做的(但没有工作): $c='rsync -r -a -v -e "ssh -l user -i ~/.ssh/id_rsa.pub" - -删除~/local/file 208.111.34.141:~/remote/'; passthru($c,$data); print_r($data);
    • 顺便说一句,它返回相同的:255
    • @Roger -i 需要私钥,而不是公钥。试试“id_rsa”
    • 我敢打赌,用户 apache 无法访问私钥 /home/you/.ssh/id_rsa,因为该目录通常为 600。将该密钥复制到 apache 可以访问的位置。
    • @Yzmir 这听起来很理想,但我不知道该怎么做。解释一下,apache 不应该能够获取私钥,因为理论上任何远程 Web 用户都可以通过一些可以访问本地文件的 apache 漏洞看到它。但是,为了让脚本使用该密钥,它必须访问它。如果它试图以安全用户的身份运行,它必须能够su - 进入它或类似的,这使得它无论如何都没有实际意义。你在考虑 suPHP 还是类似的?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-15
    • 1970-01-01
    相关资源
    最近更新 更多