閉域網でS3の署名付きURLって使えるの?

目次
こんにちは、クラウドビルダーズのきむです。
AWSのS3が提供する署名付きURL機能は、一時的なファイルアクセス権限を簡単に付与できる便利な機能です。しかし、セキュリティ要件でインターネット接続が制限されているオンプレミス環境では、この機能を利用できないのではないか、という懸念があります。
この記事では、インターネットに接続できない環境から、AWS PrivateLinkを使用してS3の署名付きURLにアクセスする方法を、実際の検証結果と共に解説します。
構成
今回は下記の構成をterraformで作成していきます。

今回は、オンプレミス環境を想定したConsumerアカウントと、S3を提供するProviderアカウントの2つのAWSアカウントを使用します。実環境でのDirect Connect接続を模擬するため、両環境のVPCをVPC Peeringで接続します。
ソースコード
今回使用する一連のスースコードは下記に公開しています。
環境構築
一連のコマンドはbuild.shを使用して実行するので、スクリプトに権限を付与します。
$ chmod +x build.sh
次にterraformのinit処理を行います。
$ ./build.sh init
Provider環境
Provider環境は2段階でデプロイします。最初はVPC Peeringのルート追加を行わずにデプロイします。
# 1回目はルートの追加を行わない
$ ./build.sh apply provider false
provider の apply を開始します...
.......
provider_account_id = "123456789012"
vpc_id = "vpc-xxxxxxxxxxxxxxxxx"
処理が正常に完了しました
Consumer環境
Provider環境から出力された情報をConsumer環境の設定に反映します。
variable "peer_owner_id" {
type = string
default = "123456789012"
}
variable "peer_vpc_id" {
type = string
default = "vpc-xxxxxxxxxxxxxxxxx"
}
設定を反映後、Consumer環境をデプロイします。
$ ./build.sh apply consumer
consumer の apply を開始します...
.......
処理が正常に完了しました
ピアリングの承諾
ConsumerアカウントをデプロイするとProducerアカウントにVPC Peeringが表示されるので、マネジメントコンソールからリクエストの承諾を行います。


承諾が完了したら、VPC Peering向けのルートを追加するために、フラグを切り替えてProvider環境をもう一度デプロイします。
# フラグをtrueに変える
$ ./build.sh apply provider true
provider の apply を開始します...
.......
provider_account_id = "123456789012"
vpc_id = "vpc-xxxxxxxxxxxxxxxxx"
処理が正常に完了しました
EC2への接続
ConsumerアカウントのマネジメントコンソールよりEC2 Instance Connectでインスタンスへ接続します。

digコマンドでs3.ap-northeast-1.amazonaws.comを確認すると、グローバルのIPアドレスが返ってきます。
$ dig s3.ap-northeast-1.amazonaws.com
; <<>> DiG 9.18.33 <<>> s3.ap-northeast-1.amazonaws.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 7441
;; flags: qr rd ra; QUERY: 1, ANSWER: 8, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;s3.ap-northeast-1.amazonaws.com. IN A
;; ANSWER SECTION:
s3.ap-northeast-1.amazonaws.com. 4 IN A 52.219.136.86
s3.ap-northeast-1.amazonaws.com. 4 IN A 52.219.8.88
s3.ap-northeast-1.amazonaws.com. 4 IN A 52.219.151.68
s3.ap-northeast-1.amazonaws.com. 4 IN A 52.219.163.60
s3.ap-northeast-1.amazonaws.com. 4 IN A 52.219.150.252
s3.ap-northeast-1.amazonaws.com. 4 IN A 52.219.152.104
s3.ap-northeast-1.amazonaws.com. 4 IN A 3.5.158.147
s3.ap-northeast-1.amazonaws.com. 4 IN A 3.5.154.121
;; Query time: 0 msec
;; SERVER: 172.16.0.2#53(172.16.0.2) (UDP)
;; WHEN: Wed Feb 12 08:38:04 UTC 2025
;; MSG SIZE rcvd: 188
このままでは署名付きURLでS3にアクセスできないため、Route53 Resolverのインバウンドエンドポイントで名前解決を行うために、DNS設定を変更します。
DNS設定変更
Consumer環境のEC2インスタンスで、Route53 Resolverのインバウンドエンドポイントを使用するようDNS設定を変更します。
Producerアカウントに存在するRoute53 ResolverのインバウンドエンドポイントのIPアドレスを確認し、EC2の/etc/resolv.confに反映させます。

sudo vi /etc/resolv.conf
# This is /run/systemd/resolve/resolv.conf managed by man:systemd-resolved(8).
# Do not edit.
#
# This file might be symlinked as /etc/resolv.conf. If you're looking at
# /etc/resolv.conf and seeing this text, you have followed the symlink.
#
# This is a dynamic resolv.conf file for connecting local clients directly to
# all known uplink DNS servers. This file lists all configured search domains.
#
# Third party programs should typically not access this file directly, but only
# through the symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a
# different way, replace this symlink by a static file or a different symlink.
#
# See man:systemd-resolved.service(8) for details about the supported modes of
# operation for /etc/resolv.conf.
# nameserver 172.16.0.2 # 既存の記述はコメントアウト
nameserver 10.0.0.144 # 追加
nameserver 10.0.1.249 # 追加
search ap-northeast-1.compute.internal
反映完了後にも再度digコマンドで確認してみます。
$ dig s3.ap-northeast-1.amazonaws.com
; <<>> DiG 9.18.33 <<>> s3.ap-northeast-1.amazonaws.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 44528
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;s3.ap-northeast-1.amazonaws.com. IN A
;; ANSWER SECTION:
s3.ap-northeast-1.amazonaws.com. 60 IN A 10.0.0.218
s3.ap-northeast-1.amazonaws.com. 60 IN A 10.0.1.62
;; Query time: 0 msec
;; SERVER: 10.0.0.144#53(10.0.0.144) (UDP)
;; WHEN: Wed Feb 12 08:49:00 UTC 2025
;; MSG SIZE rcvd: 92
VPCエンドポイントのプライベート IP アドレスが返却されるようになりました。
動作確認
署名付きURLを発行してConsumer環境のEC2からGETとPUTが行えるかを確認していきます。
署名付きURLはローカル環境で生成します。Producerアカウントの認証情報に切り替えてbuild.shを実行します。
$ ./build.sh get-url
署名付きURLを取得します...
{
"upload_url": "https://presigned-url-demo-provider-bucket-123456789012.s3.ap-northeast-1.amazonaws.com/index.html?....",
"download_url": "https://presigned-url-demo-provider-bucket-123456789012.s3.ap-northeast-1.amazonaws.com/index.html?...."
}
処理が正常に完了しました
EC2から生成されたURLにアクセスすると、Producer環境のS3にアップロードされたindex.htmlの内容が表示されます。
$ curl "https://presigned-url-demo-provider-bucket-123456789012.s3.ap-northeast-1.amazonaws.com/index.html?....",
Hello World
次は/home/ec2-userに作成されているindex.htmlをPUTしてみます。中身は下記の通りです。
$ cat index.html
Hello World !
PUT後に再度GETすると正常にアップロードができたのが確認できました。
$ curl -X PUT -T index.html "https://presigned-url-demo-provider-bucket-123456789012.s3.ap-northeast-1.amazonaws.com/index.html?....",
$ curl "https://presigned-url-demo-provider-bucket-123456789012.s3.ap-northeast-1.amazonaws.com/index.html?....",
Hello World !
解説
Interface型のVPCエンドポイントでプライベートDNSを有効化すると、以下のドメインへのアクセスがVPCエンドポイント経由となります
- s3.ap-northeast-1.amazonaws.com
- *.s3.ap-northeast-1.amazonaws.co
- *.s3-control.ap-northeast-1.amazonaws.com
- *.s3-accesspoint.ap-northeast-1.amazonaws.com
今回は署名付きURLの生成をPythonのboto3で行っており、上記のDNSに多王した署名付きURLを正しく生成するには、以下のように設定が必要です。
s3_client = boto3.client(
"s3",
region_name="ap-northeast-1,
config=Config(s3={"addressing_style": "virtual"}) # この設定が必要
)
# アップロード用URL生成
upload_url = s3_client.generate_presigned_url(
ClientMethod="put_object",
Params={"Bucket": bucket_name, "Key": object_key},
ExpiresIn=expiration,
)
# ダウンロード用URL生成
download_url = s3_client.generate_presigned_url(
ClientMethod="get_object",
Params={"Bucket": bucket_name, "Key": object_key},
ExpiresIn=expiration,
)
この設定により、VPCエンドポイント対応のURL形式(https://bucket-name.s3.ap-northeast-1.amazonaws.com/…)が生成されます。
これらの設定によりRoute53 Resolverと組み合わせることでConsumer環境からInterface型のVPCエンドポイン経由でS3へアクセスすることが可能となります。
また、「インバウンドエンドポイントのためにのみプライベートDNSを有効にする」オプションを使用することで、以下のような柔軟なアクセス制御が可能です。
- VPC内からのS3アクセス → Gateway型エンドポイント経由
- オンプレミスからのS3アクセス → Interface型エンドポイント経由

環境の削除
検証完了後は、以下の順序で環境を削除します。
$ ./build.sh destroy consumer
$ ./build.sh destroy provider
さいごに
今回は、インターネットに接続できない環境からAWS Private LinkとRoute53 Resolverを利用して、S3の署名付きURLにアクセスする方法について解説しました。
この構成を応用することで、セキュリティ要件の厳しい環境でも、S3の署名付きURLという便利な機能を使用することができますので、ぜひ活用してみてください。