Ubuntu 14.04でNginxとPhp-fpmを使用して複数のWebサイトを安全にホストする方法

前書き

LEMPスタック(Linux、nginx、MySQL、PHP)がPHPサイトの実行に比類のない速度と信頼性を提供することはよく知られています。 ただし、セキュリティや分離など、この一般的なスタックの他の利点はあまり一般的ではありません。

この記事では、さまざまなLinuxユーザーを使用してLEMPでサイトを実行することのセキュリティと分離の利点を示します。 これは、nginxサーバーブロック(サイトまたは仮想ホスト)ごとに異なるphp-fpmプールを作成することにより行われます。

前提条件

このガイドはUbuntu 14.04でテストされています。 説明されているインストールと構成は、他のOSまたはOSバージョンでも同様ですが、構成ファイルのコマンドと場所は異なる場合があります。

また、すでにnginxとphp-fpmがセットアップされていることを前提としています。 そうでない場合は、記事https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-on-ubuntu-14のステップ1とステップ3に従ってください。 -04 [Linux、nginx、MySQL、PHP(LEMP)スタックをUbuntu 14.04にインストールする方法]。

このチュートリアルのすべてのコマンドは、非rootユーザーとして実行する必要があります。 コマンドにルートアクセスが必要な場合は、先頭に「+ sudo +」が付きます。 まだセットアップしていない場合は、このチュートリアルに従ってください:https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-14-04[Ubuntu 14.04での初期サーバーセットアップ] 。

デフォルトの `+ localhost `に加えて、テスト用のドロップレットを指す完全修飾ドメイン名(fqdn)も必要です。 手元にない場合は、「 site1.example.org 」を使用できます。 ` sudo vim / etc / hosts `のようなお気に入りのエディターで ` / etc / hosts `ファイルを編集し、次の行を追加します(使用している場合は、 ` site1.example.org +`をfqdnに置き換えます)。

/ etc / hosts

...
127.0.0.1 site1.example.org
...

LEMPをさらに保護する理由

一般的なLEMPセットアップでは、同じユーザーの下ですべてのサイトのすべてのPHPスクリプトを実行するphp-fpmプールが1つだけあります。 これには2つの大きな問題があります。

  • 1つのnginxサーバー上のWebアプリケーションがブロックする場合、つまり サブドメインまたは別のサイトが侵害されると、このドロップレット上のすべてのサイトも影響を受けます。 攻撃者は、データベースの詳細を含む他のサイトの構成ファイルを読み取ったり、ファイルを変更したりすることもできます。

  • ユーザーにドロップレット上のサイトへのアクセスを許可する場合、実質的にすべてのサイトへのアクセスを許可します。 たとえば、開発者はステージング環境で作業する必要があります。 ただし、ファイルのアクセス許可が非常に厳しい場合でも、同じドロップレット上のメインサイトを含むすべてのサイトへのアクセスを許可します。

上記の問題は、サイトごとに異なるユーザーで実行される異なるプールを作成することにより、php-fpmで解決されます。

[[step-1-- configuring-php-fpm]] === ステップ1-php-fpmの構成

前提条件を満たしている場合は、Dropletにすでに機能するWebサイトが1つあるはずです。 カスタムfqdnを指定していない限り、fqdn `+ localhost +`の下でローカルに、またはリモートでドロップレットのIPを使用してアクセスできます。

次に、独自のphp-fpmプールとLinuxユーザーで2つ目のサイト(site1.example.org)を作成します。

必要なユーザーの作成から始めましょう。 最適な分離のために、新しいユーザーには独自のグループが必要です。 したがって、最初にユーザーグループ「+ site1 +」を作成します。

sudo groupadd site1

次に、このグループに属するユーザーsite1を作成してください。

sudo useradd -g site1 site1

これまでのところ、新しいユーザーsite1にはパスワードがなく、Dropletにログインできません。 このサイトのファイルへの直接アクセスを誰かに提供する必要がある場合は、コマンド `+ sudo passwd site1 +`を使用してこのユーザーのパスワードを作成する必要があります。 新しいユーザー/パスワードの組み合わせを使用すると、ユーザーはsshまたはsftpを使用してリモートでログインできます。 詳細とセキュリティの詳細については、https://www.digitalocean.com/community/questions/setup-a-secondary-ssh-sftp-user-with-limited-directory-access [セカンダリ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]







pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
chdir = /

上記の構成では、これらの特定のオプションに注意してください。

  • `+ [site1] +`はプールの名前です。 プールごとに一意の名前を指定する必要があります。

  • + user`と + group`は、新しいプールが実行されるLinuxユーザーとグループを表します。

  • `+ listen +`は各プールの一意の場所を指す必要があります。

  • + listen.owner +`と `+ listen.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スクリプトがリモートファイルを開き、攻撃者によって使用される可能性があります。

`+ pm +`オプションは現在のセキュリティトピックの範囲外ですが、プールのパフォーマンスを設定できることを知っておく必要があります。

`+ chdir `オプションは、ファイルシステムのルートである ` / `でなければなりません。 別の重要なオプション「 chroot +」を使用しない限り、これを変更しないでください。

オプション `+ chroot +`は上記の設定には意図的に含まれていません。 刑務所に入れられた環境でプールを実行できるようになります。 ディレクトリ内でロックされています。 これは、サイトのWebルート内のプールをロックできるため、セキュリティに最適です。 ただし、この究極のセキュリティは、システムバイナリに依存するまともなPHPアプリケーションやImagemagickなどのアプリケーションには利用できない深刻な問題を引き起こします。 このトピックにさらに興味がある場合は、記事https://www.digitalocean.com/community/tutorials/how-to-use-firejail-to-set-up-a-wordpress-installation-in-a-をご覧ください。 jailed-environment [Firejailを使用して、Jailed環境でWordPressインストールをセットアップする方法]。

上記の設定が完了したら、コマンドで新しい設定を有効にするためにphp-fpmを再起動します。

sudo service php5-fpm restart

次のようなプロセスを検索して、新しいプールが適切に実行されていることを確認します。

ps aux |grep site1

ここまで正確な手順を実行した場合、次のような出力が表示されます。

  14042  0.0  0.8 133620  4208 ?        S    14:45   0:00 php-fpm: pool 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 +)を再起動します。

[[step-2-- configuring-nginx]] === ステップ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;


   index index.php index.html index.htm;



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

   location ~ \.php$ {
       try_files $uri =404;
       fastcgi_split_path_info ^(.+\.php)(/.+)$;

       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ファイルのハンドラーを指定します。 すべてのサイトで、 ` / var / run / php5-fpm-site1.sock +`などの異なるUNIXソケットを使用する必要があります。

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

[[step-3-- testing]] === ステップ3-テスト

テストを実行するために、PHP環境に関する詳細情報を提供する有名なphpinfo関数を使用します。 行 + <?php phpinfo();のみを含む + info.php + `という名前で新しいファイルを作成します。 ?> + `。 デフォルトの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から実行できます。 ドロップレットにまだlynxがない場合は、コマンド `+ sudo apt-get install lynx +`を使用してインストールします。

最初にデフォルトのサイトの `+ info.php`ファイルを確認してください。 このようにローカルホストの下でアクセスできるはずです:

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

上記のコマンドでは、サーバーユーザーを表す変数 `+ SERVER [" USER "] `に対してのみgrepを使用して出力をフィルター処理します。 デフォルトのサイトの場合、出力には次のようにデフォルトの「 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設定を行っている場合は、関心のある出力をフィルタリングすることにより、上記の方法で対応する値を確認することもできます。

これまでのところ、2つのサイトが異なるユーザーで実行されていることはわかっていますが、接続を保護する方法を見てみましょう。 この記事で解決しようとしているセキュリティ問題を示すために、機密情報を含むファイルを作成します。 通常、このようなファイルには、データベースへの接続文字列が含まれ、データベースユーザーのユーザーとパスワードの詳細が含まれます。 誰かがその情報を見つけた場合、その人は関連サイトで何でもできます。

お気に入りのエディターを使用して、メインサイト `+ / usr / share / nginx / html / config.php +`に新しいファイルを作成します。 そのファイルには以下を含める必要があります。

/usr/share/nginx/html/config.php

<?php
$pass = 'secret';
?>

上記のファイルでは、値「+ secret」を保持する「+ pass +」という変数を定義しています。 当然、このファイルへのアクセスを制限したいので、ファイルの所有者に読み取り専用アクセスを許可する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 +`を2番目のサイト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

ファイルの正しい許可と所有権を設定したことを確認するには、コマンド `+ ls -l / usr / share / nginx / sites / site1 / +`を使用してsite1 Webルートのコンテンツをリストしてください。 君は見るべきだ:

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

次に、コマンド `+ lynx --dump http:// site1.example.org / readfile.php `を使用して、site1.example.comから同じファイルにアクセスしてみます。 空のスペースのみが返されます。 さらに、grepコマンド ` sudo grep error / var / log / nginx / error.log +`を使用してnginxのエラーログでエラーを検索すると、次のように表示されます。

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

この警告は、site1.example.orgサイトのスクリプトが、メインサイトの機密ファイル `+ config.php +`を読み取れないことを示しています。 したがって、異なるユーザーで実行されているサイトは、互いのセキュリティを侵害することはできません。

この記事の構成部分の最後に戻ると、opcacheが提供するデフォルトのキャッシュを無効にしていることがわかります。 興味がある場合は、ファイル `+ / etc / php5 / fpm / conf.d / 05-opcache.ini `にスーパーユーザー権限 ` opcache.enable = 1 `を設定してopcacheを再度有効にしてみてください。コマンド ` sudo service php5-fpm restart +`を使用したphp5-fpm。

驚くべきことに、まったく同じ順序でテストステップを再度実行すると、所有権と許可に関係なく機密ファイルを読み取ることができます。 opcacheのこの問題は長い間報告されていますが、この記事の時点ではまだ修正されていません。

結論

セキュリティの観点からは、同じNginxウェブサーバー上のサイトごとに異なるユーザーでphp-fpmプールを使用することが不可欠です。 パフォーマンスにわずかなペナルティがあったとしても、そのような分離の利点は深刻なセキュリティ侵害を防ぐことができます。

この記事で説明するアイデアはユニークではなく、SuPHPなどの他の同様のPHP分離テクノロジーにも存在します。 ただし、他のすべての選択肢のパフォーマンスは、php-fpmのパフォーマンスよりもはるかに劣ります。

前の投稿:ウェビナーシリーズ:コンテナ化されたアプリケーションの構築
次の投稿:Ubuntu 14.04でGitフックを使用して本番環境にHugoサイトを展開する方法