閉域 ALB の HTTPS (TLS) 証明書に AWS Certificate Manager のパブリック証明書を使えるか検証する

ガバメントクラウドで Web 型の標準準拠システムを構築する時、[各標準準拠システムに共通する非機能要件の標準](https://www.digital.go.jp/policies/local_governments#non-functional-requirements) に示されている通信の暗号化要件を満たすには、クライアントからアプリケーションサーバの間の通信を HTTPS (TLS) で暗号化することが考えられます。

デジタル庁の「ガバメントクラウド利用における推奨構成」を読むと、クライアントからロードバランサーまでの間を暗号化すれば、ロードバランサーから実際の Web サーバの間の通信の暗号化は「必要に応じて」とされています。

AWS の場合、Application Load Balancer (ALB) に TLS 証明書をインストールし、ALB で HTTPS 通信を終端することでクライアントから ALB までの間の暗号化要件を満たすことができそうです。

ガバメントクラウドは基本的に閉域で使うため、ALB で使う TLS 証明書の発行については、次の方法が考えられます。

1. オンプレミスの庁内に証明書発行機関（プライベート CA）があれば庁内のプライベート CA でプライベート証明書を発行する
2. AWS Certificate Manager でプライベート CA を作成し、プライベート CA でプライベート証明書を発行する
3. AWS Certificate Manager でパブリック証明書を発行する

庁内にプライベート CA があるなら第一選択肢になると思います。既存のプライベート CA がない場合、AWS Certificate Manager (ACM) でプライベート CA を立てることを検討することになるかと思います。

ところが、ACM でプライベート CA を立てると結構な料金（月額 400USD）がかかります。そこで、ACM で料金がかからないパブリック証明書を発行し、閉域のロードバランサーで HTTPS 通信させられるか検証しました。

結論としては、以下の条件を満たせばこれが可能であることが分かりました。

- パブリックなインターネットで名前解決できるドメインを保有しており自身で管理できる。
- このドメインのサブドメインを Route 53 プライベートホストゾーンで管理できて、ロードバランサーの名前解決に使うことができる。
- ロードバランサーにアクセスするクライアントのルート証明書などを最新にアップデートすることができる（自動手動は問わない）。

それでは早速検証していきます。

## 検証環境のネットワークの概要

今回の検証環境の構成図は次の通りです。

![ACM-Private-ALB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/26382247-5df8-e6a7-b07b-fa2e41c689f3.png)

閉域環境同士で ALB の HTTPS 通信を行えることを確認するため、2 つの AWS アカウントにそれぞれプライベートサブネットしか持たない VPC を作り、EC2 インスタンスを立てます。一方の EC2 には簡易な Web サーバを立てます。2 つの VPC は Transit Gateway で接続しルーティングできるようにします。

以下は IP アドレスが 10.128.137.120 の EC2 から IP アドレス 10.129.138.23 の EC2 に立っている Web サーバへ CURL で GET を投げて、Web サーバから HTTP で応答が返ってくるところを確認したものです。

![スクリーンショット 2025-02-01 175321.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/409926a5-081c-9273-5edb-6bba217c1186.png)

今回プライベート証明書を発行するドメインとして、自身の保有するドメインの中からサブドメインを設定し、Web サーバのある VPC の方のアカウントの Route 53 プライベートホストゾーンで管理することにします。

具体的には、今回の検証で私が個人で保有している morori.jp というドメインがあるので、test02.intra.morori.jp というサブドメインのパブリック証明書を ACM で発行し、test02.intra.morori.jp のゾーンを Route 53 プライベートホストゾーンで管理してみます。

このプライベートホストゾーンはもう一方の VPC にも関連付けを行い、両方の VPC からこのプライベートホストゾーンに登録した DNS レコード（test02.intra.morori.jp ドメインのレコード）を名前解決できるようにしておきます。

最終的に、Web サーバの立っている EC2 をターゲットとする ALB を置き、この ALB が ACM で発行したパブリック証明書を使って HTTPS 通信できるようにすることで、もう一方の EC2 から ALB 越しに HTTPS で Web サーバにアクセスして応答を確認できればゴールです。

Transit Gateway や Route 53 のプライベートホストゾーンなど、検証環境のネットワーク設定は過去の記事を参照。

https://qiita.com/takeda_h/items/32416c0536aac3698065

## AWS Certificate Manager (ACM) の設定

### パブリック証明書の作成

AWS マネージメントコンソールから「AWS Certificate Manager」→「証明書をリクエスト」へ進みます。

証明書タイプは「パブリック証明書」として次に進みます。

![スクリーンショット 2025-02-01 150817.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/58266bf7-d970-4465-f18a-9c6e60183b85.png)

事前に決めたサブドメインを使って ALB に割り当てたい完全修飾ドメイン名（最終的にアクセスさせたい名前）を設定し、検証方法は DNS 検証とします。

![スクリーンショット 2025-02-01 151808.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/672d1bb2-7414-9094-b617-65ef3922b840.png)

証明書をリクエストしたら証明書のドメインの CNAME 名と CNAME 値をコピーしておきます。

![スクリーンショット 2025-02-01 163857.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/695c0fec-ea26-d537-acea-548366092085.png)

次にこの証明書を発行するドメインの所有者が自身であることを AWS へ証明するため、保有するドメインの権威 DNS にこの CNAME を登録します。

### インターネットから名前解決できる権威 DNS の設定

先ほどコピーした CNAME を、保有するドメインの権威 DNS に登録します。これは Route 53 でドメインを取得している場合を除き AWS のサービスの範囲外になるので詳細は割愛します。

以下はさくらのドメインで morori.jp の権威 DNS に先の CNAME を登録したところです。

![スクリーンショット 2025-02-01 165732.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/222b8d38-107b-2d72-e367-63065fa8cfe9.png)

権威 DNS の登録が終わってしばらく待つと、ACM で発行した先ほどのパブリック証明書のステータスが「発行済み」に変わります。

![スクリーンショット 2025-02-01 165909.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/69c8aaf2-bbfd-c9f8-39b7-cd6cdaecc701.png)

パブリック証明書の発行はこれで完了なので、次に ALB を作っていきます。

## Application Load Balancer (ALB) の作成

### ターゲットグループの作成

まずは ALB からルーティングする実サービスを定義するターゲットグループを作成します。AWS マネージメントコンソールから「EC2」→「ターゲットグループ」から「ターゲットグループの作成」に進みます。

ターゲットタイプは「インスタンス」を、今回 EC2 に立っている Web サーバは TCP 80 番ポートでサービスを待ち受ける設定としたので、プロトコル : ポートには「HTTP:80」を設定します。

![スクリーンショット 2025-02-01 171858.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/f13f4657-5724-cc56-112a-1cc8674f9aae.png)

その他のオプションはデフォルトのまま次へ進みます。

![スクリーンショット 2025-02-01 172009.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/e8b2c580-edd7-4146-73ce-33e44f26ff64.png)

ターゲットに Web サーバの立っている EC2 インスタンスを選択し、ターゲットグループを作成します。

![スクリーンショット 2025-02-01 172127.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/0cbf8d4d-29da-cef5-7da4-550f7a4d2d2e.png)

![スクリーンショット 2025-02-01 172204.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/029299b3-5172-941b-42c3-d30e1cb5a272.png)

ターゲットグループが作成できたら ALB を作成していきます。

### ALB の作成

AWS マネージメントコンソールから「EC2」→「ロードバランサー」から「ロードバランサーの作成」に進みます。

ロードバランサータイプは「Application Load Balancer」を選び作成に進みます。

![スクリーンショット 2025-02-01 170057.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/28379696-6681-d34c-c529-d8e5ea9041e9.png)

スキームは「内部」とします。

![スクリーンショット 2025-02-01 171213.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/af8992d3-8064-5671-2f01-4cafc4505a5b.png)

ALB をアタッチする VPC と AZ を選びます。なお今回は検証なので片方の AZ にしか ALB のターゲットは設定しません。

![スクリーンショット 2025-02-01 171256.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/ab327ba6-a14a-640a-974c-f37c250a48d6.png)

ALB に設定するセキュリティグループを選択します。検証に必要なポートを開放したセキュリティグループを設定してください。

![スクリーンショット 2025-02-01 171415.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/ac66fc48-11af-86b9-9fbe-261449829cc5.png)

リスナーとルーティングを設定します。リスナーは「HTTPS:443」とし、デフォルトアクションに先ほど作成したターゲットグループを設定します。

![スクリーンショット 2025-02-01 172334.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/2ee6db57-8541-c88e-4be1-4d41c8a85741.png)

セキュアリスナーを設定します。証明書の取得先を「ACM から」を選択し、ACM で発行したパブリック証明書を選択します。

![スクリーンショット 2025-02-01 172517.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/37d2b838-0c05-2872-07df-c72ea71591f4.png)

設定が終わったらロードバランサーを作成します。正常に作成されたら ALB のステータスがアクティブになります。

![スクリーンショット 2025-02-01 172933.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/ad6c043a-06c4-4ce9-1c88-a202d1ff3e7b.png)

## Route 53 プライベートホストゾーンのレコード追加

Web サーバのある方の AWS アカウントの Route 53 プライベートホストゾーン（プライベート証明書を発行したドメインのゾーン）に、ALB のエイリアスレコードを登録し、ALB がパブリック証明書を発行したドメインで名前解決できるようにします。

レコード名はパブリック証明書を発行した時に設定した完全修飾ドメイン名とし、レコードタイプは「A」、「エイリアス」をオンとし、トラフィックのルーティング先は「Application Load Balancer へのアクセス」を選択して ALB を設定し、レコードを作成します。

![スクリーンショット 2025-02-01 173331.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/2139e960-b188-93d2-f9d1-f5b7a4c3101a.png)

以上で、ACM で発行したパブリック証明書で ALB が HTTPS を待ち受けられるようになり、検証で作った閉域環境内で名前解決もできるようになりました。

最後にもう一方の VPC の EC2 から疎通確認をします。

## 疎通確認

もう一方の VPC の EC2 から CURL で Route 53 プライベートホストゾーンで設定した ALB の DNS 名に GET を投げてみます。

![スクリーンショット 2025-02-01 175215.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/443658/dd8df330-6235-c557-638b-281b33d1f36f.png)

HTTPS でアクセスしてちゃんと Web サーバの応答が返ってきました！

## まとめ

閉域の内部で使う ALB でも、ACM で発行するパブリック証明書で HTTPS 通信ができることが分かりました。パブリック証明書とはいえ、DNS と証明書を適切に管理すれば閉域で使ってもリスクは少ない（はず）と思いますので、プライベート CA の代用とするのもありかと思います。

そもそもなぜパブリック証明書を検討したかというと、当初ネットワークアカウントに ACM でプライベート CA を立てて、マルチベンダのマルチアカウントで ACM を共用できないか考えたのですが、ACM と Route 53 のオペレーションがかなり煩雑になりそう（というかマルチベンダでの全体の名前解決の運用が大変そう）と思ったのと、やはり費用が高いというところから、パブリック証明書を検討した方がいいかも、となったからでした。

## 参考サイト

https://dev.classmethod.jp/articles/internal-alb-https/

https://docs.aws.amazon.com/acm/latest/userguide/dns-validation.html

デジタル庁の Web サイトにガバメントクラウド利用における推奨構成が Zip ファイルで掲載されています。

https://www.digital.go.jp/policies/local_governments#government-cloud