複数のSSL証明書をインストールする方法
HTTPS を使うとき、ウェブサーバには SSL証明書をインストールする。通常、一台のサーバにつき一つしか、SSL証明書はインストールできない。いや、できるのだが、複数のIPアドレスをサーバに割り当てるか、別のポートでHTTPリクエストを受けるかをしなくてはならない。前者は Amazon EC2 をはじめとするレンタルサーバでは使えないことが多いし、後者はビジネス要件として許されないことが多い。
一般的に使われている名前ベースのバーチャルホストでなぜ複数証明書を使えないのか。それは、どのバーチャルホストを使うかを決める情報が、クライアントから送られるHTTPリクエストヘッダ中のHostフィールドに入っているからである。つまりHTTPレイヤの情報だからだ。HTTPS で通信するときは、まずクライアントとサーバとで SSL の通信路を確立してから、HTTPのリクエストを送る。どのバーチャルホストを使うかという情報は SSL の通信確立の時点では知りようがないため、複数の SSL 証明書の中から選んで通信することができない。
これを解決する方法がいくつかあるが、どれも使い勝手がよくない。1つ目はワイルドカード証明書を使う方法。hoge.example.com と bar.example.com というような2ドメインを対象としたいようなときは、この手が使える。ただし example.com や sub.hoge.example.com などのように、階層がずれたものは対象にできない。
2つ目はSSL証明書の Subject Alternative Names を利用する方法。SSL証明書内部に、こっちのドメインもあっちのドメインも対象にするよ、という情報を Alternative Names(別名)として埋め込むことができる。すごく便利なようだが、ガラケーが未対応なので、携帯サイトを作りたいときには使えない。
3つ目は、Server Name Indication という SSL の拡張仕様を使う方法。どのバーチャルホストを使うかを、SSL のプロトコルで指定できるようにしたものだが、未対応のクライアントも多いため、まだ利用できる状況にはない。
というわけで、複数のドメインでSSLを使う場合は、複数のIPアドレス、あるいは複数のサーバ(つまり複数のIPアドレス)を用意しましょう、というのが現時点での標準的な方式になっている。あと3年はこのままだろう。
HTTPクライアントの接続元IPアドレスを知る
ウェブサーバで、クライアントの接続元IPアドレスを見て挙動を変えたいということはよくある。社内からのアクセスだけ特別扱いしたり、携帯電話キャリアのからのアクセスの時だけコンテンツを表示するなど、用途は広い。
普通、このIPアドレスは REMOTE_ADDR という変数名で取得できる。これはもともと、CGI で使われた仕様(http://tools.ietf.org/html/draft-robinson-www-interface-00)であるようだ。Apache の mod_rewrite なら %{REMOTE_ADDR} で、PHP なら $_SERVER[‘REMOTE_ADDR’] で参照できる。
ところがロードバランサやプロキシを間に挟むと、REMOTE_ADDR がロードバランサ等のアドレスになる。この場合、多くのロードバランサ等では HTTP のリクエストヘッダに X-Forwarded-For フィールドを追加する。これは HTTP の仕様ではないものの、デファクトスタンダードだ。
REMOTE_ADDR は IPレイヤの情報だが、X-Forwarded-For は HTTPレイヤの情報である。データの取得元が変わるので、値の取り方も変わる。mod_rewrite なら %{HTTP:X-Forwarded-For} だし、PHP なら $headers = apache_request_headers(); $ip = $headers[‘X-Forwarded-For’]; となる。
IPレイヤの REMOTE_ADDR とは違い、HTTPレイヤの X-Forwarded-For はクライアントによる偽装が容易なので、インターネットなどの信頼できないネットワークから直接HTTPリクエストを受ける経路も存在する場合、X-Forwarded-For のみを信じて大事な情報の表示可否を決めてはいけない。REMOTE_ADDR を併用したチェックが欠かせない。
HTTP で便利に使える X-Forwarded-For だが、HTTPS では利用できない。HTTPS では通信がエンドトゥーエンドで暗号化されるため、途中に挟まるプロキシ等が X-Forwarded-For ヘッダを挿入することができない。REMOTE_ADDR もプロキシのものになってしまって元のIPアドレスがわからないし、X-Forwarded-For も使えないので、アクセス元を判定できない。
HTTPS でアクセス元IPアドレスを知りたい場合は、ロードバランサやプロキシを通さずに HTTPS を受けるようにするか、SSLアクセラレーションあるいは SSLターミネーションと呼ばれる機能を使ってロードバランサで SSL を受け、復号化してウェブサーバに送るようにすればよい。
オブジェクト指向の終焉
オブジェクト指向って、もう流行らないな、と思う。
2000年ころは熱かった。Javaの影響だろう。それまで主流だったCに文法が似ていて、Cよりも簡単な Java が業界のデファクトスタンダード言語になりつつあった。Java は、初めての標準オブジェクト指向言語だった。プログラマなりたての人から熟練者までが使う、初めてのオブジェクト指向言語だった。もちろんC++ もあったが、プログラマ一年生がいきなりC++を使うことはない。VC++ から入る不幸なプログラマも多数いたが、オブジェクト指向云々の前に MFCフレームワークの大海に溺れた。
VC++にはフレームワークや便利なIDEがあったが、Javaには何もなかった。利用者が多い割には言語とクラスライブラリ以外に何もない、素のオブジェクト指向言語。テキストエディタに自分で Class Person { … } などと打たなくてはならない。否が応でもクラスを意識する。アプリケーション全体を視野に収めなければ、プログラムの理解ができなかった。それでオブジェクト指向が流行った。同時に、オブジェクト指向設計を表記するための UML も注目された。
しかし2001年にWebアプリケーションフレームワーク Struts がリリースされたときから、状況は変わる。Java でも、フレームワークの上に乗っかって、必要な処理を穴埋めするだけでアプリケーションが作れるようになった。決められたクラスを継承して、決められたメソッドを実装すれば済むようになった。こうなると、自分でクラスの構造や振る舞いを考える必要がなくなる。
いま流行っている iOS や Android でも、すでに巨大なフレームワークが存在して、プログラマは穴埋めするだけで済むようになっている。フレームワークが膨大すぎて溺れそうなのは MFC と大差ないが、オブジェクト指向が無くても仕事はできる。
オブジェクト指向は、フレームワークの利用者が必要とするものではなく、フレームワークの作成者に不可欠の技術、思考法だ。これを必要とする人は、それほどいない。今思えば、Java は初めてにして最後の、素のデファクトスタンダードオブジェクト指向言語だったのではないだろうか。
Windows で Apache2.2 + PHP5.3
Windows で Apache2.2 + PHP5.3 の環境を作った。たかが Apache + PHP と思ったが、サクッとはいかなかった。
現時点で簡単にインストールできるのは、http://windows.php.net/download/ の PHP 5.3 VC9 x86 Thread Safe (2011-Mar-22 13:27:32) と、http://www.apachelounge.com/download/ の httpd-2.2.19-win32-x86-ssl.zip の組み合わせ。必ず Thread Safe版を使う。Non thread safe版には、Apache のモジュール php5apache2_2.dll が同梱されていないので、Apache とはつながらない。
windows.php.net で提供されている PHP 5.3 は VC9版で、これは Visual C++ 2008 でビルドされていることを意味する。PHP 5.2 は Visual C++ 6 でビルドされた VC6版だった。
Apache 上で使う場合、VC9 なら VC9版 Apache を、VC6 なら VC6版 Apache と組み合わせる必要がある。Apache の公式サイト http://httpd.apache.org/ で配布されているバイナリは VC6版で、PHP 5.2 は動くものの、上記 PHP 5.3 は動かない。そこで http://www.apachelounge.com/download/ のバイナリを使う。このバイナリにはインストーラがない。サービスとしても登録してくれない。なので、コマンドラインから httpd -k install と打って、自分で登録する。Apache Monitor も、自分で Startup に登録する。
これでようやく入り口に立てた。
Amazon EC2 の安定性
Amazon EC2 のインスタンスは、結構簡単に止まる。負荷に耐えられなくなると、無反応になる場合がある。ssh ができず、AWS Console からのリブートも効かず、Terminate もできない。ping にはかろうじて反応していることもある。OS の /var/log には、止まっている間の記録は何もない。
こんなときどうするか。アタッチした外付けEBSのスナップショットを取ってボリュームを作り、他のインスタンスにつなげるくらいしか、できることがない。それすらもできないかもしれない。EBSのデタッチはできないと思ったほうがいい。強制デタッチもできない。ルートパーティションのデータは救い出せない。復旧するのを待つしかない。
Premium Support サービスを購入していなければ、AWS の公開フォーラムに書きこんで、中の人が対処してくれるのをじっと待つ。あるいはフォーラムに書きこまなくても、Amazon の監視で引っかかればリブートしてもらえるようだ。この前止まったときは、12時間以上たってからリブートされていた。12時間以上止まっているインスタンスをリブート対象にしているのかもしれない。
AWS Premium Support は、技術的支援を受けるためというよりは、大事なインスタンスが止まってしまったときに緊急対応してもらうためにあるのではないだろうか。品質の低いものをわざと提供してそこで稼いでいる、とまでは思わないが。
教訓 ・EC2インスタンスはいつ止まってもおかしくない。 ・救い出したいデータは、必ず外付けのEBSに格納する。 ・負荷が集中しそうで、止まって欲しくないサーバは、Microインスタンスなどではなく、処理能力に余裕のあるインスタンスのほうがいい。