perlを使用したapacheの拡張


------------

## はじめに
<p>本記事でお話させていただくのはapacheの強力な拡張モジュールとして利用されるmod_perlについて。</p>
今回とある依頼からPHPでいれたセッションを、mod_perlで拡張したapacheのロジックでセッションがあるかどうかの判定をする仕組みが必要となった。
本来であれば対象のサイトのPHPにセッション判定のロジックを入れるのがいいのだが元々外に公開する仕様で作成されていない為、かなりの数があるページの一つ一つを分析して仕組みを導入する点および動作確認に相当な時間が掛かる点を鑑みて別の仕組みが必要となり、コンテンツにたどり着く前のapacheで実施する方法が確実で簡単であり時間的コストを圧縮できることから本方式を採用。

仕組み自体はかなりシンプルなものである。
1. ユーザのアクセスIPをチェック。信頼されたネットワークにいる場合は[200 OK]それ以外のユーザについてはセッションをチェック。
2. DBからセッション情報を取り出し、PHPで格納されたセッションである為perl側でunserializeして読める状態にする
3. セッション情報が正しければ[200 OK]を返す/セッション情報が正しくなければ[302 redirect]でログインページに飛ばす

それ以外にエラーハンドリングの処理としてエラー発生時にメールを送る処理。
またエラー発生時のメールの輻輳を回避する為にフラグファイルを読み込んで、前回エラー送信時間から1時間以内はメールを送出しない仕組みを実装
↑インフラエンジニアの私としてはメールの配信等のトラブルシュートの時間に寄与する部分のロジックについては、
メインのプログラムと同様に重要なロジックであるといえる

プログラムの詳細な内容については次回以降で記載する。

## mod_perlとは
apache上でperlを動かすための組み込みインタープリターである。
apcahe上で各種スクリプト言語を動かすモジュールはいくつかあるが、使用用途にもよるがhook出来るフェーズの多様さ(※後述)およびapache状に組み込める豊富なライブラリ、さらにCGIの挙動である呼び出されるたびにコンパイルするのではなくコンパイルされた状態でオンメモリーに保持される為、高速に稼働することを考えるともっとも活用余地のあるモジュールではないかと考えている。

## hookについて
apacheのメインパートは25,000行ほどのC言語で書かれたコードであり、apcaheのリクエスト処理の各フェーズにおいてフックポイントと呼ばれるモジュールが介入する余地を残しており、この各フックポイントに処理を登録することでapacheの各フェーズでperlやpython等のプログラムを実行する事ができる。
ただしどのフックポイントに処理を登録できるかについては、対象モジュール(mod_perlやmod_lua等)に依存する為注意が必要である。

下記はapacheのリクエスト処理の流れに沿って、mod_perlでフック出来る12のフックポイントを記載した。

#### 1.POST_READ_REQUEST
ヘッダー解析後に最初に呼ばれるフェーズである。このフックポイントにおいては各フェーズに渡す前に初期化をする処理等を実施する際に非常に有用なフェーズである。Apache::StatINCモジュールを読み込んでおくとperlファイルの差分を検知して更新があれば自動でロードするような事もできる。

#### 2.Trans
URIを実際のファイルに紐づけるフェーズである。
apacheの一つの強力な機能としてURIを柔軟に細かく変更できることがある。ここにフックポイントを設けることで、URIをモジュールで変更できるようにしている。
ここのフェーズにてURLを変換するモジュールとしてはmod_rewrite等が広く利用されている。

#### 3.MapToStorage
紐づけたファイルに対して設定されているディレクティブであるDirectoryやFilesのようなconfigを処理するフェーズである。
または併せて.htaccessの内容もこのフェーズで解釈される。
ここにフックポイントが設けられているモジュールはわりとすくない。

#### 4.HeaderParser
リクエストヘッダーを調べて、ヘッダーの情報を操作するフェーズである。ここの実装では、標準で定義されているGET,HEAD,POSTといった標準的なメソッドの実装以外でここに処理を定義することで拡張して対応することが出来る。

#### 5.Access
このフェーズはこの処理の後に引き続く2つの処理とセットでAAA(アクセス制御,認証,アクセス許可)という形で広く知られている。主にはユーザIDを使用するようなBASIC認証を除いた、アクセスを制御するフェーズである。例を挙げるとIP制限、リクエストのアクセス日付等をチェックし処理結果に応じたステータスコードをセットする。今回作成したロジックはperlでsessionを判定してステータスコードを返すロジックである為、このフェーズでフックさせている。

#### 6.Authen
ユーザIDを用いた認証を実施するフェーズである。Basci認証やDigest認証などの処理を担当する。チャレンジ/レスポンス方式にてAuthorization Requieredを使用してユーザに認証を要求する。ユーザはAuthorizationフィールドを付加してレスポンスを再送信するような仕組みとなっている。
その他の認証方式としてスマートカードやデジタル証明書等も利用できる。

#### 7.Authz
このフェーズにおいてはAuthenフェーズで入力された情報をもとにそのユーザがアクセスしようとしているリソースが許可されたものかどうかを判定する。

#### 8.Type
このフェーズにおいてはリクエストドキュメントのMIMEタイプおよびドキュメントタイプを判定する処理が実施される。判定は主にファイル名の拡張子で判定される。mod_mimeモジュールによってmime.typeファイルに格納されている情報とAddTYPEおよびAddEncodingディレクトリの情報を組み合わせてMIMEタイプ/ドキュメントタイプとのマッピングをする。

#### 9.Fixup
このフェーズはコンテンツハンドラーが呼び出される前にモジュールに微調整の機会を与えるために設けられたフェーズである。mod_envはこのフェーズにおいてCGIやSSIページに環境変数を渡すために利用される。

#### 10.Response
このフェーズではレスポンスを作成する処理が実施される。
この処理は最も重要なフェーズといっても過言がない。動的なコンテンツ作成やコンテンツの改変等CPANに登録されているapache関連のモジュールの多くはこのフェーズにフォーカスしているものである。

#### 11.Log
このフェーズは名前の通り上記のフェーズで蓄えてきたステータスコードの情報やサーバからユーザに送信されたバイト数などアクセスに関する情報を様々な方法で書き出すことが出来る。例えばファイルやDatabase等である。このフェーズにおいてはログのフォーマットを柔軟にカスタマイズすることが可能となる。

#### 12.Cleanup
cleanupフェーズはapacheには存在せず、mod_perlの内部にのみ存在するフェーズである。この処理はC APIで提供されているメモリプールが破壊される直前に呼び出されるコールバック関数を利用している。このフェーズにおいてはDBのコネクションクローズやファイルのクローズ等に利用される。

## フックポイントの種類について
各フックポイントは以下の3つのタイプに分類される

#### VOID
登録されているフックポイントの処理はリターンコードに関わらずすべて実行される。フックポイントのtypeがvoidに登録されている処理はすべてOKを返すことが予期されている。

- 該当のフックポイント(上記記載内)
  - なし

#### RUN_FIRST
登録されている順番に応じてフックポイントの処理が実行される。各フックポイントの処理のリターンコードがDECLINEDを返さないかぎりは順々に実行される。もしDECLINEDが返された場合はそのフックポイントの処理を中断し次のフックポイントに移行する。

- 該当のフックポイント(上記記載内)
 - Trans
 - MapToStorage
 - Authen
 - Authz
 - Type
 - Response

#### RUN_ALL
登録されている順番に応じてフックポイントの処理が実行される。各フックポイントの処理がOKまたはDECLINED以外の値を返した場合にフックポイントの処理が中断し次のフックポイントに移行する。

- 該当のフックポイント(上記記載内)
 - PostReadRequest
 - HeaderParser
 - Access
 - Fixup
 - Log
 - Cleanup

## おわりに
mod_perlはapacheを拡張する上で非常に強力なモジュールであり、実装次第ではロードバランスや不正なユーザの排除などあらゆることが可能である。
mod_perlの実装については以下ページにドキュメントや豊富なsampleがあるため、作成する際にかなり有用である。

https://perl.apache.org/docs/2.0/index.html

明日は @a_tamura さんです。よろしくお願い致します。