AWS Certificate Manager(ACM)でLambdaからELBへの接続をHTTPS化する

アプリケーションのアーキテクチャ改修前の調査を行っていた際、Lambdaから別VPCのELBへの接続がHTTPであるというセキュリティリスクを発見し、すぐに改修を行うことにしました。

これまでHTTPS化の作業経験がなかったので、今回の調査と実装内容を備忘録としてまとめました。
内容に不備がありましたらコメント欄で指摘していただけるとうれしいです。

# LambdaとELB間のHTTPS接続の流れ
実装の前に、LambdaからELBにHTTPS接続を行うときの流れを整理します。

1. 接続の開始: Lambda関数がELBのHTTPSエンドポイントに接続を開始する。
2. 証明書の提示: ELBが証明書(公開鍵と共に)をLambda関数に提示する。証明書はACMや他の証明書マネージャーで発行し、ELBに設定する。
3. 証明書の検証: Lambda関数(正確にはLambda関数のコード内の`axios`などのHTTPクライアントライブラリ)が証明書を検証する。証明書が信頼できる認証局(CA)によって発行されていること、まだ有効であること、その証明書が実際に接続を試みているサーバ(ELB)のものであることを確認する。
4. 暗号化キーの作成と共有: 一旦証明書が検証された後、Lambda関数がセッションキー(共有秘密鍵)を生成する。セッションキーはELBの公開鍵で暗号化され、ELBに送信される。ELBは自身の秘密鍵で、暗号化されたセッションキーを復号する。
5. 安全な通信の開始: ELBとLambda関数の間で共有されたセッションキーを使って、両者間の全ての通信が暗号化される。これにより、第三者がこの通信を傍受しても、秘密鍵なしでは情報を読み取ることはできなくなる。

# 実装
前項のHTTPS接続の流れを理解した上で、以下が必要な実装となります。

1. SSL/TLS証明書の発行
2. ELBで新しいリスナーの作成

基本的にはHTTP接続をHTTPS接続に更新する作業となるため、Route53, AWS Certificate Manager(ACM), ELBの設定を行います。

## 1. SSL/TLS証明書の発行
ELBにアタッチするSSL/TLS証明書を、AWS Certificate Manager(ACM)で発行します。

### ELB用のカスタムドメイン作成
ACMで証明書を発行する際、ELBのドメインを紐付ける必要があるのですが、`.elb.amazonaws.com`のようにAWSがすでに所有しているドメインに対しては証明書を発行することができないため、カスタムドメイン(例: `mywebsite.com`)を新しく作成する必要があります。

今回は既存のドメイン(例： `mywebsite.com`)があるため、Route53のホストゾーンに新しいレコード(例: `service.mywebsite.com`)を作成し、ELBのURLにマッピングします。

1. Route 53ダッシュボードで、「DNS management」をクリックする。
2. 「Hosted zones」をクリックする。
3. 該当のホストゾーン(例: `mywebsite.com`)の行をクリックする。
4. 右側の画面で「Create Record」をクリックする。
5. 新しいレコードの設定画面で以下を行う：
a. 「Record name」には、設定したいサブドメインを入力する(例: `service`)。
b. 「Record type」は A - IPv4 address を選択する。
c. 「Value/Route traffic to」は Alias to Elastic Load Balancer を選択する。
d. 「Choose Region」でELBのあるリージョンを選択する(例: `ap-northeast-1`)。
e. 「Choose ELB」で対象のELBを選択する(例: alb-12345678)。
f. 「Create records」をクリックする。

![スクリーンショット 2023-07-01 16.40.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/662609f6-ed96-ee6a-29fb-cb0024d9e62a.png)

### 証明書のリクエスト
作成したカスタムドメインを元に、ACMから証明書をリクエストします。
以下のように設定し、「Request」をクリックします。
![スクリーンショット 2023-07-01 16.47.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/e2a0fcf9-39d1-d550-6c07-deab4b4e937c.png)

### DNS検証
前項でDNS検証を選択した場合、ドメインの所有権を確認するために、ACMから提供されるCNAMEレコードをRoute53に追加する必要があります。

証明書をリクエストした後、詳細ページを開いてCNAME名とCNAME値をコピーします。
![スクリーンショット 2023-07-01 17.13.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/2e141c70-35ec-b89e-698b-649bed62be91.png)

Route53から再度レコードを作成します。
レコードタイプにはCNAMEを選択し、レコード名にはCNAME名、値にはCNAME値を入力します。
![スクリーンショット 2023-07-01 17.18.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/387b5d8b-4c2e-3446-3f1a-1a2beaeeba56.png)

これでRoute53がACMから提供されたCNAMEレコードを使用してドメインの所有権確認を行う設定が完了します。
時間を置いてから、ACMの管理コンソールに戻り、証明書のステータスが[発行済み]になっていることを確認します。

## 2. ELBで新しいリスナーの作成
発行した証明書をELBのリスナー設定からELBに登録します。

ELBの設定画面に移動し、対象のLoad Balancerを選択した後、「Listeners」タブに移動します。ここで「Edit」をクリックし、「Add listener」を選択してHTTPSのリスナーを追加します。

Target Groupには既存のものを選択しています。
HTTPと表示されているのは、ELBからバックエンドサービスへの接続がHTTPとなっているためです。
こちらもHTTPS化すべきなのですが、説明のため今回はこのままにしています（ELBがSSL終端となります)。

また、Security PolicyではLambdaとELB間の通信で使用するSSL/TLSプロトコルと暗号を定義します。
今回は`(recommended)`となっているものを選択しています。

Default SSL/TLS certificateの部分で、今回発行した証明書を選択します。
![スクリーンショット 2023-07-01 17.28.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/e8856601-e351-d871-a405-85daa31783b2.png)

これでリスナーが作成されれば、ELBへの接続におけるHTTPS化は完了です。