CloudFront - Origin間でHTTP2通信ができなかった話

### はじめに
[Japan AWS Jr. Champions Advent Calendar](https://qiita.com/advent-calendar/2023/jr-champions) 25日目の記事です

以前CloudFrontを使用してALBとの通信を行ったとき、CloudFront - Origin間でHTTP/2プロトコルを使用した通信がうまくいかなかったので、通信失敗の原因・解決策についてご紹介します。

###　構成
ターゲットグループにECSサービスを登録したALBをカスタムオリジンとして登録し、CloudFront - ALB間で通信を行っていました。
その際、Nginxを使用したコンテナをフロントエンドとして動かしており、HTTP2を使用して通信を行うように設定していました。
下の構成図より、CloudFront - ALB - ECS 間の通信は全てHTTP/2プロトコルを使用していると思っていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620290/d9b868a8-e923-2d02-83d4-12fc4ec7238f.png)

###　発生した問題
実際に環境構築を行い、CloudFrontに対してアクセスしたところ、464エラーが返ってきました。
エラー発生時のログを確認したところ、どうやらCloudFront - ALB間でエラーが発生していました。ALBのアクセスログ（一部抜粋）を以下に記載します。
```
https xxxx-xx-xxTxx:xx:xx.xxxxxxZ xxx/test-alb/xxxxxxxxxxxx x.x.x.x:x 464 - x x "GET https://index.html/ HTTP/1.1"
```

### 原因
464エラーが発生した原因については、[AWSの公式ドキュメント](https://aws.amazon.com/jp/cloudfront/faqs/#HTTP_and_HTTP.2F2:~:text=Amazon%20CloudFront%20%E3%81%A7%E3%81%AF%E3%80%81%E7%8F%BE%E5%9C%A8%20HTTP/2%20%E3%81%8C%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88%E3%81%95%E3%82%8C%E3%81%A6%E3%81%8A%E3%82%8A%E3%80%81%E3%83%93%E3%83%A5%E3%83%BC%E3%82%A2%E3%81%AE%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E3%81%8A%E3%82%88%E3%81%B3%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%81%AB%E3%82%B3%E3%83%B3%E3%83%86%E3%83%B3%E3%83%84%E3%82%92%E9%85%8D%E4%BF%A1%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82%E3%82%A8%E3%83%83%E3%82%B8%E3%83%AD%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A8%E3%82%AA%E3%83%AA%E3%82%B8%E3%83%B3%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E9%96%93%E3%81%AE%E9%80%9A%E4%BF%A1%E3%81%AB%E9%96%A2%E3%81%97%E3%81%A6%E3%81%AF%E3%80%81Amazon%20CloudFront%20%E3%81%A7%E3%81%AF%E5%BC%95%E3%81%8D%E7%B6%9A%E3%81%8D%20HTTP/1.1%20%E3%81%8C%E4%BD%BF%E7%94%A8%E3%81%95%E3%82%8C%E3%81%BE%E3%81%99%E3%80%82)に記載がありました。
>Amazon CloudFront では、現在 HTTP/2 がサポートされており、ビューアのクライアントおよびブラウザにコンテンツを配信できます。エッジロケーションとオリジンサーバー間の通信に関しては、Amazon CloudFront では引き続き HTTP/1.1 が使用されます。

つまり、 CloudFront - ALBでHTTP/2プロトコルを使用しているという認識がそもそもの間違いでした。
実際には、CloudFront - ALB間ではHTTP/1.1プロトコルを使用して通信を行っているおり、ECS側のNginxではHTTP2でリッスンするように設定していたため、プロトコルバージョンの互換性によるエラーが発生しまったというのが事の顛末です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620290/2708f1b2-c1c6-9b39-a9cb-6af4fb76f9ce.png)

### 解決策
CloudFront - Origin間の通信でHTTP/1.1プロトコルを使用することはAWS側の仕様のため、ECS側のNginxでHTTP/1.1プロトコルを使用するように修正しました。これを機に、何かしらのエラーが発生したらまずは公式ドキュメントを参照しようと心に誓いました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620290/2d0bd3be-c75e-21f1-246e-cf0497e7d3c6.png)

### HTTP/2プロトコルを使用して通信したい場合
結論から言うと、CloudFrontを使用せずにELBに直接アクセスを行う必要があります。
上記にある通り、CloudFrontを使用するとCloudFront - Origin間の通信がHTTP/1.1で固定されてしまうので、必然的にCloudFrontを使用してはいけないことになります。以下のようにクライアントからのアクセスを直接ALBに飛ばすように設定することになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620290/eb6c77b8-6e59-f287-b367-9d5a3a304fdd.png)
ただし、こちらの構成を採用する場合は以下のようなデメリットがあります。

 - ALBにAWS WAFをアタッチしたとしても、全てのアクセスがVPCに入った後にWAFアクセス許可・拒否を行うことになる。この場合、悪意のアクセスがVPCを通過することになるため、セキュリティ面ではCloudFrontを採用した場合に比べて劣る
 - S3バケットを使用した静的サイトの配信ができなくなる

基本的にはメリットよりもデメリットの方が多いので、どうしてもHTTP/2プロトコルを使用して通信を行いたい場合に上記の構成を採用することをお勧めします。

### まとめ
CloudFront - Origin間で使用するHTTPプロトコルについて紹介しました。
当時はAWSの公式ドキュメントの参照を後回しにして、ALBやECSのアクセスログばかりを分析していたため、原因究明と解決までにかなり時間がかかってしまいました。
自分への戒めとして、エラーが発生したらまずは公式ドキュメントを参考にしながらトラブルシューティングを行っていきまいと思いました。
また、今後のアップデートでCloudFront - Origin間でのHTTP2通信が可能になるかもしれないので、アップデート情報を日々確認していこうと思います。

### Tips
AWS WAFの話をちらっとしましたが、CloudFront - ALB - ECS間の通信をよりセキュアにするための構成をご紹介します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620290/2e961764-b747-74c5-0033-6beccc223afd.png)

大まかなポイントは以下です。

 - CloudFrontにWAFとACMをアタッチする
     - ACMを使用したTLS/SSL通信、WAFによる不正アクセスの防御を行う。
 - ALBのセキュリティグループに、CloudFrontからの443アクセスを許可する
     - CloudFrontが使用するIPアドレスをまとめたリスト（マネージドプレフィックスリスト）からのアクセスのみ許可することで、CloudFrontを経由しないアクセスを防御することができる。[参考](https://aws.amazon.com/jp/blogs/news/limit-access-to-your-origins-using-the-aws-managed-prefix-list-for-amazon-cloudfront/)
     - 指定したCloudFrontからのアクセスのみ許可したい場合は、カスタムヘッダーをCloudFrontに設定し、ALBのリスナールールで、カスタムヘッダーの値によってアクセス制御を行うことで可能。
     - ALB - ECS間の通信についてはVPC内の通信のため、HTTPプロトコルでの通信を許可する。仮に、同一VPCに複数のシステムが共存している場合は、HTTPS通信を使用することを推奨する。
     - 使用しないポートは基本的には解放しない。

上記の図は比較的オーソドックスなセキュリティ対策のため、InspectorやGuardDutyを導入するなどして、システムをより堅牢にしてみてください。