如何安全地承载多个网站nginx的和PHP-FPM在Ubuntu 14.04

介绍

众所周知,LEMP(Linux,nginx,MySQL,PHP)为运行PHP站点提供了无与伦比的速度和可靠性。 这种流行的的其他好处,如安全和隔离不太受欢迎。

在本文中,我们将向您展示使用不同Linux用户在LEMP上运行站点的安全和隔离优势。 这将通过为每个nginx服务器块(站点或虚拟主机)创建不同的php-fpm池来完成。

先决条件

本指南已在Ubuntu 14.04上测试。 所描述的安装和配置在其他OS或OS版本上类似,但配置文件的命令和位置可能不同。

它还假设您已经设置了nginx和php-fpm。 如果没有,请按照步骤之一,从文章的第三步如何在Ubuntu 14.04安装Linux,nginx的,MySQL和PHP(LEMP)的

本教程中的所有命令都应以非root用户身份运行。 如果需要该命令的root访问权限,它会在前面加sudo 如果你不已经有设置,请按照本教程: 使用Ubuntu 14.04初始服务器设置

您还需要一个指向Droplet中除了默认的测试完全限定域名(FQDN) localhost 如果您还没有一个在眼前,你可以使用site1.example.org 编辑/etc/hosts有你喜欢的编辑这样的文件sudo vim /etc/hosts ,加入这一行(替换site1.example.org与你的FQDN,如果你正在使用它):

/ etc / hosts
...
127.0.0.1 site1.example.org
... 

保护LEMP的原因另外

在常见的LEMP设置下,只有一个php-fpm池为同一用户下的所有站点运行所有PHP脚本。 这造成两个主要问题:

  • 如果一个nginx服务器上的Web应用程序块(即子域或单独的站点)被攻破,此Droplet上的所有站点也将受到影响。 攻击者能够读取其他站点的配置文件,包括数据库详细信息,甚至可以更改其文件。
  • 如果你想让用户访问您的Droplet上的网站,你实际上将授予他访问所有网站。 例如,您的开发人员需要在登台环境中工作。 然而,即使有非常严格的文件权限,你仍然可以访问所有的网站,包括你的主要网站,在同一个Droplet。

上面的问题是通过创建一个不同的池在不同的用户为每个站点php-fpm解决。

第1步 - 配置php-fpm

如果你已经涵盖了先决条件,那么你应该已经在Droplet上有一个功能网站。 除非你指定FQDN为它定制的,你应该能够在FQDN下访问localhost本地或Droplet远程的IP地址。

现在,我们将创建一个具有自己的php-fpm池和Linux用户的第二个站点(site1.example.org)。

让我们从创建必要的用户开始。 为了最佳隔离,新用户应该有自己的组。 因此,首先创建用户组site1

sudo groupadd site1

然后请创建属于此组的用户site1:

sudo useradd -g site1 site1

到目前为止,新用户site1没有密码,不能登录Droplet。 如果你需要有人提供可以直接访问这个网站的文件,那么你应该用命令该用户创建一个密码sudo passwd site1 使用新的用户/密码组合,用户可以通过ssh或sftp远程登录。 欲了解更多信息和安全细节检查文章安装有限目录访问辅助SSH / SFTP用户

接下来,为site1创建一个新的php-fpm池。 php-fpm池在其本质上只是一个普通的Linux进程,在某些用户/组下运行,并在Linux套接字上监听。 它也可以侦听IP:端口组合,但这将需要更多的Droplet资源,它不是首选的方法。

默认情况下,在Ubuntu 14.04每个PHP-FPM池应在目录中的文件配置/etc/php5/fpm/pool.d 与扩展的每个文件.conf这个目录中在php-fpm的全局配置自动加载。

因此,对于我们的新网站让我们创建一个新的文件/etc/php5/fpm/pool.d/site1.conf 你可以用你喜欢的编辑器这样做:

sudo vim /etc/php5/fpm/pool.d/site1.conf

此文件应包含:

/etc/php5/fpm/pool.d/site1.conf
[site1]
user = site1
group = site1
listen = /var/run/php5-fpm-site1.sock
listen.owner = www-data
listen.group = www-data
php_admin_value[disable_functions] = exec,passthru,shell_exec,system
php_admin_flag[allow_url_fopen] = off
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
chdir = /

在上面的配置注意这些具体选项:

  • [site1]是池的名称。 对于每个池,你必须指定一个唯一的名称。
  • usergroup立为Linux用户和新池将其下运行的组。
  • listen应指向为每个池一个独特的位置。
  • listen.ownerlisten.group定义监听器的所有权,即新的PHP-FPM池的插座。 Nginx必须能够读取这个套接字。 这就是为什么插座与下运行nginx的用户和组的创建- www-data
  • php_admin_value允许您设置自定义的PHP配置值。 我们用它来禁用它可以运行Linux命令功能- exec,passthru,shell_exec,system
  • php_admin_flag类似于php_admin_value ,但它仅仅是为布尔值,即打开和关闭的开关。 我们将禁用PHP函数allow_url_fopen ,允许PHP脚本来打开远程文件,并可以通过攻击者可以使用。

注:以上php_admin_valuephp_admin_flag值可能会在全球范围也可适用。 但是,一个网站可能需要它们,这就是为什么默认情况下它们没有配置。 php-fpm池的优点是它允许您微调每个站点的安全设置。 此外,这些选项可用于安全范围之外的任何其他PHP设置,以进一步定制站点的环境。

pm选项是当前安全议题之外,但你应该知道,他们允许您配置池的性能。

chdir选项应该是/这是文件系统的根。 除非你使用另一种重要选择这不应该被改变chroot

选项chroot不包含在故意上述结构。 它将允许您在被监控的环境中运行池,即锁定在目录中。 这是伟大的安全,因为你可以锁定网站的web根内的池。 然而,这个最终的安全性将导致严重的问题,任何体面的PHP应用程序,它依赖于系统二进制文件和应用程序,如Imagemagick,将不可用。 如果你有进一步的兴趣这个主题,请阅读文章如何使用Firejail建立一个WordPress安装在监禁的环境

完成上述配置后,重新启动php-fpm以使新设置在命令中生效:

sudo service php5-fpm restart

通过搜索其进程,如下所示验证新池是否正确运行:

ps aux |grep site1

如果你遵循到这里的确切指示,你应该看到类似的输出:

site1   14042  0.0  0.8 133620  4208 ?        S    14:45   0:00 php-fpm: pool site1
site1   14043  0.0  1.1 133760  5892 ?        S    14:45   0:00 php-fpm: pool site1

红色是进程或php-fpm池运行的用户 - site1。

此外,我们将禁用opcache提供的默认php缓存。 这个特定的缓存扩展可能是伟大的性能,但它不是为了安全,我们将在后面看到。 要禁用它编辑该文件/etc/php5/fpm/conf.d/05-opcache.ini具有超级用户权限,并添加一行:

/etc/php5/fpm/conf.d/05-opcache.ini
opcache.enable=0

然后再次重新启动PHP-FPM( sudo service php5-fpm restart使设置生效)。

第2步 - 配置nginx

一旦我们为我们的网站配置了php-fpm池,我们将在nginx中配置服务器块。 为此,请创建一个新的文件/etc/nginx/sites-available/site1与你喜欢的编辑器是这样的:

sudo vim /etc/nginx/sites-available/site1

此文件应包含:

/ etc / nginx / sites-available / site1
server {
    listen 80;

    root /usr/share/nginx/sites/site1;
    index index.php index.html index.htm;

    server_name site1.example.org;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php5-fpm-site1.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

上面的代码显示了nginx中服务器块的常见配置。 注意有趣的突出部分:

  • Web根目录是/usr/share/nginx/sites/site1
  • 服务器名称使用FQDN site1.example.org这是本文的先决条件中提到的之一。
  • fastcgi_pass指定的PHP文件的处理程序。 对于每一个网站,你应该使用不同的Unix套接字如/var/run/php5-fpm-site1.sock

创建web根目录:

sudo mkdir /usr/share/nginx/sites
sudo mkdir /usr/share/nginx/sites/site1

为了使上述网站必须在目录中创建一个符号链接它/etc/nginx/sites-enabled/ 这可以使用命令:

sudo ln -s /etc/nginx/sites-available/site1 /etc/nginx/sites-enabled/site1

最后,重启nginx使更改生效,如下所示:

sudo service nginx restart

第3步 - 测试

对于运行测试,我们将使用知名的phpinfo函数,它提供有关php环境的详细信息。 创建名下的新文件info.php仅包含行<?php phpinfo(); ?> <?php phpinfo(); ?> 您将首先在默认nginx的网站及其网页根需要这个文件/usr/share/nginx/html/ 为此,你可以使用这样的编辑器:

sudo vim /usr/share/nginx/html/info.php

之后,将文件复制到其他网站(site1.example.org)的web根目录,如下所示:

sudo cp /usr/share/nginx/html/info.php /usr/share/nginx/sites/site1/

现在您可以运行最基本的测试来验证服务器用户。 您可以使用浏览器或从Droplet终端和lynx,命令行浏览器执行测试。 如果你没有对你的Droplet猞猁然而,用命令安装sudo apt-get install lynx

首先检查info.php从默认的站点文件。 它应该可以在localhost下这样访问:

lynx --dump http://localhost/info.php |grep 'SERVER\["USER"\]' 

在上面的命令,我们只对变量筛选使用grep输出SERVER["USER"]它代表服务器用户。 对于默认的网站上的输出应显示默认的www-data类似这样的用户:

_SERVER["USER"]                 www-data

同样,下一步检查site1.example.org的服务器用户:

lynx --dump http://site1.example.org/info.php |grep 'SERVER\["USER"\]' 

您应该看到这一次在输出中site1用户:

_SERVER["USER"]                 site1

如果您已经在每个php-fpm池基础上进行了任何自定义php设置,那么您也可以通过过滤您感兴趣的输出以上述方式检查其对应的值。

到目前为止,我们知道我们的两个网站运行在不同的用户,但现在让我们看看如何保护一个连接。 为了演示我们在本文中解决的安全问题,我们将创建一个包含敏感信息的文件。 通常这样的文件包含到数据库的连接字符串,并包括数据库用户的用户和密码细节。 如果任何人发现该信息,该人能够对相关网站做任何事情。

用你喜欢的编辑器创建你的主站一个新的文件/usr/share/nginx/html/config.php 该文件应包含:

/usr/share/nginx/html/config.php
<?php
$pass = 'secret';
?>

在上述文件中,我们定义了一个变量叫pass持有的价值secret 当然,我们希望限制对此文件的访问,因此我们将其权限设置为400,这样只能访问文件所有者。

要将权限更改为400,请运行命令:

sudo chmod 400 /usr/share/nginx/html/config.php

此外,我们的主网站下的用户运行www-data应该能够读取该文件谁。 因此,将文件的所有权更改为该用户,如下所示:

sudo chown www-data:www-data /usr/share/nginx/html/config.php

在我们的例子中,我们将使用名为另一个文件/usr/share/nginx/html/readfile.php读取秘密信息并打印。 此文件应包含以下代码:

/usr/share/nginx/html/readfile.php
<?php
include('/usr/share/nginx/html/config.php');
print($pass);
?>

更改此文件的所有权www-data ,以及:

sudo chown www-data:www-data /usr/share/nginx/html/readfile.php

要确认所有权限和所有权是正确的在Web根目录中运行命令ls -l /usr/share/nginx/html/ 您应该看到类似的输出:

-r-------- 1 www-data www-data  27 Jun 19 05:35 config.php
-rw-r--r-- 1 www-data www-data  68 Jun 21 16:31 readfile.php

现在访问您的默认网站后文件,命令lynx --dump http://localhost/readfile.php 你应该能够看到打印输出secret这表明敏感信息的文件是相同的网站,这是预期的正确行为中访问。

现在将文件复制/usr/share/nginx/html/readfile.php你的第二个网站,site1.example.org是这样的:

sudo cp /usr/share/nginx/html/readfile.php /usr/share/nginx/sites/site1/

为了保持站点/用户关系的顺序,请确保在每个站点内文件由相应的站点用户拥有。 通过使用以下命令将新复制的文件的所有权更改为site1:

sudo chown site1:site1 /usr/share/nginx/sites/site1/readfile.php

为了确认您已经设置正确的权限和文件的所有权,请列出site1的Web根目录的内容,用命令ls -l /usr/share/nginx/sites/site1/ 你应该看到:

-rw-r--r-- 1 site1 site1  80 Jun 21 16:44 readfile.php

然后尝试访问从site1.example.com同一个文件的命令lynx --dump http://site1.example.org/readfile.php 您将只能看到返回的空白空间。 此外,如果您搜索Nginx的和grep命令的错误日志错误sudo grep error /var/log/nginx/error.log您将看到:

2015/06/30 15:15:13 [error] 894#0: *242 FastCGI sent in stderr: "PHP message: PHP Warning:  include(/usr/share/nginx/html/config.php): failed to open stream: Permission denied in /usr/share/nginx/sites/site1/readfile.php on line 2

注意:您也将看到一个类似的错误在山猫的输出,如果你有display_errors设置为On在php-fpm的配置文件/etc/php5/fpm/php.ini

该警告表明,从site1.example.org站点脚本无法读取敏感文件config.php从主站点。 因此,在不同用户下运行的站点不能损害彼此的安全性。

如果回到本文结尾的配置部分,您将看到我们已禁用opcache提供的默认缓存。 如果你是好奇,为什么,尝试启用再次用超级用户权限设置opcache opcache.enable=1在文件/etc/php5/fpm/conf.d/05-opcache.ini并重新启动PHP5-FPM用命令sudo service php5-fpm restart

令人惊讶的是,如果您以完全相同的顺序再次运行测试步骤,则无论其所有权和权限如何,您都可以读取敏感文件。 opcache中的这个问题已经报道了很长时间,但到本文的时候,它还没有被修复。

结论

从安全角度来看,对于同一Nginx Web服务器上的每个站点,使用不同用户的php-fpm池非常重要。 即使它带来了小的性能损失,这种隔离的好处可以防止严重的安全漏洞。

本文中描述的想法不是唯一的,它存在于其他类似的PHP隔离技术,如SuPHP。 然而,所有其他替代品的性能比php-fpm差。

赞(52) 打赏
未经允许不得转载:优客志 » 系统运维
分享到:

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏