【AWS】インターフェイスエンドポイントのDNS名を使用してS3の署名付きURLを作成できるのか？

## はじめに

Amazon S3に対して一時的なアクセス権限を付与したい場合、署名付きURLの使用がまず検討項目に上がると思います。

アクセス権限を付与したいエンドユーザがオンプレミス環境にいて、かつインターネットを経由せずにS3までの通信を行いたいケースでは、AWS PrivateLink for Amazon S3を使用した構成を検討する必要があります。

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/privatelink-interface-endpoints.html#accessing-s3-interface-endpoints

AWS PrivateLink for Amazon S3を作成すると、以下のような2種類のエンドポイント固有のDNS名が払い出されます。
```
vpce-xxx-yyy.s3.<Regison名>.vpce.amazonaws.com 
vpce-xxx-yyy-<AZ名>.s3.<Region名>.vpce.amazonaws.com 
```

このDNS名を使用して署名付きURLを発行できるのか検証してみたので、その内容についてまとめます。検証のモチベーションについては記事の後段でまとめています。

## 構成図

以下のような構成を考えます。

![スクリーンショット 2025-05-21 23.04.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3880214/5b0c7423-003c-48e0-a2a0-21aa8c4a5dec.png)

VPC-1に配置したEC2 `bastion`からTransit GatewayとPrivateLinkを経由してS3にアクセスしたい、というのが要件です。
今回の構成ではVPC-1をオンプレミス環境、EC2をオンプレミスサーバに見立てて検証を行っていきます。

## 検証手順・結果
### リソースの作成

上記構成を作成するために、以下のTerraformコードを使用します。

https://github.com/OchiShuji/s3-presigned-url

リソースの作成後、マネジメントコンソールからVPCエンドポイントのDNS名を確認します。
いくつかのDNS名が記載されていますが、`vpce.amazonaws.com`というドメインを持つものがエンドポイント固有のS3 DNS名です。

![スクリーンショット 2025-05-25 15.06.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3880214/be0746a4-26aa-4302-a663-250b036fea3a.png)


### 署名付きURLの作成

ローカルの端末から`bastion`に対してSSH接続した上で、`bastion`内でAWS SDKを使用して署名付きURLを発行します。

署名付きURLの作成には以下のAWS公式ドキュメントで公開されているpythonのサンプルコードを使用します。

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/API/s3_example_s3_Scenario_PresignedUrl_section.html

注意点として、エンドポイント固有のS3 DNS名を使用して署名付きURLを作成する場合は、以下のように`boto3.client`の実行時に`endpoint_url`を指定する必要があります。

`endpoint_url`を指定しない場合、パブリックのS3 DNS名(s3.<リージョン名>.vpce.amazonaws.com)を使用して署名付きURLが作成されます。

```py
s3_client = boto3.client(
    "s3", 
    region_name="ap-northeast-1",
    endpoint_url="https://bucket.vpce-xxx-yyy.s3.ap-northeast-1.vpce.amazonaws.com"
)
```
また、endpoint_urlには`vpce-xxx-yyy.s3.ap-northeast-1.vpce.amazonaws.com`というFQDNは使用できないので注意が必要です。VPCエンドポイントに対してアクセスする際に使用する証明書のCNおよびSANには上記のDNS名が含まれないので、証明書エラーが発生します。

```shell_sessionsession
[root@ip-10-0-1-241 ~]# HOST="vpce-xxx-yyy.s3.ap-northeast-1.vpce.amazonaws.com"
[root@ip-10-0-1-241 ~]# echo | openssl s_client -connect ${HOST}:443 -servername ${HOST} \
-showcerts 2>/dev/null \
| openssl x509 -noout -text \
| grep -A1 "Subject Alternative Name"
            X509v3 Subject Alternative Name: 
                DNS:s3.ap-northeast-1.amazonaws.com, DNS:*.control.vpce-xxx-yyy-ap-northeast-1c.s3.ap-northeast-1.vpce.amazonaws.com, DNS:*.accesspoint.vpce-xxx-yyy-ap-northeast-1d.s3.ap-northeast-1.vpce.amazonaws.com, DNS:bucket.vpce-xxx-yyy-ap-northeast-1c.s3.ap-northeast-1.vpce.amazonaws.com, DNS:bucket.vpce-xxx-yyy-ap-northeast-1a.s3.ap-northeast-1.vpce.amazonaws.com, DNS:*.bucket.vpce-xxx-yyy.s3.ap-northeast-1.vpce.amazonaws.com, DNS:*.control.vpce-xxx-yyy-ap-northeast-1a.s3.ap-northeast-1.vpce.amazonaws.com, DNS:*.control.vpce-xxx-yyy.s3.ap-northeast-1.vpce.amazonaws.com, DNS:*.bucket.vpce-xxx-yyy-ap-northeast-1d.s3.ap-northeast-1.vpce.amazonaws.com, DNS:bucket.vpce-xxx-yyy-ap-northeast-1d.s3.ap-northeast-1.vpce.amazonaws.com, DNS:bucket.vpce-xxx-yyy.s3.ap-northeast-1.vpce.amazonaws.com, DNS:*.s3.ap-northeast-1.amazonaws.com, DNS:*.accesspoint.vpce-xxx-yyy-ap-northeast-1c.s3.ap-northeast-1.vpce.amazonaws.com, DNS:*.control.vpce-xxx-yyy-ap-northeast-1d.s3.ap-northeast-1.vpce.amazonaws.com, DNS:*.accesspoint.vpce-xxx-yyy-ap-northeast-1a.s3.ap-northeast-1.vpce.amazonaws.com, DNS:*.bucket.vpce-xxx-yyy-ap-northeast-1a.s3.ap-northeast-1.vpce.amazonaws.com, DNS:*.accesspoint.vpce-xxx-yyy.s3.ap-northeast-1.vpce.amazonaws.com, DNS:*.bucket.vpce-xxx-yyy-ap-northeast-1c.s3.ap-northeast-1.vpce.amazonaws.com, DNS:*.s3-control.ap-northeast-1.amazonaws.com, DNS:*.s3-accesspoint.ap-northeast-1.amazonaws.com
```

### S3に対するアクセスのテスト

サンプルコードを実行すると、`INFO: Got presigned URL: `に続いて署名付きURLが出力されます。FQDNが`endpoint_url`で指定したURLのFQDN: `bucket.<エンドポイント固有のS3 DNS名>`と同じになっていることが確認できます。

`200 OK`が返されているので、署名付きURLを使用して正常にS3にアクセスできているようです。

```shell_session
(.venv) [root@ip-10-0-1-241 ~]# python3 main.py vpc-endpoint-demo-renewing-bird test.txt get
----------------------------------------------------------------------------------------
Welcome to the Amazon S3 presigned URL demo.
----------------------------------------------------------------------------------------
INFO: Found credentials from IAM Role: bastion_ec2_role
INFO: Got presigned URL: https://bucket.vpce-xxx-yyy.s3.ap-northeast-1.vpce.amazonaws.com/vpc-endpoint-demo-renewing-bird/test.txt?AWSAccessKeyId= ...
Using the Requests package to send a request to the URL.
Status: 200
Reason: OK
----------------------------------------------------------------------------------------
(.venv) [root@ip-10-0-1-241 ~]# 
```

## 本検証のモチベーション

エンドポイント固有のS3 DNS名を使用せずに、バブリックのS3 DNS名を使用して署名付きURLを作成した場合、閉域網内で署名付きURLを使用するには以下の設定が必要です。

① インタフェースエンドポイント作成時に「プライベートDNS名」を有効にする
② Route 53 Inbound Resolver Endpointを作成する
③ オンプレミスからのDNSクエリがRoute 53のプライベートホストゾーンで名前解決されるようにオンプレ側のDNSに転送設定を行う

構成図にすると以下のような形になります。

![スクリーンショット 2025-05-25 21.26.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3880214/3f3290ad-4513-4647-a028-50fdad2a602c.png)

社内固有のDNSサーバをオンプレ環境に持つ場合、他の③の設定を入れるのが難しいケースもあると思います。エンドポイント固有のS3 DNS名を使用すれば、このDNS名はRoute 53のパブリックホストゾーンで名前解決が行われるため、②、③の設定を入れることなく署名付きURLを使用することができます。

## まとめ

エンドポイント固有のS3 DNS名を使用して署名付きURLを発行できるかについて検証しました。
どなたかの参考になれば幸いです。