使用php脚本中的rsync同步本地和远程文件夹,而无需输入密码

时间:2011-01-23 17:27:56

标签: php ssh rsync openssh

如何在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,以便脚本作为调用它们的用户运行。“

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

更新: - 因为这很好用:

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

返回家庭垃圾箱列表,我可以肯定,自动登录没有问题。它是从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密钥以在服务器之间进行无密码登录。

这是我需要投入更多时间的事情。如果有人知道如何做到这一点,那么,这将是非常有帮助的。

3 个答案:

答案 0 :(得分:5)

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

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

以下是我们正在使用的代码,为安全起见略有编辑。请注意,我们的代码实际上支持对本地和远程服务器进行rsync,因为目标是完全可配置的,以便于进行简单的测试安装。

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用户可以访问的任何位置,即使在另一台网络计算机上也是如此。根据您的其他安全层,这可能是也可能不是可接受的妥协。

  • 根据您正在复制的数据,您可能会严重锁定ssh用户在另一台计算机上的权限,这样如果有人肆无忌惮地设法进入那里,他们就会有最小的权限。能够造成伤害。

  • 您可以使用suEXEC以Apache用户以外的用户身份运行脚本,从而允许您锁定对其他用户的密钥访问权限。因此,有人破坏Apache用户只是无法访问密钥。

我希望这会有所帮助。

答案 1 :(得分:0)

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

使ssh使用-i参数的特定键:

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

它应该有效

答案 2 :(得分:0)

我正在使用这个问题的简单但直接的解决方案。

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

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

我也是这样做的。