AWS PrivateLink が Amazon S3 に対応！何が嬉しいのか

## はじめに

2021/2/2 のアップデートで AWS PrivateLink 経由で Amazon S3 にアクセスできるようになりました。

**AWS PrivateLink for Amazon S3 is Now Generally Available**
https://aws.amazon.com/jp/blogs/aws/aws-privatelink-for-amazon-s3-now-available/

これまでの Gateway 型 VPC エンドポイントと比較して何が嬉しく、どのような点に注意すべきか記載します。

## 嬉しい点
### オンプレミスからのアクセス
なんと言ってもこの点が待望だったかと思います。
Gateway 型の場合、S3 のエンドポイントはグローバル IP アドレスのままです。
VPC 内の S3 に対する通信を、ルートテーブルの設定で VPC エンドポイントに向けることにより
AWS NW の外 (インターネット) には出ない形で通信を行います。

この仕様上、AWS Direct Connect や VPN を使用して接続されたオンプレミス環境から
直接 S3 と通信することはできませんでした。
ではこれまでどうしていたかというと、EC2 にプロキシサーバーたてて対応することがありました。
当然のことながら、このプロキシサーバーの稼働コスト、メンテナンスコストが発生します。

AWS PrivateLink による Interface 型の エンドポイントは VPC に エンドポイント用の ENI が作成されます。
ENI に割り当てられた プライベートIP を使用して、オンプレミス環境から S3 へ
直接プライベート接続することができます！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/30336378-0b5b-5264-df9b-e3baae36f7bd.png)

### 他 VPC や別のリージョンからのアクセス
考え方としてはオンプレミスと同じです。
エンドポイントに払い出された VPC のプライベート IP を介してアクセスするため
例えば VPC Peering で接続された他リージョンの VPC からもアクセスすることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/1a0f8e33-cb48-6fac-1f03-995050d3cca5.png)
1つのエンドポイントで、任意のリージョンの S3 バケットにアクセス**できるわけはありません。**
Gateway 型の VPC エンドポイントと同様、アクセスする S3 バケットは
同じリージョンに存在する必要があります。東京リージョンの VPC および VPC エンドポイントからは
東京リージョンに存在する S3 バケットにのみアクセスできます。

### Gateway 型 と Interface 型エンドポイントの併用
S3 の Gateway 型エンドポイントと Interface 型エンドポイントは併用が可能です。
VPC 上のアプリケーションは これまで通り Gateway 型エンドポイントを使用し、
オンプレミスアプリケーションは Interface 型エンドポイントを使用するといった構成が可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/645aa439-0b18-a5e5-b7bd-7d0ce589ed09.png)
後述のとおり、Interface 型エンドポイントはデータ転送料金やエンドポイント URL の変更などを
考慮しなければならないため、この構成にはメリットがあります。

## 考慮すべき点

### 利用料金
Gateway 型の VPC エンドポイントは無料で利用することができましたが、
Interface 型の VPC エンドポイントは エンドポイント 1 つあたりの料金 (USD/時間) および
処理データ 1 GB あたりの料金 (USD) が発生します。

2021/2/3 時点で、東京リージョンの場合は以下のコストが発生します。

|VPC エンドポイント 1 つあたりの料金 (USD/時間)|処理データ 1 GB あたりの料金 (USD)|
|---|---|
|0.014 USD| 0.01 USD|

3 AZ で冗長構成をとる場合、エンドポイントの時間料金が3つ分発生します。
1 GBあたりのデータ転送料金もそれほど高額ではありませんが、大量のデータをやり取りする場合は
料金について予め考慮が必要でしょう。

### エンドポイントの指定
S3 の Interface 型エンドポイントはプライベート DNS 機能を**サポートしていません。**
エンドポイント作成画面でもチェックボックスがグレーアウトされています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/50cbabe9-017a-7e23-b524-360bc8951e34.png)
プライベート DNS 機能とはサービスのデフォルト DNS 名 (ex. ec2.ap-northeast-1.amazonaws.com) を
VPC のエンドポイントに割り当てられたプライベート IP アドレスに解決してくれる機能です。
これにより Route 53 Resolver (以前の Amazon Provided DNS) を使用している VPC や 
Route 53 Resolver for Hybrid Clouds を使用しているオンプレミス環境ではアプリケーション側の
変更を必要とせず、デフォルトの DNS ホスト名を使用して VPC エンドポイントに対して
リクエストを送信することができます。

この機能を使用することができないため、PrivateLink を使用して S3 にアクセスするには
以下のように VPC エンドポイントに払い出された固有の DNS 名を使用する必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/a8cbb5c3-6e89-7809-2f02-08eb4b651c85.png)
例えば AWS CLI の場合は以下のようになります。

```shell-session
$ aws s3 ls s3://my-bucket/ --endpoint-url https://bucket.vpce-1a2b3c4d-5e6f.s3.ap-northeast-1.vpce.amazonaws.com
```

AWS SDK (boto3) を使用したアプリケーションの場合。

```py
import boto3

s3_client = boto3.client(
    's3',
    endpoint_url = 'https://bucket.vpce-1a2b3c4d-5e6f.s3.ap-northeast-1.vpce.amazonaws.com'
)
```

サブドメインに bucket を指定するのは、S3 バケットに関連する API 操作を行う場合です。
(指定するのはバケット名ではないことに注意してください。)
操作対象の API によってサブドメインを変える必要があります。

|API操作|エンドポイント例|
|---|---|
|バケット|bucket.vpce-1a2b3c4d-5e6f.s3.ap-northeast-1.vpce.amazonaws.com|
|アクセスポイント|accesspoint.vpce-1a2b3c4d-5e6f.s3.ap-northeast-1.vpce.amazonaws.com|
|S3 Control|control.vpce-1a2b3c4d-5e6f.s3.ap-northeast-1.vpce.amazonaws.com|

例えばエンドポイントのプライベート IP などを直に指定すると、`SSL validation failed` が発生します。

```shell-session
$ aws s3 ls --endpoint-url https://xxx.xxx.xxx.xxx
SSL validation failed for https://xxx.xxx.xxx.xxx/ ("hostname 'xxx.xxx.xxx.xxx' doesn't match either of 's3.ap-northeast-1.amazonaws.com', 
'*.accesspoint.vpce-1a2b3c4d-5e6f-ap-northeast-1d.s3.ap-northeast-1.vpce.amazonaws.com', 
'*.control.vpce-1a2b3c4d-5e6f-ap-northeast-1a.s3.ap-northeast-1.vpce.amazonaws.com', 
'*.accesspoint.vpce-1a2b3c4d-5e6f.s3.ap-northeast-1.vpce.amazonaws.com', 'bucket.vpce-1a2b3c4d-5e6f-ap-northeast-1a.s3.ap-northeast-1.vpce.amazonaws.com', 
'*.s3-control.ap-northeast-1.amazonaws.com', '*.control.vpce-1a2b3c4d-5e6f-ap-northeast-1d.s3.ap-northeast-1.vpce.amazonaws.com', 
'*.s3.ap-northeast-1.amazonaws.com', '*.s3-accesspoint.ap-northeast-1.amazonaws.com', '*.control.vpce-1a2b3c4d-5e6f.s3.ap-northeast-1.vpce.amazonaws.com', '*.bucket.vpce-1a2b3c4d-5e6f-ap-northeast-1d.s3.ap-northeast-1.vpce.amazonaws.com', 
'bucket.vpce-1a2b3c4d-5e6f-ap-northeast-1c.s3.ap-northeast-1.vpce.amazonaws.com', 'bucket.vpce-1a2b3c4d-5e6f.s3.ap-northeast-1.vpce.amazonaws.com',
'*.control.vpce-1a2b3c4d-5e6f-ap-northeast-1c.s3.ap-northeast-1.vpce.amazonaws.com', '*.bucket.vpce-1a2b3c4d-5e6f.s3.ap-northeast-1.vpce.amazonaws.com', '*.accesspoint.vpce-1a2b3c4d-5e6f-ap-northeast-1c.s3.ap-northeast-1.vpce.amazonaws.com', 
'*.bucket.vpce-1a2b3c4d-5e6f-ap-northeast-1c.s3.ap-northeast-1.vpce.amazonaws.com', 
'bucket.vpce-1a2b3c4d-5e6f-ap-northeast-1d.s3.ap-northeast-1.vpce.amazonaws.com', 
'*.accesspoint.vpce-1a2b3c4d-5e6f-ap-northeast-1a.s3.ap-northeast-1.vpce.amazonaws.com', 
'*.bucket.vpce-1a2b3c4d-5e6f-ap-northeast-1a.s3.ap-northeast-1.vpce.amazonaws.com'",)
```

## 実験: アプリケーションが エンドポイント URL の変更に対応していなかったら？
VPC エンドポイントに払い出されたプライベート IP アドレスを hosts に設定し、ローカルで
デフォルトの DNS 名を強制的に上書きしたところ、バケットレベルの操作は行うことができました。

```
xxx.xxx.xxx.xxx s3.ap-northeast-1.amazonaws.com
xxx.xxx.xxx.xxx <bucket-name>.s3.ap-northeast-1.amazonaws.com
```

```shell-session
$ aws s3 ls
$ aws s3 ls s3://my-bucket/
```

ただこのような使い方はドキュメントには記載がなく、AWS 的にサポートされるかは不明です。
そもそも hosts を使用する場合は、

* VPC エンドポイントの冗長構成がとれない
* バケット毎に DNS 名を追加する必要がある

などデメリットが多いですので、参考程度にお願いします。


## ドキュメント
**Amazon S3 and interface VPC endpoints (AWS PrivateLink)**
https://docs.aws.amazon.com/AmazonS3/latest/userguide/privatelink-interface-endpoints.html

以上です。
参考になれば幸いです。
Happy storing!