CloudFront に SSL 証明書を導入する際のポイントまとめ

CloudFront に SSL を入れる際に気をつける項目のまとめです。

2017年9月時点では、特に S3 との連携で大きな制約があり、将来的には改善されていくのではと思います。現在の状況を確認してください。

(2017/10/26 追記) [Lambda@Edge](https://aws.amazon.com/jp/lambda/edge/) で S3 の制約を回避できるようになりました。[こちらの記事](https://aws.amazon.com/jp/blogs/compute/implementing-default-directory-indexes-in-amazon-s3-backed-amazon-cloudfront-origins-using-lambdaedge/)を参照してください。

# SNI か専用 IP か

まず、ブラウザと [CloudFront](https://aws.amazon.com/jp/cloudfront/) 間の通信で、[SNI (Server Name Indication)](https://ja.wikipedia.org/wiki/Server_Name_Indication) が使えるかどうか検討します。

ガラケーやWindowsXP の IE は SNI に対応していません。これらの古い環境でサイトが見えなくても構わないかどうかを確認します。

SNI 非対応のブラウザに対応するには、専用 IP を使う必要があります。専用 IP は $600/月かかります。

 * https://aws.amazon.com/jp/cloudfront/custom-ssl-domains/

> 専用 IP 独自 SSL の料金体系はシンプルです。SSL 証明書ごとの専用 IP アドレスに伴う追加コストのため、CloudFront ディストリビューションに関連付けられた各独自 SSL 証明書ごとに、毎月 600 USD をお支払いいただきます。

SNI は無料です。

> SNI 独自 SSL 証明書管理には、前払い料金や最低月額料金は不要です。お客様にお支払いいただくのは、Amazon CloudFront のデータ転送と HTTPS リクエストに対する通常料金のみです。

# 証明書の取得について

どのドメインでどんな証明書が必要か、証明書は有料か無料か、を確認します。

証明書の取得方法には、以下の選択肢があります。

1. 代理店などから購入する (有料)
2. [ACM (AWS Certificate Manager)](https://aws.amazon.com/jp/certificate-manager/) を使う (無料、ただし EC2 にはインストール不可)
3. [Let's Encrypt](https://letsencrypt.org/) を使う (無料、ただし Amazon Linux 未対応)
4. etc.

## CloudFront - オリジン間を HTTPS にする

通常は、ビューア - CloudFront 間だけでなく、 CloudFront - オリジン間も通信を暗号化します。(表向き暗号化している風を装って、裏を平文で流して良いのかは微妙な問題です…)

CloudFront とオリジンの両方で、証明書が必要になる可能性があります。

## オリジンが EC2 の場合、 ELB を入れるか検討する

ACM で取得した証明書を EC2 にインストールすることはできません。

https://aws.amazon.com/jp/certificate-manager/faqs/

> Q: ACM により提供された証明書を使用できるのは、AWS のどのサービスですか?
>
> ACM は AWS の以下のサービスと連携できます。
> • Elastic Load Balancing – Elastic Load Balancing のドキュメントを参照してください。
> • Amazon CloudFront – CloudFront のドキュメントを参照してください。
> • Amazon API Gateway – API Gateway のドキュメントを参照してください。
> • AWS Elastic Beanstalk – AWS Elastic Beanstalk のドキュメントを参照してください。

https://aws.amazon.com/jp/certificate-manager/faqs/

> Q: Amazon EC2 インスタンスや自分のサーバーに証明書を使用できますか?
> 
> いいえ。現在のところ、ACM により提供される証明書は、AWS の特定のサービスのみで使用できます。

EC2 で ACM を使いたい場合は、CloudFront - ELB - EC2 の構成にします。証明書が無料になる代わりに、ELB の費用が発生します。

## S3 に証明書を入れる必要はない

 * http://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/using-https-cloudfront-to-s3-origin.html

> * Amazon S3 は SSL/TLS 証明書を提供するため、その必要はありません。

この日本語訳はいまいち何を言っているのかわかりませんが、、

> * Amazon S3 provides the SSL/TLS certificate, so you don't have to.

「S3 が証明書を提供するので、あなたが(提供する)必要はない」という意味です。

今のところ、S3 に独自の証明書を入れる手段はありません。オリジンに S3 の CNAME を指定することもできません。

http://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/DownloadDistS3AndCustomOrigins.html

> 以下の形式を使用してバケットを指定しないでください。
>
> * Amazon S3 のパススタイル (s3.amazonaws.com/bucket-name)
> * Amazon S3 の CNAME (ある場合)


## CloudFront - S3 間の HTTPS には大きな制約がある

S3 をウェブサイトエンドポイントとしてオリジンに設定すると、HTTPS が使えなくなります。

 * http://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/using-https-cloudfront-to-s3-origin.html 

> Amazon S3 バケットがウェブサイトエンドポイントとして構成されている場合、オリジンとの通信に HTTPS を使用するように CloudFront を構成することはできません。Amazon S3 はその構成で HTTPS 接続をサポートしていないためです。 

[ウェブサイトエンドポイント](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/WebsiteEndpoints.html)は、

```
http://example-bucket.s3-website-ap-northeast-1.amazonaws.com/
```

のような URL での配信方法です。「静的ウェブサイトホスティング」の設定です。この URL をオリジンに設定すると、CloudFront - S3 間で HTTPS が使えません。

ウェブサイトエンドポイントは、以下の機能で必要です。

 * `/foo/bar/` で `/foo/bar/index.html` を返す
 * 独自のエラー画面を表示する
 * リクエストを全てリダイレクトする (例えば、www なしドメインを www ありにリダイレクトする)

ウェブサイトエンドポイントを使わず、S3オリジンで設定すると、サイトのトップ `/` (Default Root Object) 以外、`index.html` が見えるようにはなりません。

 * https://qiita.com/naoiwata/items/3c6626cbeacbb44d4aa8
 * http://dev.classmethod.jp/cloud/aws/difference-between-cf-origin/

> CloudFrontではDefault Root Objectという設定項目でIndex Documentを指定することができますが、これはRootへのアクセス時(例：http://example.com/) に対してのみ有効であり、サブディレクトリ(例：http://example.com/foo/) に対しては有効になりません。そのため、階層構造のあるWEBサイトをホストする際には不向きだと思われます。 


ウェブサイトエンドポイントを使う場合は、裏が暗号化されていなくてもいいのか、バレなければいいのか、を検討しなければならなくなります。。ちなみに、オリジンが S3 であることは `Server` レスポンスヘッダで判別できます。S3 がウェブサイトエンドポイントかどうかは、`/foo/bar/` の挙動で判別できます。

また、ウェブサイトエンドポイントにすると、[Origin Access Identity](http://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html) を使うことができなくなります。バケット名を推測して、S3 に直接アクセスされる可能性があります。



## ワイルドカード証明書の取得を検討する

CloudFront にもオリジンにも、ワイルドカード証明書が使えます。例えば、以下のような組み合わせが考えられます。

 * CloudFront `*.example.com` → Origin `*.example.com`
   * 1つのワイルドカード証明書を使う
 * CloudFront `www.example.com` → Origin `www.example.com`
   * 1つの非ワイルドカードの証明書を使う 
   * **CloudFront から Host ヘッダを転送する場合のみ**、オリジンの証明書で Host ヘッダのドメイン名が使える
 * CloudFront `www.example.com` → Origin `*.example.com`
   * 表は高い非ワイルドカード証明書を使い、裏は安いワイルドカード証明書で済ませる
 * CloudFront `www.example.com` → Origin `origin.example.com`
   * 表と裏と、別々に非ワイルドカード証明書を購入する
   * または、証明書に別名 (SAN) を設定する
 * CloudFront `www.example.com` → Origin `origin.example.jp`
   * オリジンは `.example.com` でなくてもよい。DNS の自由が効かない場合、裏は Route53 で独自のドメインを取得すると便利

詳細は以下のドキュメントを参照してください。

 * http://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/cnames-and-https-requirements.html

> カスタムオリジンを使用する場合、オリジンの SSL/TLS 証明書の共通名フィールドにドメイン名が含まれ、さらにサブジェクト代替名フィールドにもドメイン名がいくつか含まれることがあります。 (CloudFront は証明書ドメイン名にワイルドカード文字を使用できます)。
> 
> 証明書のドメイン名のうち 1 つは、オリジンドメイン名に指定したドメイン名と一致する必要があります。ドメイン名が一致しない場合、CloudFront は、ビューワーに HTTP ステータスコード 502 (Bad Gateway) を返します。 

## 内部の証明書を安くあげる

エンドユーザの目に触れないオリジン用の証明書にまでコストをかける必要があるかどうかを検討します。安価な証明書でも十分かもしれません。

オリジンが ELB なら ACM が使えます。

## DNS を操作する権限があるか確認する

表側の DNS を操作する権限がない場合、オリジンに Route53 で取得した独自ドメインを設定すると、自由度が上がります。

オリジンに独自のドメインを使う場合は、その証明書が必要です。

## リリース前の確認方法を検討する

リリース前に CloudFront に別のドメインを割り当てて確認する場合は、ワイルドカード証明書があったほうが便利です。

例えば https://staging.example.com/ のような URL を CloudFront に設定します。

## Naked ドメインを使うか確認する

https://example.com/ のような Naked ドメイン (Zone Apex) を使うかどうか確認します。

Route53 を使わずに、Naked ドメインを CloudFront や ELB や S3 に割り当てることはできません。CloudFront を通さず、例えば EC2 で直接リクエストを受ける必要が出てきます。

### Naked ドメインへのリクエストを EC2 で受ける場合

Naked ドメインへのリクエストを EC2 で直接受ける場合は、ACM が使えません。ACM で取得した証明書は EC2 にインストールできないからです。

証明書の購入が必要になる可能性があります。

### Naked ドメインの DNS に Route53 を使っている場合

Route53 でエイリアスを使うと、CloudFront に Naked ドメイン (Zone Apex) を設定できます。

この場合は、Naked ドメインの証明書取得に ACM が使えます。

ワイルドカード証明書を使う場合、ワイルドカードに Naked ドメインは含まれないので、`*.example.com` に別名で `example.com` を設定します。


## 既存の証明書がある場合は SAN を確認する

 既に証明書があって、足りない証明書を購入する場合。

既存の証明書の別名 (SAN) が何になっているのかを確認します。特に Naked ドメインなど、必要なドメインが SAN に設定されているかもしれません。


# 証明書をインポートするリージョンに注意する

CloudFront の証明書は、**バージニア北部リージョン**に入れる必要があります。

ACM で証明書を取得する場合にも、バージニア北部リージョンを選ぶ必要があります。

 * http://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/cnames-and-https-requirements.html

> ビューワーと CloudFront との間で HTTPS を必須にするには、証明書をリクエストまたはインポートする前に AWS リージョンを 米国東部（バージニア北部） に変更する必要があります。
>
> CloudFront とオリジンとの間で HTTPS を必須にする場合、オリジンとして ELB ロードバランサーを使用しているなら、任意のリージョンで証明書をリクエストまたはインポートできます。

例えば、オリジンが東京の ELB だとしたら、東京とバージニア北部リージョンの両方の ACM で証明書を取得 (またはインポート) します。

# ACM の本人確認について

ACM で証明書を取得する場合は、本人確認 (取得意思の確認) メールが、証明書のドメイン宛てに送信されます。

 * http://docs.aws.amazon.com/ja_jp/acm/latest/userguide/setup-email.html

## Whois 情報の更新状態をチェックする

本人確認メールは、公開されている Whois の連絡先アドレスにも送信されます。

Whois に正しいアドレスが公開されている場合は、そのアドレスでメールを受け取るのが簡単です。

## SES で ACM の認証を通す

ドキュメントの通りにやれば、ACM の確認メールを SES で受信するのは、さほど難しくありません。

 * http://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/receiving-email.html
 * https://dev.classmethod.jp/cloud/aws/acm-verifydomain-ses/

SES の受信設定を行い、DNS に MX レコードを設定、受信したメールを SNS トピックで受け取るようにして、そのトピックに任意のメールアドレスをサブスクライブします。

### ルールセットがアクティブになっているかどうかに注意する

SES のメール受信設定には、「ルールセット」というバージョン管理の仕組みがあります。

現行のルールセットをコピーし、編集し、アクティブなルールセットを入れ替える、という手順で設定を変更します。**Inactive Rule Sets に新しいルールセットを足しても何も起こりません。**