はじめに
S3の署名付きURLは、URLに含まれる署名で認証するため、取得する側にS3の権限がなくてもオブジェクトを取得できます。インターネットに接続できないVPCでは、S3への通信をVPCエンドポイント経由にする必要があります。インターフェイスエンドポイントには、エンドポイント固有のDNS名が割り当てられます。署名付きURLはホスト名も署名の対象になるため、今回はインターフェイスエンドポイントだけの構成で、署名付きURLのホスト名と名前解決の組み合わせを検証しました。
検証環境
検証環境の構成は下記の通りです。
| リソース | 内容 |
|---|---|
| VPC | 10.0.0.0/16。プライベートサブネットのみ |
| S3のインターフェイスエンドポイント | セキュリティグループで443番ポートを許可。プライベートDNSは無効で作成 |
| Systems ManagerのVPCエンドポイント | ssm、ssmmessages、ec2messagesの3つ。Session ManagerでEC2に接続するために使用 |
| EC2 | Amazon Linux 2023。IAMロールはAmazonSSMManagedInstanceCoreのみで、S3の権限は付与しない |
| S3バケット | テスト用のファイル(test2026.txt)を1つ配置 |
インターネットゲートウェイ、NATゲートウェイ、S3のゲートウェイエンドポイントは作成していません。署名付きURLはCloudShellで発行し、EC2からcurlで取得します。
① S3のインターフェイスエンドポイントのDNS名で署名付きURLを発行する
名前解決と通信の流れは下記の通りです。
S3のインターフェイスエンドポイントのDNS名を確認する
CloudShellでS3のインターフェイスエンドポイントのDNS名を確認します。
aws ec2 describe-vpc-endpoints --region ap-northeast-1 \
--filters Name=service-name,Values=com.amazonaws.ap-northeast-1.s3 Name=vpc-endpoint-type,Values=Interface \
--query 'VpcEndpoints[].{Id:VpcEndpointId,Dns:DnsEntries[0].DnsName,PrivateDns:PrivateDnsEnabled}' --output table
+--------------------------------------------------------------------------+-------------------------+--------------+
| Dns | Id | PrivateDns |
+--------------------------------------------------------------------------+-------------------------+--------------+
| *.vpce-xxxxxxxxxxxxxxxxx-xxxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com | vpce-xxxxxxxxxxxxxxxxx | False |
+--------------------------------------------------------------------------+-------------------------+--------------+
CloudShellで署名付きURLを発行する
先頭の*をbucketに置き換えたDNS名を--endpoint-urlに指定して、署名付きURLを発行します。
aws s3 presign s3://<バケット名>/test2026.txt --region ap-northeast-1 \
--endpoint-url https://bucket.vpce-xxxxxxxxxxxxxxxxx-xxxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com \
--expires-in 600
https://bucket.vpce-xxxxxxxxxxxxxxxxx-xxxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com/<バケット名>/test2026.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...&X-Amz-Date=...&X-Amz-Expires=600&X-Amz-SignedHeaders=host&X-Amz-Security-Token=...&X-Amz-Signature=...
バケット名がパスに入る形式で発行されます。
EC2から署名付きURLでファイルを取得する
EC2でS3のインターフェイスエンドポイントのDNS名を名前解決し、curlで署名付きURLにアクセスします。
getent hosts bucket.vpce-xxxxxxxxxxxxxxxxx-xxxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com
curl -v -o /tmp/case1.txt "<署名付きURL>"
10.0.x.x bucket.vpce-xxxxxxxxxxxxxxxxx-xxxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com
* Trying 10.0.x.x:443...
* SSL connection using TLSv1.3 / TLS_CHACHA20_POLY1305_SHA256 / x25519 / RSASSA-PSS
* subjectAltName: "bucket.vpce-xxxxxxxxxxxxxxxxx-xxxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com" matches cert's "bucket.vpce-xxxxxxxxxxxxxxxxx-xxxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com"
< HTTP/1.1 200 OK
< Server: AmazonS3
< Content-Length: 8
S3のインターフェイスエンドポイントのIPアドレスに解決され、200が返ってファイルを取得できました。EC2のIAMロールにS3の権限は付与していません。
② 通常のS3のDNS名ではS3に接続できないことを確認する
名前解決と通信の流れは下記の通りです。
--endpoint-urlを指定せずに発行すると、署名付きURLのホスト名は<バケット名>.s3.ap-northeast-1.amazonaws.comになります。プライベートDNSが無効のため、EC2ではパブリックIPアドレスに解決されます。接続の段階で失敗するため、署名なしのURLで確認しています。
getent hosts <バケット名>.s3.ap-northeast-1.amazonaws.com
curl -v --max-time 10 -o /dev/null https://<バケット名>.s3.ap-northeast-1.amazonaws.com/test2026.txt
52.219.x.x s3-r-w.ap-northeast-1.amazonaws.com <バケット名>.s3.ap-northeast-1.amazonaws.com
3.5.x.x s3-r-w.ap-northeast-1.amazonaws.com <バケット名>.s3.ap-northeast-1.amazonaws.com
* Trying 16.15.x.x:443...
* Trying 52.219.x.x:443...
* Trying 3.5.x.x:443...
* Connection timed out after 10001 milliseconds
curl: (28) Connection timed out after 10001 milliseconds
インターネットへの経路がないため、タイムアウトしました。①で取得できたのは、S3のインターフェイスエンドポイントを経由したためです。
③ S3のインターフェイスエンドポイントのプライベートDNSを有効にする
名前解決と通信の流れは下記の通りです。
S3のインターフェイスエンドポイントのDNSの設定を変更する
プライベートDNSを有効にすると、プライベートDNSの「インバウンドエンドポイントのみ」の設定(PrivateDnsOnlyForInboundResolverEndpoint)も既定で有効になります。この設定が有効の場合、VPC内からの通信はゲートウェイエンドポイントを経由します。今回はゲートウェイエンドポイントを作成していないため、この設定を無効にしてプライベートDNSを有効にします。
aws ec2 modify-vpc-endpoint --region ap-northeast-1 --vpc-endpoint-id vpce-xxxxxxxxxxxxxxxxx \
--private-dns-enabled --dns-options PrivateDnsOnlyForInboundResolverEndpoint=false
変更後、状態がpendingからavailableになるまで数分かかりました。
aws ec2 describe-vpc-endpoints --region ap-northeast-1 --vpc-endpoint-ids vpce-xxxxxxxxxxxxxxxxx \
--query 'VpcEndpoints[].[State,PrivateDnsEnabled,DnsOptions.PrivateDnsOnlyForInboundResolverEndpoint]' --output text
available True False
EC2とCloudShellで通常のS3のDNS名を名前解決する
EC2では、通常のS3のDNS名がS3のインターフェイスエンドポイントのIPアドレスに解決されます。
[EC2]
10.0.x.x <バケット名>.s3.ap-northeast-1.amazonaws.com
同じDNS名をVPCの外にあるCloudShellで名前解決すると、パブリックIPアドレスが返ります。
[CloudShell]
16.15.x.x s3-r-w.ap-northeast-1.amazonaws.com <バケット名>.s3.ap-northeast-1.amazonaws.com
EC2から通常のS3のDNS名の署名付きURLでファイルを取得する
--endpoint-urlを指定せずに署名付きURLを発行します。
aws s3 presign s3://<バケット名>/test2026.txt --region ap-northeast-1 --expires-in 1800
https://<バケット名>.s3.ap-northeast-1.amazonaws.com/test2026.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&...&X-Amz-SignedHeaders=host&...&X-Amz-Signature=...
EC2からcurlでアクセスします。
* Trying 10.0.x.x:443...
* subjectAltName: "<バケット名>.s3.ap-northeast-1.amazonaws.com" matches cert's "*.s3.ap-northeast-1.amazonaws.com"
< HTTP/1.1 200 OK
通常のS3のDNS名の署名付きURLのまま、S3のインターフェイスエンドポイントを経由してファイルを取得できました。
おわりに
S3のインターフェイスエンドポイントだけの構成で、閉域から署名付きURLを利用できることを確認しました。



