Amazon EC2で常時SSLを実現する際の注意点
2017-08-21EC2 で作った LAMP 環境 + Application Load Balancer で常時 SSL を実現しようとした際に、 http → https のリダイレクトがループしてしまったときの対処方法です。
前提
- SSL証明書はAWSのCertificate Managerで取得
- 取得したSSL証明書は、 ELB (Application Load Balancer) で使用
詰まった点
単純にhttp→httpsリダイレクトをすると無限ループに陥る
常時SSLにするためには、http→https のリダイレクトが必要ですが、上記の前提で実現しようとするとこのリダイレクトが無限ループとなってしまいます。
AWSのELBを使用している場合、クライアントからサーバ(EC2インスタンス)へのアクセスの間にELBが入ります。
ここで注意が必要なのが、ELBからEC2への通信はhttpだということです。
つまり、クライアントがhttpsでアクセスしていても、ELBからEC2への通信はhttpとなります。その結果、Apache(EC2)でプロトコルのチェックをしても常にhttp通信だということになります。
そのため、「httpならhttpsへリダイレクト」という条件が無限ループとなってしまうわけです。
これを回避するため、常時SSLにしたいディレクトリの.htaccessは下記のように記述します。
RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} !^ELB-HealthChecker
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP:X-Forwarded-Proto} !=https
RewriteRule ^/?(.*) https://%{HTTP_HOST}/$1 [R=301,L]
この記述では、以下の条件に当てはまる場合はhttpsでリダイレクトする という記述です。
RewriteCond %{HTTP_USER_AGENT} !^ELB-HealthCheckerユーザーエージェントがELB-HealthCheckerから始まらないRewriteCond %{HTTPS} !=on通信プロトコルがhttpsではないRewriteCond %{HTTP:X-Forwarded-Proto} !=httpsHTTPヘッダーX-Forwarded-Protoの値がhttpsではない
1については後ほど書きます。
2, 3が常時SSLに関わる部分です。
特に3つ目の条件は、AWSでELBを使用してhttps通信を実現する際の回避策としては必須となっているようです。(現状では)
HTTPヘッダーX-Forwarded-Protoは、ELBからEC2へ通信する際に付与されるヘッダー情報で、クライアントからELBへの通信がhttpだった場合のみ、httpという値になる というものです。
この値を判定して、httpsへのリダイレクトを実現します。
(2はELBを通している以上、不要?かもしれません)
ELBのヘルスチェックがエラーになる
単純にhttp→httpsのリダイレクトを実現するためには前項の2, 3のみでよいのですが、その状態で運用しているとELBのヘルスチェックがエラーとなってしまいます。
ELBのヘルスチェックとは、定期的にELBからEC2に通信を行い、返ってくるレスポンスコードによって正常かどうかを判定しています。
ヘルスチェックでは、返ってくるコードが200以外の場合はエラーとするようです。
前項で書いた1の条件が無いと、ヘルスチェックのための通信もhttp→httpsリダイレクトされ、ELBに返すコードとしては301になってしまいます。
1は、これを回避するためにアクセス元のUserAgentを見て「ヘルスチェックの際のUserAgentであるELB-HealthCheckerの場合はリダイレクトをしない」という記述になります。