Apache

WebサーバーApacheについての話題

2009年12月 6日

Segmentation faultが発生するとHTTPレスポンスが返らない

Linux+Apache2 + PHPの環境において、特定のページだけがブラウザでアクセスしても「ページが表示できません」となってしまう。

VirtualHostディレクティブで設定しているaccess_logをtail -f で監視していても対象のページだけリクエストがログに残らない。ページが表示されるときには正しくログに残っているので、書き出し先が間違っているわけでもない。

次にVirtualHostディレクティブで設定しているerror_logを見てみたが考えられるエラーは記録されていない。

その次にWiresharkでTCP通信を監視してみた。すると
(正常系)
クライアント --(SYN)--> サーバー
クライアント <--(SYN+ACK)-- サーバー
クライアント --(ACK)--> サーバー
クライアント --(ACK)--> サーバー HTTPリクエスト
クライアント <--(ACK)--> サーバー HTTPレスポンス
と続くはずが

(異常系)
クライアント --(SYN)--> サーバー
クライアント <--(SYN+ACK)-- サーバー
クライアント --(ACK)--> サーバー
クライアント --(ACK)--> サーバー HTTPリクエスト
クライアント <--(ACK)-- サーバー
クライアント <--(ACK+FIN)-- サーバー
クライアント --(ACK)--> サーバー
クライアント --(ACK+FIN)--> サーバー
で通信が終了してしまっている。

つまりHTTPレスポンスが返っていないのだ。
(このときはなぜにFINを返してしまうのだーと正直思った)

Linuxでもtcpdumpを使って解析したが、同じパケットがやり取りされており、通信経路上の問題点ではないことがわかった。

そうなるとTCPレイヤーが上位アプリケーション層にパケットを渡さなかったか、アプリケーション層が受け取ったパケットに対する応答をTCPレイヤーに返せないかのどちらかである。

/var/log/messageにはなにも記録されていなかったので、前者は考えにくく、またこの現象はPHPプログラムを入れ替えた機能から発生していることをみると、プログラムが怪しそうだったので、前のプログラムに入れ替えるとやはり問題が改善していた。

プログラムで問題のあると思われるところを修正した結果、正しくページが表示できるようになった。

これでめでたしめでたしだったが、ステータスコードが返せない理由がよくわからなかったので、ログをいろいろ調べてみた。
すると、httpd.confで設定しているerror_logをみると
[notice] child pid 21450 exit signal Segmentation fault (11)
という記録があった。

今回勉強になったのは次の3点
・Segmentation faultなどの致命的な問題が起動プログラム上で発生した場合、ApacheはログをVirtualHostディレクティブで指定したErrorLogのパスに書き出してくれない。(子プロセスが落っこちるから仕方ない?)
・また、Apacheの子プロセスが落っこちた場合にHTTPレスポンスを返せない。
・access_logを記録するのはHTTPレスポンスを返したあとなので、リクエストを受け取れたとしてもaccess_logには記録が残らない。

初めて遭遇した不可思議なこのトラブルには解決に2時間くらい要してしまった。。。

2008年10月 8日

リバースプロキシを利用する上での問題点

Apache2.0から利用可能となったリバースプロキシ機能で ProxyPassとProxyPassReverseを利用することで本格的なロードバランサーを作成できる。

ただ、このロードバランサーを利用する上での問題点もあることは事実。その問題点について説明したい。

1.ProxyPassの指定先が異なるディレクトリだった場合においてコンテンツ内に絶対パスが含まれているとリンクがうまくいかない

たとえば、
ProxyPass / http://192.168.0.1/
などであれば問題はないが、
ProxyPass /share http://192.168.0.1/
などとしてしまうと絶対パスが指定されたリンクではうまく行かないことがある。また、baseタグが利用されているコンテンツがある場合にも注意が必要。


2.ProxyPassの指定先のホストが外部からは接続できないようなセグメントに配置されている場合において、HTTP_HOSTというサーバー環境変数をリンクに利用している場合にうまくいかない

たとえば、
ProxyPass / http://192.168.0.1/
と設定してリバースプロキシサーバーにグローバルIPアドレスが振られているケースでインターネットからリバースプロキシサーバーにアクセスするとはHTTP_HOSTの返答値が192.168.0.1になってしまい、それがリンクに使われているようなことがあると、そのリンク先をクリックすることでプライベートIPアドレスに接続に行ってしまうことになる。これはSERVER_NAMEという環境変数を使った上で、httpd.confのServerNameの設定値が外部DNSにおいて正引き可能なホスト名でかつ外部から到達可能なIPアドレスで解決できなければならない。なお、UseCanonicalNameがOffになっているとhttpd.confのServerNameは利用されないので、Onにする必要がある。(ApacheのデフォルトはOn)


もしリバースプロキシによるロードバランシングを検討されている場合には、上記のようなコンテンツ構成になっていないかを注意する必要があるだろう。

2007年9月 3日

クロスサイトトレーシング

TRACEメソッドを利用したクロスサイトトレーシングという攻撃手法があるそうだ。
概要ならびにその対策については、TraceEnable メモというのがあるので、Apache管理者の人は是非一読してみて欲しい。

2007年8月24日

1サーバーに格納していたコンテンツを分離する方法

もともと1サーバーにコンテンツを丸ごと格納していたものを別々のサーバーに分離するようなことがあった(CGIプログラムなどは暴走するとコンテンツがすべて見れなくなるので、それを別サーバーに分離して影響度を減らすようにした)ので、そのための方法を考えてみた。

○やりたいこと
HTML,CGI,PHPファイルおよびその他画像ファイルがあり、いままではAサーバーに格納されていた
それをCGI,PHPコンテンツのみをBサーバーで実行させ、残りをAサーバーで実行させる

○実現方法
1.CGI,PHPファイルをBサーバーへ移動させ、CGI,PHPファイルへリンクしているHTMLファイルのパスを変更する。またCGI,PHPからそのたファイルへリンクしているパスを変更する。
→修正範囲が多すぎるのであっさり却下

2.RedirectMatchディレクティブをAサーバーに設定し、CGI,PHPファイルへのリクエストがあった場合Bサーバーへ転送する
→RedirectMatchを使うとGETリクエストは問題ないが、POSTリクエストされた場合、クライアントに対しては、301,302などのステータスコードが返され、クライアントはBサーバーへGETリクエストで再度リクエストする。そのため、POSTデータがBサーバーに対しては転送されないので、正しくコンテンツを遷移できなくなるのでやはりあきらめた。

3.リバースプロキシの仕組みを利用し、CGI,PHPファイルへのリクエストがあった場合、AサーバーはBサーバーへまったく同じ問い合わせを行って、その結果をクライアントへ返す
→これであればまったく問題ないことが分かった。

リバースプロキシ(通常のプロキシサーバーはフォワードプロキシといわれている)の仕組みはApacheを使って安価にロードバランサーを実現する際にも使われるので、設定を熟知できるようにしておきたい。

2007年7月18日

1.3系のWindows用Apacheのインストーラが見つからない

もうすでにApacheは2.2がリリースされており、1.3系の古いApacheはもう使われないのかもしれない。
だけど、何らかの理由によって1.3系のApacheを使いたいとしても、apache.orgには1.3系のWindows用のバイナリパッケージを探すことはできない。tgzファイルはリンクが貼ってあるが・・・。

アーカイブ用のページが実は存在していて、1.3系のWindows用バイナリパッケージがあるので、もし必要なときには覗いてみると良いだろう。

2007年5月 6日

ホスト制限が効かない

<Directory (pathname)>
order allow,deny
allow from all
deny from XXX.XXX.XXX.XXX
</Directory>

Apacheでホスト制限が効かない場合には、以下が考えられる。
1.pathnameがそもそも勘違いしている
pathnameはDocumentRoot以下の絶対パスではなく、システム上のパスである。Windows版のApacheならば、c:\などから始まることになるだろう。

2.同一のディレクティブが存在しないかをチェックする
httpd.confファイルとそのファイルがincludeしている全てのファイルにおいて、同じ
<Directory XXX>
が存在していないかを確認する。経験では、同一のpathnameを持つDirectoryディレクティブは後に出てくるものが無視されているように思える。どのような使用になっているかはもう少し確認してみる必要がありそうだが、同一のpathnameを持つDirectoryディレクティブが存在していないかを確認してみると良いだろう。

もし同じものが見つかったならば、見つかった方に追加するかもしくはpathnameをもっと詳細なパス(たとえば、/var/wwwなど)を設定するようにすれば良いだろう。

2007年3月19日

Webサーバーの環境を破壊した?

友人のサーバーにPHPアプリケーションを組み込んでいたら恐ろしいことが起こった。

コンテンツ組み込みにあたりApacheの設定変更が必要になり、設定ファイルを変更して/etc/init.d/httpd configtest
を実行したらなんと大量にエラーが発生するじゃないですか。

そこでやめておけば良かったのにtry&errorで何とかできるレベルだと思ってエラー箇所をコメントアウトしてエラーが出なくなったことを確認して、強引に再起動したわけです。

もちろんPHPは動かず。たしかPHPは動作する環境だって聞いてたはずだし、どうやらPHPのマッピングがうまくいってないんだなと勝手に判断してガリガリ設定ファイルに書くもうまくいかず。

落ち着いて考えてみたらLoadModuleも入ってないんじゃねーのと気づくが、どうやらmoduleディレクトリにPHPのライブラリらしい物は見あたらず。。。

ただphp -vは通るわけで、変だなぁと思ってrpmの一覧を調べたらなんと入ってないわけです。えっ?ソースコンパイル?でもさっきの一覧にApacheはあったしなぁ・・・?

まさかと思ってPHPのライブラリをルートからfindしてみたら愕然としました。なんとApacheもソースコンパイルされてるし。

どうやら再起動コマンドに
/etc/init.d/httpd
ではなく /usr/local/apache2/bin/apachectl
を使わなきゃいけないってオチだったらしい。

念のため、
/sbin/chkconfig
してみたらやっぱりhttpdはrunlevel=4以外がすべてoffになってましたとさ。(えっ?でも4だけonなの?と突っ込みたくなりましたが)

もーまぎらわしいからrpmはアンインストールしといてよと言いたくなりました。

2007年1月15日

Apacheが起動していてもWebサーバーが見れない

Apacheが起動していてもWebサーバーが見れない。。。そんなときは??

まずpingの応答が返ってくるかどうかをまず確認する。(ホストに到達できるかチェック/ネットワーク層のチェック)
返ってくるようであれば、フィルタリング設定(Linuxならiptablesなど)がされていないかどうか、ファイアウォールが設定されていないかを確認する。(トランスポート層のチェック)
それでも問題がなく、OSがLinuxであれば、SELinuxが設定されていないかどうかを確認する。(アプリケーション層のチェック)

もしSELinuxを疑う場合には、configファイルを参照し、enforcingなどになっていればdisabledにしてアクセスできるかを確認する。なお、SELinuxの変更適用はOSの再起動を実施する。
# vi /etc/selinux/config

#SELINUX=enforcing

SELINUX=disabled

2006年12月17日

Apache + OpenSSLのインストール方法

#サーバーの秘密鍵の生成(key.pemが作成される)
openssl genrsa -des 1024 > private/key.pem

#パスフレーズの削除
openssl rsa -in private/key.pem -out private/key.pem

#証明書発行に必要なCSRの生成(key.pemからcsr.pemが作成される)
openssl req -new -days 365 -key private/key.pem -out csr.pem

#サイト証明書の作成(key.pemとcsr.pemからcert.pemが作成される)
#※自局認証でない場合には、この作業はCAに行ってもらう
openssl x509 -in csr.pem -out certs/cert.pem -req -signkey private/key.pem -days 365

#apacheのconfファイルへ設定する情報
SSLCertificateKeyFile key.pem
SSLCertificateFile cert.pem

2006年11月26日

httpdをサービス登録する

ソフトウエアをinet.dにサービス登録するためにはchkconfigコマンドを利用して以下のように行う。

# /sbin/chkconfg --add httpd #おそらくrpmインストールされているサービスはこのコマンドが実行されている

# /sbin/chkconfig --list httpd #現在のサービス登録状況を確認.
httpd 0:off 1:off 2:off 3:off 4:off 5:off 6:off #なにも登録されていない.

# /sbin/chkconfig --level 35 httpd on #ランレベル3, 5にて起動するように設定する.

# /sbin/chkconfig --list httpd #もう一度現在のサービス登録状況を確認.

httpd 0:off 1:off 2:off 3:on 4:off 5:on 6:off #うまく反映されている

2006年7月30日

Apacheの設定「KeepAliveTimeout」編

M/M/1待ち行列モデルからKeepAliveTimeoutの有効性についてうまく説明してある。うーん確かに、タイムアウトを延ばせばサービス時間が延びて待ち行列ができやすくなるわけですね。
http://akalabo.net/archives/2005/09/apachekeepalive.html