【AWS】あまり費用をかけずに閉域網からHTTPSでS3のファイルダウンロードを可能にする方法

# 0. はじめに
公共系システムでは政府共通ネットワークなど、各公共機関の組織で用意された閉域網に接続することが多いです。公共系システムのクラウド化にあたってはその閉域網からアクセスしてくるユーザ向けにサービスを提供する場合も多いですが、一般的にあまりノウハウが流通していないかと思います。

今回はそんな閉域網から接続するユーザ向けにAWS S3からファイルダウンロードを可能にする構成について検討し、比較的簡易な構成であまり費用をかけずに行える方法を確認しました。

# 目次
[0. はじめに](#0-はじめに)
[1. 要件](#1-要件)
[2. 考えられる構成](#2-それぞれ暗号化する方法)
[3. インタフェースエンドポイント経由でS3から直接ダウンロードする方法の検証](#3-インタフェースエンドポイント経由でS3から直接ダウンロードする方法の検証)
[3-1. 必要な設定](#3-1-必要な設定)
[3-2. 検証結果](#3-2-検証結果)
[3-2-1. s3の証明書と接続可能なurl](#3-2-1-s3の証明書と接続可能なurl)
[3-2-2. ファイルの種類ごとの動き](#3-2-2-ファイルの種類ごとの動き)
[4. まとめ](#4-まとめ)

# 1. 要件
以下の要件で構成を考えました。通常のサービス提供するルートと別に大きなファイルをダウンロードするためのルートを用意し個別に帯域制御したいという要望に応える構成となります。
- AWSでALBとEC2を使ってWebサービスを提供
- 大きなファイルはS3からダウンロードできるようにする
- ユーザはブラウザを使って閉域網(WAN)からDirect Connectを通ってAWSにアクセスする
- 閉域網のDNSにはAレコードを登録する必要がある<br>→固定IPアドレスにする必要がある
- 帯域制御装置はURLでは制御できずIPアドレスで制御する必要がある<br>→WebサービスアクセスとS3アクセスのIPアドレスを分ける必要がある

![概要.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3571868/d5e67332-fe98-bd51-300c-40aaaea26dbc.png)

# 2. 考えられる構成
Public VIFを使う構成も考えられますが、S3アクセスのためだけにVIFを変更するのは大掛かりなので別の方法を考えます。セキュリティや可用性、運用性を考慮するとNAT GW経由の接続やEC2でプロキシを立てるような構成は避けたいため、多少コストがかかってもインタフェースエンドポイント経由でのアクセスが望ましいと考えます。

通常S3でファイルを公開する場合、静的ウェブサイトホスティングを使用しますが、静的ウェブサイトホスティングはHTTPSに対応していないため、HTTPS対応するならALB経由でアクセスが必要になります。カスタムドメイン名を使いたい場合はこの構成が必須になるかと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3571868/f73c9dbf-f327-d366-506a-4cc63f80ef22.png)

https://aws.amazon.com/jp/blogs/news/internal-static-web-hosting/

しかし、要件に沿ってサービス提供用IPとダウンロード用IPを分けて固定IPにするためには、サービス提供用EC2とダウンロード用S3それぞれに対しNLBとALBのセットが必要になってしまい、それなりにコストがかかります。

![ALB経由の接続.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3571868/5fc0abc8-bc39-f820-327a-1fceb207dcc3.png)

コストを下げるため、ダウンロード用NLBとダウンロード用ALBをなくせないか？ということで、静的ウェブサイトホスティングを使わずにS3の各オブジェクトが持つオブジェクトURL(`https://バケット名.s3.ap-northeast-1.amazonaws.com/オブジェクトパス`)から直接ダウンロードできないか調べました。
すると、バケットポリシーを適切に設定すれば直接ダウンロードが可能で、動作としては
> REST API の GetObject を署名なし（未認証）で実行する

ことになるとの記事を見つけました。

https://techblog.ap-com.co.jp/entry/2024/01/26/215603

同記事はインターネット経由の接続になっていますが、閉域網からインタフェースエンドポイント経由でも同様のことができるのではないかと検証してみました。

# 3. インタフェースエンドポイント経由でS3から直接ダウンロードする方法の検証
実際にオンプレからのDirect Connectを検証するための機材の準備は難しいので、以下の構成で検証します。インタフェースエンドポイントのIPアドレスをWANユーザ用DNSに登録し、private subnetまで通信が到達できれば条件は同じになるはずです。

![検証構成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3571868/e55afa8a-a4e5-3318-e2b9-3a974277599a.png)

インタフェースエンドポイントは昨年からIPアドレス固定が可能になりました。

https://aws.amazon.com/jp/about-aws/whats-new/2023/08/aws-privatelink-user-defined-ip-vpc-endpoints/

また、DNSから同じホスト名に対して複数のIPアドレスが返された場合、ブラウザはDNSラウンドロビンでどれかのIPにアクセスし、接続できなかった場合は他のIPに接続するため、DNSに各AZで固定したIPを登録すればAZ障害にも対応可能になります。

## 3-1. 必要な設定
以下の設定を行いました。
- 接続元EC2
    - WindowsのEC2インスタンスを構築
    - DNS機能をインストール
    - DNSのフォワードルックアップゾーンにゾーン「s3.amazonaws.com」を作成
    - ゾーン「s3.amazonaws.com」にAレコードとしてホスト名にバケット名、IPアドレスにインタフェースエンドポイントのIPを登録
    - ネットワークの設定でDNSサーバとしてローカル(127.0.0.1)を参照
    - セキュリティグループでインタフェースエンドポイントのセキュリティグループへのHTTPSを許可
- インタフェースエンドポイント
    - 2つのAZのサブネットでIPアドレスをそれぞれ「IPアドレスの指定」にチェックしてIPv4アドレスとして「172.25.21.23」「172.25.22.23」を設定
    - セキュリティグループで接続元EC2のセキュリティグループからのHTTPSを許可
- ダウンロード用S3
    - バケット「test-private-a0313bc」を作成
    - アクセス用のオブジェクト「s3access.html」「test.mp4」をアップロード
    - バケットポリシーで接続元EC2のIPアドレスからのs3:GetObjectを許可

バケットポリシーは以下で検証しました。aws:SourceVpceでインタフェースエンドポイントのエンドポイントIDを指定することで、そのエンドポイント経由のアクセスだけを許可できます。今回はVPC上のEC2で検証したためaws:VpcSourceIpで接続元IPを指定しましたが、実際にオンプレミスのユーザ端末を指定する場合はaws:SourceIpを使用することになります。
> ```json
> {
>     "Version": "2012-10-17",
>     "Statement": [
>         {
>             "Effect": "Allow",
>             "Principal": "*",
>             "Action": "s3:GetObject",
>             "Resource": "arn:aws:s3:::test-private-a0313bc/*",
>             "Condition": {
>                 "StringEquals": {
>                     "aws:SourceVpce": "vpce-xxxx"
>                 },
>                 "IpAddress": {
>                     "aws:VpcSourceIp": [
>                         "172.25.21.0/24"
>                     ]
>                 }
>             }
>         }
>     ]
> }
> ```

## 3-2. 検証結果
上記の設定で無事接続できました。証明書エラーも発生していません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3571868/e47acd8a-fcaa-c06f-6e06-d79a2378efec.png)

パケットキャプチャしてみると「172.25.21.23」にアクセスしていたのでAZ障害を模してインタフェースエンドポイントのサブネットを編集し、「172.25.22.23」だけ残して再度アクセスしたところ、少し時間がかかりましたが問題なく接続できました。

### 3-2-1. S3の証明書と接続可能なURL
S3の証明書を確認するとサブジェクトは以下の値でした。
> CN = s3.ap-northeast-1.amazonaws.com

これだけだと接続したドメイン「s3.amazonaws.com」と違うので証明書エラーになるはずなのですが、実はサブジェクトの別名(Subject Alternative Names)に様々なドメインやホストが登録されており、これによりいくつかのパターンで証明書エラーなく接続可能なことを確認しました。

:::note 
接続可能なパターン
1. https://バケット名.s3.amazonaws.com/ファイルパス
1. https://バケット名.s3.ap-northeast-1.amazonaws.com/ファイルパス
1. https://bucket.vpce-xxxx-xxxx.s3.ap-northeast-1.vpce.amazonaws.com/バケット名/ファイルパス<br>※vpce-xxxx-xxxx.s3.ap-northeast-1.vpce.amazonaws.comはインタフェースエンドポイントのDNS名。ap-northeast-1は東京リージョンのコードのためインタフェースエンドポイントのリージョンが変わればこれも変わるはずです。
:::

1、2番目のURLは仮想ホスト型のURL、3番目のURLはパス型のURLというそうで、パス型のURLは非推奨になろうとしているものなので避けたほうが良いようです。(2020年から非推奨化が延期され続けているようですが)

https://aws.amazon.com/jp/blogs/aws/amazon-s3-path-deprecation-plan-the-rest-of-the-story/

### 3-2-2. ファイルの種類ごとの動き
Edgeで接続した場合、htmlなら上記のようにhtmlの内容を表示でき、mp4なら動画再生できました。exeファイルの場合はダウンロードされました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3571868/98c4d7d7-730d-7c12-6826-6924034d2d28.png)

記事を書いていて気づきましたが、Qiitaに貼った画像も同様のアクセス方式(閉域接続ではないですが)になっているようで、上に貼ったpngファイルなら画像が表示されますね。

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3571868/98c4d7d7-730d-7c12-6826-6924034d2d28.png

# 4. まとめ
S3に閉域網相当の端末からインタフェースエンドポイント経由で接続した場合もオブジェクトのURLでファイルをダウンロード可能であることを確認しました。閉域網接続で必要になる場合が多いIPアドレス固定にも対応可能です。URLはS3のままで良く、静的なコンテンツをHTTPSで配布したいという場合には使いやすい構成だと思いますので参考になればと思います。

※ 本ブログに記載した内容は個人の見解であり、所属する会社、組織とは関係ありません。