PHP 実行環境を fcgid + php-cgi から PHP-FPM に変更

以前に公開したエントリ、「mod_fcgid 設定」で紹介した様に、本サイトの php 実行環境は fcgid + php-cgi で行っていました。
が、今回興味があって、PHP-FPM で実行する様に変更したので、その備忘録です。

ちなみに変更前の環境は Debian GNU/Linux 9 + Apache 2.4 + fcgid + php-cgi (PHP 7.0) で動作する設定で、今回はこれを fcgid + php-cgi 部分について fgcid + PHP-FPM (PHP7.0) で実行する様変更したものです。

なお、設定内容はあくまで例です。また間違いがあればコメント欄でいただけると嬉しいです。

続きを読む

mod_fcgid 設定

Apache2.4 + mod_fcgid で php5 を動かす個人的なメモです。

最低でも suExec が有効であれば、特に問題は無いと思われます。(suExec に関する設定などは、このメモでは省いています)

注意点として、php スクリプト側での BASIC 認証は通らない点があります。

OS は Debian GNU/Linux 8 相当を想定しています。

・CGI 版 php のラッパースクリプトを準備

# mkdir /etc/skel/bin/

・/etc/skel/bin/php-wrapper

#!/bin/sh
exec /usr/bin/php5-cgi

・実行権限変更と、www-data 権限から読まれる場合用にスクリプトをコピー

# chmod 755 /etc/skel/bin/php-wrapper
# cp -p /etc/skel/bin/php-wrapper /usr/local/bin/

・ラッパースクリプトのコピーとバーチャルホストの設定変更 (各バーチャルホスト毎に作業が必要)

# su username
$ cp -a /etc/skel/bin /home/username/

・/etc/apache2/sites-available/example.com.conf

・・・省略・・・
<Directory /home/usrname/html/>
	Options +ExecCGI
</Directory>
・・・省略・・・
<IfModule mod_fcgid.c>
	FCGIWrapper /home/username/bin/php-wrapper .php
	FCGIWrapper /home/username/bin/php-wrapper .php3
	FCGIWrapper /home/username/bin/php-wrapper .phtml
	AddHandler fcgid-script .php .php3 .phtml
</IfModule>
・・・省略・・・

・/etc/apache2/sites-available/000-default.conf (デフォルトバーチャル)

・・・省略・・・
<Directory /var/www/>
	Options +ExecCGI
</Directory>
・・・省略・・・
<IfModule mod_fcgid.c>
	FCGIWrapper /usr/local/bin/php-wrapper .php
	FCGIWrapper /usr/local/bin/php-wrapper .php3
	FCGIWrapper /usr/local/bin/php-wrapper .phtml
	AddHandler fcgid-script .php .php3 .phtml
</IfModule>
・・・省略・・・

・/etc/apache2/conf-available/php-wrapper-limit.conf

<Directory /usr/lib/cgi-bin/>
	<FilesMatch "^(php|php5)$">
		Require all denied
	</FilesMatch>
</Directory>

上記設定を有効化する。

# a2enconf php-access-limit

・php5 モジュールの無効化と mod-fcgid のインストールと設定変更

# a2dismod [今使用している PHP5 のモジュール]
# apt-get install libapache2-mod-fcgid
# a2enmod fcgid

・/etc/apache2/mods-available/fcgid.conf

<IfModule mod_fcgid.c>
  AddHandler    fcgid-script .fcgi
  FcgidConnectTimeout 20
  FcgidIOTimeout 300
  FcgidInitialEnv PHP_FCGI_CHILDREN 0
  FcgidInitialEnv PHP_FCGI_MAX_REQUESTS 10000
  FcgidMaxProcesses 50
  FcgidMaxProcessesPerClass 2
  FcgidMinProcessesPerClass 1
  FcgidMaxRequestsPerProcess 5000
  FcgidFixPathinfo 1
</IfModule>

・/etc/php5/cgi/php.ini

・・・省略・・・
cgi.fix_pathinfo=1
・・・省略・・・

・apache2 の MPM の Worker への変更と再起動

# a2dismod [今使っている MPM]
# a2enmod mpm_worker
# /etc/init.d/apache2 restart

・追記

  • 2015/07/10: 内容を Debian 8 相当の設定例の書式に変更。
  • 2015/09/08: 内容を修正。

mod_limitipconn.c に LocalIP ディレクティブ追加のパッチをあててインストールするメモ

Debian GNU/Linux 6.0 において、Apache 2.2 に「mod_limitipconn.c」とそれに LocalIP ディレクティブを追加するパッチを当ててインストールするメモです。

以前書いたエントリと内容が重複しますが、もうちょっと丁寧に書きたいと思います。

ちなみに、「mod_limitipconn.c」とは、同一 IP アドレスからの接続制限を行うモジュールで、パッチを当てることにより、特定 IP アドレスからは一切制限しない、という設定が可能になります。

  • あらかじめ該当のマシンで、ビルドに必要なパッケージをインストールします。

    # apt-get install build-essential apache2-prefork-dev

  • mod_limitipconn.c」のサイトよりソースをダウンロードします。なお、ファイル名などは変更になることもあり得るので、必ず公式ページにて確認して下さい。

    $ wget "http://dominia.org/djao/limit/mod_limitipconn-0.24.tar.bz2"

  • 次に同ページで公開されている「LocalIP」ディレクティブを追加するためのパッチをダウンロードします。

    $ wget "http://dominia.org/djao/limit/contrib/dg/mod_limitipconn_local_IP_patch_2.diff"

  • 展開します。

    $ tar xfvpj mod_limitipconn-0.24.tar.bz2

  • 展開したディレクトリに入ります。

    $ cd mod_limitipconn-0.24

  • 「LocalIP」ディレクティブを追加するためのパッチを当てます。

    $ patch < ../mod_limitipconn_local_IP_patch_2.diff

  • Make ファイルの「APXS」と「APACHECTL」の項目を以下のように修正します。

    APXS=/usr/bin/apxs2
    APACHECTL=/usr/sbin/apachectl

  • root 権限にてビルドとインストールを行います。

    # make install

    自動的に、limitipconn のモジュール読み込みファイルが /etc/apache2/mods-available/limitipconn.load に作成され、かつこのファイルのシンボリックリンクが /etc/apache2/mods-enabled/limitipconn.load に作成されます。

    つまり、この時点で Apache2 を再起動すると、モジュールが読み込まれます。


さて、mod_limitipconn.c の設定例としては以下のようになります。

  1. 同一 IP アドレスからの接続数は 20 までに制限。
  2. ただし、MIME タイプが image/* (画像) 、text/css (CSS ファイル) 、application/javascript (JavaScript ファイル) には制限をかけない。
  3. さらに、IPv4 アドレス 127.0.0.1 および 192.0.0.1 、IPv6 アドレス ::1 および 2001:DB8:1::1 からの接続については、接続数制限は行わない。

上記のような場合は、該当のバーチャルホストなどにおいて、以下のような設定を追加して、Apache2 を再起動します。

<Location />
	<IfModule mod_limitipconn.c>
		MaxConnPerIP 20
		LocalIP 127.0.0.1 192.0.0.1 ::1 2001:DB8:1::1
		NoIPLimit image/* text/css application/javascript
	</IfModule>
</Location>

特定 UA について、https から http へリダイレクト

特にリンクしているわけでは無いのに、Googlebot が https で自サイトをインデックスしてしまうので、以下のようにいくつかの bot だけ、ユーザーエージェントで判断して、強制的に http へ遷移するように、該当バーチャルの Apache の設定ファイルに記載しました。

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} on  
RewriteCond %{HTTP_USER_AGENT} (Googlebot|Yeti/[0-9]\.[0-9]|bingbot/[0-9]\.[0-9]|msnbot|Baiduspider)
RewriteRule ^/(.*)?$ http://%{HTTP_HOST}/$1 [R=301,L]
</IfModule>

なお、この設定だけど、.htaccess ファイルでは、他に rewrite ルールが設定されているからか、上手く動作できず、Apache のバーチャルホストの設定内で記述することで目的を達成できています。

このサイトは https でもアクセス出来るのだけど、非 SSL コンテンツを読み込んでいるので、SSL 暗号化コンテンツと非暗号化コンテンツが混在するという、余りよろしくない状態になるのも嫌だし・・・。

Apache 2.2.16 における SSL の SNI 設定

Apache 2.2.12 以降では、SSL でも Server Name Indication (SNI) という仕組みで名前ベースのバーチャルホストが設定可能です。

と言うわけで、自サーバ (Debian GNU/Linux Squeeze + apache 2.2.16) で設定をしてみました。

と言っても特別なことは不要で、普通に 80 番ポートの名前ベースのバーチャルホストと同じように設定すれば良いだけです。

・/etc/apache2/ports.conf

NameVirtualHost *:80
Listen 80
<IfModule mod_ssl.c>
    NameVirtualHost *:443
    Listen 443
</IfModule>

SSL で使用する TCP 443 ポートでも「NameVirtualHost」の指定をしてあげます。

・/etc/apache2/mods-available/ssl.conf

SSLStrictSNIVHostCheck Off

上記の指定を追加します。

「SSLStrictSNIVHostCheck Off」は、SNI 非対応環境からのアクセスの場合、一番最初のデフォルトのバーチャルホストへのアクセスを許可する設定です。これを「On」とした場合、SNI 非対応環境からは SSL サイトへの接続自体が拒否されます。

あとは、普通に /etc/apache2/sites-available 以下に通常のバーチャルホストと同じ感覚で、SSL サイトのバーチャルホストを作成していき、a2ensite で有効にします。

ただし、デフォルトとして使用するバーチャルホストは最初に読み込まれる必要があるので、例えば、https://www.example.com/ (設定ファイルは www.example.com-ssl) と https://www.example.net/ (設定ファイルは www.example.net-ssl) があり、SNI 非対応環境からのアクセスは https://www.example.com/ と限定する場合、下記のようにシンボリックリンクのファイル名を変更します。

# cd /etc/apache2/sites-enabled/
# mv www.example.com-ssl 000-www.example.com-ssl
# /etc/init.d/apache2 restart

これで、SNI 対応環境からはそれぞれ該当の SSL サイトへ名前ベースのバーチャルホストでアクセスできるようになります。

なお、SNI 非対応環境からは https://www.example.com/ では証明書エラーが発生せず通常にアクセス可能、https://www.example.net/ では証明書エラーが発生するようになります。

ちなみに、Mac OS X 10.6 の Safari 5.1.7 や、Windows XP の Firefox 13.0.1 では正常にそれぞれの SSL サイトへのアクセスが出来ました。

謎なのが、Windows XP 上の Internet Explorer 8 。この組み合わせでは SNI に対応しないのは知っていたのですが、デフォルト以外の SSL サイトへアクセスした場合、証明書のエラーは出るものの、実は参照しているドキュメントルートは正しいサイトのものであるという・・・。

Apache2 における共有 SSL 機能の実装

Apache2 において、よくレンタルサーバ等で利用されている共有 SSL 機能を簡単に実装できることがわかったので、そのメモです。(説明は良いからすっとばして設定方法へ進む)

そもそも共有 SSL の存在意義ですが、現時点での SSL の実装では、URL も暗号化されて来るため、ウェブサーバ側では実際に暗号を複合してみないと、どの URL にアクセスしてきているのかわかりません。

つまり、単一 IP アドレスしか持たないサーバでは、名前ベースのバーチャルホストで複数の SSL サイトを実現することは出来ません。

何とかして、単一ウェブサーバで複数ドメインの SSL を実装する方法としては、

  • SSL コモンネーム毎に、IP アドレスを割り当てる。
  • SSL コモンネーム毎に、ポート番号を変更する。
  • SAN を含む SSL 証明書 (いわゆるマルチドメイン SSL 証明書) を使う。
  • SNI を使用する。

が考えられます。

最初の方法は SSL 対応すべきコモンネームが必要な個数分の IP アドレスが必要で、現時点での IPv4 の枯渇からして厳しいし、中間の方法は、2 個目のコモンネームから標準的では無いポート番号でのアクセスになるため、環境によってはアクセスできない可能性がある等の理由で現実的ではありません。

SAN を含む SSL 証明書を使うの方法は一見上手くいけそうですが、前提としてブラウザ側が SAN に対応していなければならないし、しかもその単一のバーチャルホストの定義で、証明書に含まれている分の複数のコモンネームでアクセスしてもエラーにならない、と言うことだけあって、名前ベースのバーチャルホストが出来るわけでは無いので、どのみち目的は果たせません。(という認識なのですが、間違っていたらコメントで突っ込みをいただけると嬉しいです)

本命は、SNI を使用する方法なのですが、これも、ブラウザ側の対応と、サーバ側では Apache 2.2.12 以降が必要なので、みんなが SNI 対応ブラウザを使うまで待つしか無いです。

で、前置きが非常に長くなりましたが、SNI 対応クライアント環境の普及や、IPv4 でのアクセスが主流の現時点においての妥協策としては、共有 SSL の実装が無難と考えられます。しかも、先にも書いたとおり、その方法もそんなに難しくないです。(環境は、例によって Debian GNU/Linux 6.0 + Apache 2.2.16 です)

なお、以下のような共有 SSL の設定を想定します。既にバーチャルホストは設定されているものとします。

http://www.example.com/ の共有 SSL での表示は、https://ssl.example.jp/www.example.com/
http://www.example.net/ の共有 SSL での表示は、https://ssl.example.jp/www.example.net/

  • /etc/apache2/mods-available/proxy.conf ファイルを下記のように設定します。

    ProxyRequests Off
    ProxyVia On #こちらは必須では無い。

    基本的に Debian GNU/Linux 6.0 の apache2 においてはデフォルトのままで OK 。

  • 以下のコマンドを入力して 必要な proxy モジュールを有効にします。

    # a2enmod proxy proxy_http
  • https://ssl.example.jp/ を定義しているバーチャルホスト設定部分で下記を追加します。

    ProxyPass /www.example.com/ http://www.example.com/
    ProxyPass /www.example.net/ http://www.example.net/

これで、Apache2 を再起動することで、https://www.example.jp/www.example.com/ にアクセスすると、サーバ内部で http://www.example.com/ へアクセスが行われるため、上記ドメインで、http://www.example.com/ と同一のコンテンツ内容が表示されます。また、www.example.net についても同様です。

注意点としては下記があります。

  • アクセス元がサーバ自身となる。(しかし アクセス元が自サーバであった場合に、HTTP_X_FORWARDED_FOR 変数を参照することで、アクセス元はわかる)
  • 自分自身のサーバ内で完結すること。(他のサーバをアクセス先にすると、リバースプロキシを設定したサーバと他のサーバ感の間の通信は HTTP となり暗号化されない)
  • URL の階層が非 SSL でのアクセスの場合と SSL のそれとでずれる。(これは、ワイルドカード SSL と mod_rewrite を使えば解決できそうだが、携帯電話からのアクセスに対応できないと思われる)

上記中、特に、自分自身のサーバ内で、非 SSL 部分となる通信を完結することは重要です。

Linux: Apache2 のプロセス数を MRTG で表示する

最近、VPS で動作中の Apache2 へのアクセスが増加した際に頻繁にメモリ不足が生じるため、参考情報として MRTG で apache2 + php-cgi のプロセス数をグラフに取るように設定してみた。その際のメモ。

/etc/snmp/snmpd.conf に以下を記述する

proc apache2
proc php-cgi

上記のように snmpd.conf で 2 つの proc を設定した場合、以下の MIB で情報を取得可能。
.1.3.6.1.4.1.2021.2.1.5.1→ Apache2 のプロセス数
.1.3.6.1.4.1.2021.2.1.5.2→ php-cgi のプロセス数

これは snmpd.conf で設定した順番に、
.1.3.6.1.4.1.2021.2.1.5.1
.1.3.6.1.4.1.2021.2.1.5.2
.1.3.6.1.4.1.2021.2.1.5.3
と続くらしい。

あとは、mrtg.cfg で読み出すように設定すればよい。私の場合は以下のようにしてみた。

Target[apache2]: .1.3.6.1.4.1.2021.2.1.5.1&.1.3.6.1.4.1.2021.2.1.5.2:コミュニティ名@ホスト名:
MaxBytes[apache2]: 150
Options[apache2]: gauge, nopercent, integer, noinfo
YLegend[apache2]: Processes
ShortLegend[apache2]: Processes
LegendI[apache2]: Apache2
LegendO[apache2]: PHP-CGI
Legend1[apache2]: Apache2 Processes
Legend2[apache2]: PHP-CGI Processes
Title[apache2]: Web Server Processes
PageTop[apache2]: <H1>Web Server Processes</H1>

上記のように設定した結果、以下のようにグラフ表示がされた。

 

Linux: mod_limitipconn.c のメモ

諸事情あって、Debian GNU/Linux 6.0 squeeze の Apache 2.2 に mod_limitipconn.c をインストールしたメモ。

# apt-get install apache2-prefork-dev

Makefile 中を以下のように修正

APXS=/usr/bin/apxs2
APACHECTL=/usr/sbin/apachectl

# make install

以上で完了する。

設定方法としては、例えばバーチャルホスト内で、以下のように Location を使用して指定する。

<Location />
<IfModule mod_limitipconn.c>
MaxConnPerIP 4
</IfModule>
</Location>

・追記 (2013/06/27)

もう少し詳しく方法を書いたものを、「mod_limitipconn.c に LocalIP ディレクティブ追加のパッチをあててインストールするメモ」として掲載しました。

Net: StartSSL の個人証明書を使ってクライアント認証をする

StartSSL の個人証明書と Apache を使って、SSL クライアント認証してみたメモ。

※本内容はあくまでもメモなので、認識違いなどがあるかもしれない。その場合はご指摘頂けるとありがたい。

■設定したい内容

  • StartSSL の個人証明書を用いて、特定の URL のみ SSL クライアント認証を行いたい。

今回の例では、サーバ内パス /var/www/clilentauth ディレクトリ以下については SSL クライアント認証を有効とする。それ以外は SSL でのアクセスであってもクライアント認証を求めない、と言う設定を行っているつもり。

なお、現時点での私の認識は以下の通り。

  • SSLCACertificateFile で StartSSL のルート証明書と中間証明書指定しているので、StartSSL 以外で同一メールアドレスの個人証明書を持つクライアントがアクセスしてきてもこれは拒否される。
  • SSLCARevocationFile で証明書失効リストを設定する。
  • 証明書失効リストは時々更新してあげる必要がある。(CRL の有効期限が切れると、クライアント認証に失敗するようになってしまうので、追記部分に記載の CRL のアップデートスクリプトを cron 等で自動実行させるようにしておく方が楽)

■設定

  • 認証局のルート証明書をダウンロードする

    StartSSL サイトの Tool Box 内、StartCom CA Certificates より StartCom Root CA (PEM encoded) をダウンロード。
    ※ wget で直接ダウンロードできる
    $ wget https://www.startssl.com/certs/ca.pem

  • 中間証明書をダウンロードする

    StartSSL サイトの Tool Box 内、StartCom CA Certificates より Class 1 Intermediate Client CA をダウンロード
    ※ wget で直接ダウンロードできる
    $ wget https://www.startssl.com/certs/sub.class1.client.ca.pem

  • 証明書失効リストをダウンロードする

    StartSSL サイトの Tool Box 内、StartCom CA Certificates より Server Certificate Bundle with CRLs (PEM encoded) をダウンロード
    ※ wget で直接ダウンロードできる
    $ wget https://www.startssl.com/certs/ca-bundle.pem

  • 中間証明書と認証局のルート証明書を結合

    Class 1 Intermediate Client CA 、StartCom Root CA の順番で結合。
    $ cat sub.class1.client.ca.pem ca.pem > StartCom_Root_CA.pem

  • 連結した証明書ファイルを所定のディレクトリへコピー

    # cp StartCom_Root_CA.pem /etc/ssl/certs/

  • 証明書失効リストファイルを適当な名前で所定のディレクトリへコピー

    # cp StartCom_Root_ca-bundle.pem /etc/ssl/certs/

  • apache の SSL を設定している箇所で以下を指定

    # 認証局の中間証明書とルート証明書を結合したファイルを指定
    SSLCACertificateFile /etc/ssl/certs/StartCom_Root_CA.pem

    # 認証局の証明書失効リストファイルを指定
    SSLCARevocationFile /etc/ssl/certs/StartCom_Root_ca-bundle.pem

    # SSL クライアント認証を求めるディレクトリ
    <Directory /var/www/clilentauth/>

    # SSL クライアント認証を求める
    SSLVerifyClient require

    # たどる証明書の枚数は 2 枚
    SSLVerifyDepth 2

    # クライアント証明書に含まれるメールアドレスが「メールアドレス」と一致することを要求
    #メールアドレスは、ログインさせたいクライアント証明書に含まれるメールアドレスとする。
    SSLRequire %{SSL_CLIENT_S_DN_Email} eq “メールアドレス”
    </Directory>

  • /etc/apache2/mods-available/ssl.conf

    SSLInsecureRenegotiation on
    # Mac OS X の Safari 5 では上記の設定を行わないとクライアント認証そのものが成り立たなかった。

続きを読む

Linux: WebDAV を設定してみた

PSP はかなりパソコンと親和性の高いハードなのだけれど、Mac とのデータの受け渡しの際に、いちいち USB ケーブルを接続するのが面倒。

双方とも LAN に接続しているのだから、ネットワーク経由でデータをやりとりすれば楽、と言うことで、WebDAV を設定してみた。

うちの自宅サーバは Debian なので、
# apt-cache search webdav
を実行して、パッケージを確認。

で、インストールは
# apt-get install libapache-mod-dav
とした。

あとは、WebDAV のディレクトリを適切な場所に www-data:www-data (660) で作成。

htdigest コマンドで、Digest 認証のパスワードファイルを作成する。

/etc/apache/conf.d/libapache-mod-dav ファイルを編集して、そのディレクトリを WebDAV でアクセス可能に、および Digest 認証をかけるように設定する。
※必ず何らかのアクセス制限を設定する。

Apache を再起動する。

Mac OS X の Finder の「移動」-「サーバへ接続…」より、先ほど設定した WebDAV の URL を入力して、Digest 認証で設定したユーザ名とパスワードを入力し、マウントできることを確認。

基本的にこれで OK みたい。こんなに簡単で良いのか不安だが。

で、これだけでは、PSP 側から Mac へのデータの受け渡しができないので、WebDAV として設定したディレクトリ内に、適当なディレクトリを作成し、ここに、ファイルをアップロードするような CGI 等をインストールする。
つまり、PSP からは、ブラウザで「http://WebDAV の URL/適当なディレクトリ名/アップロード CGI」と言った具合にアクセスし (無論、ここも先ほど設定した Digest 認証が効いている) 、アップロード CGI からファイルをサーバに転送する。
Mac からは、普通に WebDAV ボリュームをマウントし、Finder でアップロードされたファイルをローカルにコピーできる。

逆方向は、Mac でマウントした WebDAV ボリュームにファイルをコピーし、PSP 側では、そのファイルの URL をブラウザでアクセスすれば OK 。

なお、基本的に LAN 内を想定した設定なので、Digest 認証の部分以外は、平文で情報がネットワーク上を流れるので注意が必要。

・参考にさせていただいた URL
●debian でwebDAV をインストール
mod_dav のインストールと設定