CloudFront を利用して AWS のパブリック IPv4 アドレス課金を回避する…のは今のところ無理だった

:::note alert
2024年4月時点では、この方法でパブリック IPv4 アドレス課金を**回避することはできない。**
ただし、将来この方法が使えるようになる可能性はある。
:::

:::note warn
記事中の情報は執筆時点のものである。
重要な決定や操作を行う前は、最新情報を確認することを推奨する。
:::

2024年2月、AWS におけるパブリック IPv4 アドレスが無条件で有料化された。

[新着情報 – パブリック IPv4 アドレスの利用に対する新しい料金体系を発表 / Amazon VPC IP Address Manager が Public IP Insights の提供を開始 | Amazon Web Services ブログ](https://aws.amazon.com/jp/blogs/news/new-aws-public-ipv4-address-charge-public-ip-insights/)

そこで、EC2 でインスタンスにパブリック IPv4 アドレスを割り当てない状態でのWebサーバーの公開を試みた。

以下、AWS のサービスの操作時の設定において、デフォルトから変更する設定は強調して示す。

# EC2でWebサーバーを用意する

EC2を用い、アクセス先となるWebサーバーを用意する。
このとき、パブリック IPv4 アドレスを割り当てないことで、パブリック IPv4 アドレスを割り当てたことによる課金を回避することを狙う。

## ネットワーク (VPC) を作成する

以下の手順で、EC2インスタンスを入れるネットワークを作成する。
今回は、インターネットに接続できるネットワークが1組という最小構成にする。

今回使用する VPC・サブネット・インターネットゲートウェイは無料で使用できる。
[料金 - Amazon VPC | AWS](https://aws.amazon.com/jp/vpc/pricing/)
[VPCにいるだけで利用料金がかかってしまうリソース - fu3ak1's tech days](https://fu3ak1.hatenablog.com/entry/2020/07/02/233639)

1. 左上の検索などから「VPC」サービスを開く
2. リージョンを選択する
3. 「VPCを作成」を押す
4. 設定を行う
   * 作成するリソース：VPCなど
   * **名前タグの自動生成：「自動生成」にチェックを入れ、適当な名前を入力**
   * IPv4 CIDR ブロック：`10.0.0.0/16`
   * **IPv6 CIDR ブロック：Amazon 提供の IPv6 CIDR ブロック**
   * テナンシー：デフォルト
   * **アベイラビリティゾーン (AZ) の数：1**
   * パブリックサブネットの数：1
   * **プライベートサブネットの数：0**
   * NAT ゲートウェイ ($)：なし
   * Egress Only インターネットゲートウェイ：いいえ
   * **VPC エンドポイント：なし**
   * DNS ホスト名を有効化：チェックを入れる
   * DNS 解決を有効化：チェックを入れる
5. 「VPCを作成」を押す

:::note info
今回VPCを作成する目的は、IPv6 を利用することである。
IPv6 CIDR ブロックを忘れずに有効にすること。
:::

これにより、以下のリソースが作成され、初期設定が行われる。

* VPC
* サブネット
* インターネットゲートウェイ
* ルートテーブル

## EC2インスタンスを作成する

作成したVPC (ネットワーク) 内に、EC2インスタンスを作成する。
このとき、IPv6 アドレスを用意し、パブリック IPv4 アドレスが自動で割り当てられないようにする。

なお、EC2インスタンスを起動している間は、インスタンスの種類に応じた料金がかかる。
また、ストレージ (EBS) 代もかかる。
[Amazon EC2 の料金 | AWS 公式](https://aws.amazon.com/jp/ec2/pricing/)
[ハイパフォーマンスブロックストレージの料金 – Amazon EBS の料金 – Amazon Web Services](https://aws.amazon.com/jp/ebs/pricing/)

1. 左上の検索などから「EC2」サービスを開く
2. 「インスタンスを起動」を押す
3. ネットワーク以外について、通常の設定を行う
   * 名前
   * AMI
   * インスタンスタイプ
   * キーペア
   * その他 (スポットインスタンスなど)
4. ネットワーク設定を行う (「編集」を押して編集モードに入る)
   * **VPC：前の章で作成したVPC**
   * サブネット：前の章で作成したサブネット
   * パブリック IP の自動割り当て：無効化
   * **IPv6 IP を自動で割り当てる：有効化**
   * **インバウンドセキュリティグループのルール**
     * ssh を許可する (デフォルト)
     * 「セキュリティグループルールを追加」を押し、追加されたルールを設定する
       * タイプ：HTTP
       * ソースタイプ：任意の場所
5. 「インスタンスを起動」を押す

:::note info
「IPv6 IP を自動で割り当てる」を忘れずに有効にすること。
:::

## EC2インスタンス接続用のIPアドレスを割り当てる

前の章でEC2インスタンスを起動したが、パブリック IPv4 アドレスを割り当てていないため、そのままではインスタンスに接続しての作業がしにくい。
そのため、作業を行う間、一時的に IPv4 アドレスを割り当てて接続できるようにする。

:::note warn
Elastic IP アドレスを割り当てている間、インスタンスに関連付けていなくても、料金が発生する。
作業後、課金を回避するためには、関連付けを解除するだけでなく割り当ての解除 (解放) を行うこと。
:::

1. EC2の左側のメニューの「ネットワーク & セキュリティ」にある「Elastic IP」を開く
2. 「Elastic IP アドレスを割り当てる」を押す
3. デフォルトの設定で「割り当て」を押す
4. 割り当てられたIPアドレスを右クリックし、「Elastic IP アドレスの関連付け」を押す
5. 設定を行う
   * リソースタイプ：インスタンス
   * **インスタンス：前の章で立てたEC2インスタンス**
   * プライベート IP アドレス：空欄
   * この Elastic IP アドレスの再関連付けを許可する ：オフ

## EC2インスタンスにWebサーバーをインストールする

作成したEC2インスタンスで、アクセス先となるWebサーバーを立てる。
ここでは、例としてUbuntu 22.04 (AMI ID: ami-080e1f13689e07408) で nginx を起動する手順を紹介する。

前の章で割り当てたIPアドレスを用い、作成したEC2インスタンスに (Tera Term などで) SSH ログインする。
ユーザー名は `ubuntu`、秘密鍵はインスタンスの作成時に設定したキーペアに対応するものである。

:::note info
今回の接続先は一時的に割り当てたIPアドレスなので、known hosts には追加しない、(自動で) 追加されてしまった場合は削除しておくのがいいだろう。
:::

以下のコマンドを実行し、ソフトウェアを最新化する。

```
sudo apt-get update
sudo apt-get upgrade -y
```

:::note info
`-y` オプションにより、確認を省略してインストールを行う。
:::

再起動するサービスの選択を求められたら、全部を選択する。

Webサーバーをインストールする。

```
sudo apt-get install nginx -y
```

Webブラウザで `http://(割り当てたIPアドレス)/` (例：`http://203.0.113.178/`) にアクセスすると、「Welcome to nginx!」のページが表示されるはずである。

## EC2インスタンス接続用のIPアドレスの割り当てを解除する

作業が終わったら、接続用に割り当てたIPアドレスを解放し、課金されるのを止める。

:::note warn
関連付けを解除するだけでなく、忘れずに解放を行うこと。
:::

1. EC2の左側のメニューの「ネットワーク & セキュリティ」にある「Elastic IP」を開く
2. 割り当てられたIPアドレスを右クリックし、「Elastic IP アドレスの関連付けの解除」を押す
3. 「関連付け解除」を押す
4. 割り当てられたIPアドレスを右クリックし、「Elastic IP アドレスの解放」を押す
5. 「解放」を押す

# CloudFront でWebサーバーにアクセスできるように…したかった

Webサーバーを起動したが、これだけではパブリック IPv4 アドレスを割り当てていないため、アクセスが難しい。
そこで、普通の環境からアクセスできるようにしたい。

## ドメインにEC2インスタンスの IPv6 アドレスを登録する

ドメインの設定を行い、EC2インスタンスの情報と関連付ける。
この方法は、ドメインをどこで管理しているかによる。
筆者が使っている[ムームードメイン](https://muumuu-domain.com/)の場合、以下のようにして設定を行う。

1. ログインする (コントロールパネルを開く)
2. 「ドメイン管理」→「ドメイン操作」→「ムームーDNS」を開く
3. 使用するドメインの「変更」ボタンを押す
4. 以下のレコード値を追加し、「セットアップ情報変更」ボタンを押す

|項目|内容|
|---|---|
|サブドメイン|適当に設定(UUIDなど)<br>例：`42304df9-0526-44de-9ef4-b37838d02237`|
|種別|AAAA|
|内容|作成したEC2インスタンスの「IPv6 アドレス」<br>例：`2001:0db8:8268:dc31:7a64:86ff:1a67:1567`|

## Webサーバーが使えることを確認する

[Bulk URL HTTP Status Code, Header & Redirect Checker | httpstatus.io](https://httpstatus.io/)
を用いて、立てたWebサーバーが使えることを確認する。
設定したドメインを用いたURL (例：`http://42304df9-0526-44de-9ef4-b37838d02237.example.com/`) のチェックを行い、200 (など期待するステータスコード) が返ってくることを確認する。

:::note info
以下のサービスでは、上記サービスではチェックに成功する状態でもエラーが発生してしまい、今回のチェックには役立たなそうだった。

* [HTTP状況とリダイレクトチェッカー - Kinsta®](https://kinsta.com/jp/tools/redirect-checker/)
* [HTTP Request Online | Tools | Base64](https://base64.guru/tools/http-request-online)
* [HTTPステータスコードチェッカー – SEOStudio Tools](https://seostudio.tools/ja/http-status-code-checker)
* [WEBサイト接続チェック（ラッコの点検屋） | ラッコドメイン](https://rakkodomain.com/tool/websiteConnectionCheck)
* [HTTPチェック【外部からの疎通確認】](http://www.cman.jp/network/support/http.html)

:::

## CloudFront 用の証明書を用意する

CloudFront に独自ドメインでアクセスできるようにするには、証明書が必要である。
ここではその証明書を作成する。

ここでの証明書の作成、および作成した証明書は無料である。
[料金 - AWS Certificate Manager | AWS](https://aws.amazon.com/jp/certificate-manager/pricing/)

:::note warn
CloudFront 用の証明書は、**米国東部 (バージニア北部) リージョン**で作成しなければならない。
リージョンを間違えないようにすること。
[で SSL/TLS 証明書を使用するための要件 CloudFront - Amazon CloudFront](https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/cnames-and-https-requirements.html)
:::

1. 左上の検索などから「AWS Certificate Manager」サービスを開く
   * 検索結果には「Certificate Manager」と表示される
   * 「acm」で検索すればよい
2. **リージョンを「バージニア北部」(us-east-1) にする**
3. 「証明書をリクエスト」を押す
4. 「証明書タイプ」を「パブリック証明書をリクエスト」(デフォルト) にして、「次へ」を押す
5. 設定を行う
   * **完全修飾ドメイン名：管理できるドメインを適当に設定**
     例：`noipv4charge.example.com`
   * 検証方法：DNS 検証 - 推奨
   * キーアルゴリズム：RSA 2048
   * タグ：なし
6. 「リクエスト」を押す
7. 左側の「証明書を一覧」を開き、作成した証明書を開く
8. ドメインに検証用の設定を行う

筆者が使っている[ムームードメイン](https://muumuu-domain.com/)の場合、「ドメインにEC2インスタンスの IPv6 アドレスを登録する」での手順と同様に、以下のレコード値を追加する。

|項目|内容|
|---|---|
|サブドメイン|「CNAME 名」に表示されている文字列のうち、<br>取得したドメイン名とその前後のピリオドを除く部分|
|種別|CNAME|
|内容|「CNAME 値」に表示されている文字列のうち、<br>末尾のピリオドを除く部分|

たとえば、「CNAME 名」が `_12c29965cbf24962bb12df7817fe035a.noipv4charge.example.com.` の場合、サブドメインは `_12c29965cbf24962bb12df7817fe035a.noipv4charge` を設定する。

:::note warn
「内容」の設定時、末尾のピリオドをつけたままだとエラーになって設定ができなかったので、注意すること。
:::

設定後数分程度待機し、ステータスが「発行済み」になれば完了である。

## CloudFront ディストリビューションを作成する

CloudFront で「ディストリビューション」を作成する。
これにより、一般の端末からのリクエストの内容に沿って CloudFront がコンテンツがあるWebサーバー (「オリジン」という) にアクセスし、返ってきたコンテンツを端末に返すようにする。
今回は、リクエストの内容をそのままオリジンに送信し、キャッシュなどの余計なことはしないようにする。

CloudFront は、(アカウント作成から12ヶ月経過後でも) 毎月 1TB・10,000,000 リクエストまでは無料だが、それを超える場合は有料である。
[料金 - Amazon CloudFront | AWS](https://aws.amazon.com/jp/cloudfront/pricing/)
[AWS 無料利用枠のデータ転送量の拡大 — リージョンから 100 GB、Amazon CloudFront から 1 TB / 月 | Amazon Web Services ブログ](https://aws.amazon.com/jp/blogs/news/aws-free-tier-data-transfer-expansion-100-gb-from-regions-and-1-tb-from-amazon-cloudfront-per-month/)

1. 左上の検索などから「CloudFront」サービスを開く
2. 「CloudFront ディストリビューションを作成」を押す
3. 設定を行う
   * オリジン
     * **Origin domain：前の章で設定したサブドメインとドメイン**
       (例：`42304df9-0526-44de-9ef4-b37838d02237.example.com`)
     * **プロトコル：HTTP のみ**
     * HTTP port：80
     * Origin path - optional：空欄
     * 名前：適当に設定
     * カスタムヘッダーを追加 - オプション：なし
     * Enable Origin Shield：いいえ
   * デフォルトのキャッシュビヘイビア
     * パスパターン：デフォルト (\*)
     * オブジェクトを自動的に圧縮：Yes
     * **ビューワープロトコルポリシー：Redirect HTTP to HTTPS**
     * **許可された HTTP メソッド：GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE**
     * HTTP メソッドをキャッシュ オプション：オフ
     * キャッシュキーとオリジンリクエスト
       * Cache policy and origin request policy (recommended)
       * **キャッシュポリシー：CachingDisabled**
       * **オリジンリクエストポリシー - オプション：AllViewer**
     * レスポンスヘッダーポリシー - オプション：空欄
   * 関数の関連付け - オプション：すべて「関連付けなし」
   * **ウェブアプリケーションファイアウォール (WAF)：セキュリティ保護を有効にしないでください**
   * 設定
     * **料金クラス：北米と欧州のみを使用**
     * **代替ドメイン名 (CNAME) - オプション：用意した証明書で設定したドメイン**
       例：`noipv4charge.example.com`
     * **Custom SSL certificate - optional：用意した証明書**
       * レガシークライアントサポート：有効にしない
       * セキュリティポリシー：TLSv1.2_2021 (推奨)
     * サポートされている HTTP バージョン：デフォルト (HTTP/2)
     * デフォルトルートオブジェクト - オプション：空欄
     * 標準ログ記録：オフ
     * IPv6：オン
     * 説明 - オプション：空欄もしくは適当に設定
4. 「ディストリビューションを作成」を押す

:::note info
Origin domain にIPアドレスを直接指定することは拒否され、ドメインが必要なようである。
ウェブアプリケーションファイアウォール (WAF) の有効化は有料である。
:::

## ドメインに CloudFront ディストリビューションのアドレスを登録する

ドメインに CloudFront ディストリビューションの情報を登録し、アクセスできるようにする。

筆者が使っている[ムームードメイン](https://muumuu-domain.com/)の場合、「ドメインにEC2インスタンスの IPv6 アドレスを登録する」での手順と同様に、以下のレコード値を追加する。

|項目|内容|
|---|---|
|サブドメイン|設定したCNAMEを表す内容<br>例：`noipv4charge`|
|種別|CNAME|
|内容|作成したディストリビューションの<br>「ディストリビューションドメイン名」<br>例：`vmpr9cxys7my9.cloudfront.net`|

:::note warn
「ディストリビューションドメイン名」をコピーする際に、ドメイン名の左のコピーボタンを使うと、`https://` という**ゴミがついた状態**でコピーされるようなので、注意すること。
:::

## エラー

CloudFront ディストリビューション用に設定したドメイン (CNAME) を用いてHTTPSでアクセスする。(例：`https://noipv4charge.example.com/`)
すると、EC2で立てたWebサーバーが配信している内容(たとえば、「Welcome to nginx!」のページ) が表示され…てほしかった。

しかし、実際は「オリジンのドメインの名前解決ができない」旨のエラーが表示された。
調べてみると、CloudFront は現状 IPv6 によるオリジンへのアクセスには対応していないようである。
**残念。**

* [CloudFront support for IPv6 origins | AWS re:Post](https://repost.aws/ja/questions/QUOWEDVURTSxWkSlenuHaS4g/cloudfront-support-for-ipv6-origins)
* [CloudFront connect to Ipv6 Only EC2 Instance | AWS re:Post](https://repost.aws/ja/questions/QUBCL3TUKrRbG6zIOi4nU2Uw/cloudfront-connect-to-ipv6-only-ec2-instance)

# ELBでWebサーバーにアクセスできるようにする？ (未調査、できても無駄)

AWSでWebサーバーにアクセスできるようにするためのプロキシ (リバースプロキシ) 的に使えそうなサービスには、ELB (Elastic Load Balancing) もある。
しかし、ELB は**有料**である。

[料金 - Elastic Load Balancing | AWS](https://aws.amazon.com/jp/elasticloadbalancing/pricing/)

米国東部 (バージニア北部) の Application Load Balancers の場合、1時間あたり 0.0225 USD～である。
一方、今回のテーマであるパブリック IPv4 アドレス課金は1時間あたり 0.005 USD であり、なんと**約5倍**もの高額な料金がかかってしまう。

無料枠もあるが、パブリック IPv4 アドレスと同じ (アカウントを作成して最初の12ヶ月、月750時間) なので、これもあまり意味がないだろう。

よって、ELB はパブリック IPv4 アドレス課金を回避する方法としては (使えるかを調査していないが、もし使えるとしても) **無駄である**といえる。
もちろん、ELB の本来の目的である負荷分散のための利用は検討してもよいだろう。

# 結論

今回の実験では、EC2 で AWS のパブリック IPv4 アドレス課金を回避しながら普通にアクセスできるWebサーバーを運用することはできなかった。

AWS のパブリック IPv4 アドレスの料金「1時間あたり 0.005 USD」は、以下の金額に相当する。

|利用時間|料金 (USD)|料金 (円)|
|---|--:|--:|
|1時間|0.005|0.8|
|1日 (24時間)|0.120|18.6|
|1週間 (7日)|0.840|130.2|
|1ヶ月 (30日)|3.600|558.0|
|1年 (365日)|43.800|6789.0|

(1USD = 155円で計算)

これを高いと感じるかは、人次第だろう。
とはいえ、節約したい場合は、運用中のパブリック IPv4 アドレスの使用を避けるのではなく、運用時間を減らすことを考えた方がいいかもしれない。