AWS Elastic Load Balancer と Apache を使って HTTPS(SSL) 通信を行うための設定方法

AWS Elastic Load Balancer で HTTPSSSL)通信を行うための準備、設定についてのメモ。目標とするサーバー環境のイメージは下記の通り。

Elastic Load Balancer の設定 - Listener Configuration

Elastic Load Balancer(以下、ELB)とは?

ELB の一般的な設定方法は、下記の記事が参考になった。


今回、クライアントとアプリケーション間の通信は HTTPS 通信を行う。まずはじめに ELB と EC2 Instance の役割に関して、軽く説明をする。その後で、詳細な設定方法を説明しようと思う。

ELB と EC2 Instance の関係については、下記の図を見てほしい。
クライアントが任意のドメインへ接続しようとした時、ELB へ繋がるようにする(これを【接続1】とする)。ELB は自分が管理する EC2 Instance へリクエストを振り分ける(これを【接続2】とする)。


この【接続1】と【接続2】の設定を、ELB では「Listener Configuration」と呼んでいる。先に「Listener Configuration」の設定内容を下記に示す。設定は下記の設定を、2つとも入力すること。

  1. 【接続1】HTTP:80 → [ELB] → 【接続2】HTTP 80
  2. 【接続1】HTTPS:443 → [ELB] → 【接続2】HTTP 80




【接続1】に注目してほしい。HTTP と HTTPS をリッスンしている。これに関しては説明不要かもしれないが、念のため解説すると、ユーザーが HTTP(80ポート)で接続してきた時に、HTTPS(443ポート)で接続し直すように要求させたいので、HTTP・HTTPS 両方共リッスンしている。

例えば、Facebook は通常 HTTPS 通信を行うようになっているのでアドレスは「https://www.facebook.com/」である。これをHTTPプロトコルhttp://www.facebook.com/」でアクセスするとどうなるか?即座にアドレスが「https://www.facebook.com/」に切り替わっている。これはユーザーが HTTP 接続をしてきた時に、HTTPS にリダイレクトしているのである。

もし HTTP の設定を行わない場合は、クライアントが「http://www.facebook.com/」へアクセスした時、単純に接続エラーになってしまう。



【接続2】は HTTP(80ポート)で通信を行うようにした。ELB ←→ EC2 Instance 間の接続では暗号化が行われない。実はここの通信で HTTPS(443ポート)を使うことも可能だ。ここで、どちらの設定を利用するべきか?ベスト・プラクティスが何なのか、私にはわからなかった。

調べた限りの情報を記すので、あとはみなさんの判断で、どちらを選択するか考えて頂ければと思う。


ELB での HTTPSSSL)通信について

ELB に SSL 証明書をインストールすることで、次のような恩恵を受けることができる。

・クライアント接続の終了に使用する SSL サーバー証明書を、個々のアプリケーションインスタンスではなく、ロードバランサーで集中管理できます。

SSL トラフィックの暗号化と復号化の作業が、アプリケーションインスタンスからロードバランサーに移されます。

ロードバランサーは、着信する HTTPS リクエストを終了してからコンテンツを再暗号化してバックエンドアプリケーションインスタンスに送信することで、セッションのアフィニティ、つまり「sticky セッション」を確保できます。

・HTTP で使用可能な機能はすべて HTTPS 接続で使用できます。

よく分かっていないのが、もし ELB と EC2 Instance 間の通信を HTTPS にしたい場合、それぞれの EC2 Instance に SSL証明書のインストールを行わなければいけないのか?ということ。ELB と EC2 Intance 間の通信を HTTP にすることで、そういったことは考えなくて良くなりそうだが、ELB と EC2 Instance 間の通信が盗聴されてしまう危険性はないのか?

ELB と EC2 Instance 間の通信内容の安全性に関して、きちんとした資料を見つけることが出来なかったが、似たような議論が AWS フォーラムに上がっていた。

Whenever you are connecting to an RDS instance from an EC2 instance within the same region, that communication is going through AWS private network.

However traffics between RDS DB instances and EC2 instances which are located in different regions will go through the internet.

If you are concerned about security, you may want to use SSL for communication between RDS DB instances and EC2 instances.

AWS では同一リージョン間の通信に関しては、AWS のプライベート回線を使っているため、第三者がこれを盗聴することが出来ない。しかし、もし心配ならば SSL 通信を用いることも出来る。という温度感のようだ。



Elastic Load Balancer の設定 - Health Check

ELB のヘルスチェックの設定は、デフォルトのままでも基本的に問題ないと思うが、一応、私の設定を晒しておく。

Ping Target HTTP:80/index.html
Timeout 5 sec
Interval 30 seconds
Unhealthy Threshold 2
Healthy Threshold 6

Elastic Load Balancer の設定 - パフォーマンス上の注意点

ELB に登録する EC2 Instance を、複数の AZ に分散させる場合は、下記の特徴を頭に入れておくこと。パフォーマンスリスクとなり得る。

着信トラフィックロードバランサーで有効になっているすべてのアベイラビリティゾーン間で均等に負荷分散されるので、各ゾーンのインスタンス数をほぼ同等にすることが重要です。例えば、利用可能ゾーン us-east-1a に10個のインスタンス、利用可能ゾーン us-east-1b に2個のインスタンスがある場合でも、トラフィックは2つの利用可能ゾーン間で等分に分散されます。その結果、us-east-1b 内の2個のインスタンスは、us-east-1a 内の10個のインスタンスと同じ量のトラフィックを処理しなければならなくなります。

追記:2014-03-11

現在 ELB には新機能として「Cross-Zone Load Balancing」というのが追加されています。
「Cross-Zone Load Balancing」を「Enable」にすると、
複数の AZ で処理できるパワーが偏っていたとしても、それを考慮してロードバランシングしてくれる機能だそうです。

ELBのCross-Zone Load Balancingを試してみる - okochangのインフラ日誌
http://okochang.hatenablog.jp/entry/2013/11/09/233732



Apachehttpd.conf)の設定 - HTTP → HTTPS への rewrite・リダイレクト

ELB から HTTP(80ポート)でリクエストが来るので、Apache の設定は 80 ポートの設定のみ行えば良い。VirtualHost に下記のような設定を追加する。

...
<VirtualHost *:80>
    ...

    # http -> https  rewrite
    RewriteEngine On

    RewriteCond %{HTTP:X-Forwarded-Port} !^443$
    RewriteCond %{HTTP_USER_AGENT} !^ELB-HealthChecker
    RewriteRule ^(.*)?$ https://%{HTTP_HOST}$1 [R=301,L]
    # http -> https  rewrite [end]

    ...
</VirtualHost>
...

これは、ユーザーが HTTP で接続してきた時に、HTTPS で接続し直すように促すための設定である。ELB から EC2 Instance に通信が来るときは、クライアントが HTTPS、HTTP どちらで接続していたとしても HTTP(80ポート)で来てしまうため、単純なポート番号の判定ではうまくリダイレクト出来ない。




RewriteCond %{HTTP:X-Forwarded-Port} !^443$

クライアント ←→ ELB の通信が何で行われたのか?は「X-Forwarded-Port」に格納されるため、上記のような RewriteCond が必要になる。




RewriteCond %{HTTP_USER_AGENT} !^ELB-HealthChecker

この設定がないと、なぜか ELB のヘルスチェックが失敗し続けて、EC2 Instance がいつまでも使えないという状態に陥った。その時に出会ったのは次の記事である。

ELB が HTTP(80ポート)でヘルスチェックを行うのだが、この時 Apache がリダイレクト(Status 302)を戻すと、ELB はヘルスチェックに失敗していると判断するらしい。そのため、ヘルスチェックのリクエストが来たときは、HTTP→HTTPS リダイレクトを行わないようにしなければいけない。

上記の StackExchange にいくつか対処が紹介されているが、今回は USER AGENT で判定することにした。




RewriteRule ^(.*)?$ https://%{HTTP_HOST}$1 [R=301,L]

これは HTTP→HTTPS のリダイレクト条件。


Apachehttpd.conf の設定は以上だが、Rewirte がうまくいっていないときなどはログを監視すること。
httpd.conf に Rewrite ログを残すようにするための設定は下記の通り。

RewriteLog "logs/rewrite_log"
RewriteLogLevel 1

先ほどの設定に追加するとしたら、下記のような感じか

...
<VirtualHost *:80>
    ...

    # http -> https  rewrite
    RewriteEngine On

    # debug log
    RewriteLog "logs/rewrite_log"
    RewriteLogLevel 1

    RewriteCond %{HTTP:X-Forwarded-Port} !^443$
    RewriteCond %{HTTP_USER_AGENT} !^ELB-HealthChecker
    RewriteRule ^(.*)?$ https://%{HTTP_HOST}$1 [R=301,L]
    # http -> https  rewrite [end]

    ...
</VirtualHost>
...


以上。