はい、承知いたしました。AWS VPCのゲートウェイ型VPCエンドポイント、特にAmazon S3とDynamoDBへのアクセスに焦点を当て、実務で役立つ情報満載のブログ記事を執筆します。現場のSRE/クラウドアーキテクトの視点から、パケットがどう動くのか、なぜそう動くのかを丁寧に解説し、具体的なコード例や設定例を交えながら、読者の皆さんがすぐにでも実践できるような内容を目指します。
—
イントロダクション:S3/DynamoDBへの「内緒の道」を切り拓く – ゲートウェイ型VPCエンドポイントの真髄
おい、諸君!今日も元気にパケットを追いかけているか? SREとして、あるいはクラウドアーキテクトとして、日々インフラの信頼性と効率性を追求するのは、我々の宿命だ。特にAWSのようなメガクラウドの世界では、VPC(Virtual Private Cloud)は我々の庭であり、そのネットワーク構成を深く理解することは、トラブルシューティングの鬼になるための第一歩と言える。
さて、今日はちょっとマニアックで、それでいて実務ではめちゃくちゃ役立つテーマに切り込んでいくぞ。「ゲートウェイ型VPCエンドポイント」、特にAmazon S3とDynamoDBへのアクセスに特化した話だ。
「え、S3やDynamoDBにアクセスするのに、わざわざVPCエンドポイントなんて使うの?」と思った君、甘い! インターネットゲートウェイ(IGW)を経由してパブリックエンドポイントにアクセスするのが、一番簡単で直感的かもしれない。だが、セキュリティ要件が厳しかったり、ネットワークのレイテンシを最小限に抑えたい、あるいはAWSのマネージドサービスへのアクセスを「プライベートな経路」で完結させたい、なんていう状況は、現場では山ほどあるんだ。
そこで登場するのが、このゲートウェイ型VPCエンドポイントだ。こいつを使うと、なんとインターネットゲートウェイを経由することなく、VPC内のリソースからS3やDynamoDBといったAWSのサービスにアクセスできるようになる。しかも、そのアクセス経路を「プレフィックスリスト」と「ルートテーブル」で制御し、さらに「エンドポイントポリシー」で細かくアクセス権限を管理できるとなれば、もう、君のVPCネットワーク設計の幅は、一気に広がるはずだ。
この記事では、このゲートウェイ型VPCエンドポイントのルーティングの仕組み、通信フロー、そして具体的な設定方法を、現場で培った経験を元に、余すところなく解説していく。RFCの精神に則りつつも、教科書的な説明に終始せず、パケットがどう動くのか、なぜその設定が必要なのかを、君の頭の中に「見える化」させることを目指す。
さあ、覚悟はいいか? 君のVPCネットワークを、さらに一段上のレベルへと引き上げる旅を始めよう!
1. ゲートウェイ型VPCエンドポイントとは何か? – IGWを通らない「近道」の正体
まず、基本から固めていこう。ゲートウェイ型VPCエンドポイントは、AWSが提供するVPCエンドポイントの一種だ。VPCエンドポイントには、大きく分けて「ゲートウェイ型」と「インターフェイス型」の2種類がある。今日は、S3とDynamoDBに適用できる「ゲートウェイ型」に絞って話を進める。
1.1. インターネットゲートウェイ(IGW)の「お約束」と、その課題
普段、VPC内のEC2インスタンスからインターネット上のWebサイトや、S3/DynamoDBといったAWSのパブリックエンドポイントにアクセスする場合、通信は通常、以下のような経路を辿る。
1. EC2インスタンス → プライベートIPアドレス
2. EC2インスタンス → サブネットのルートテーブル → インターネットゲートウェイ(IGW)
3. IGW → インターネット → S3/DynamoDBのエンドポイント
この経路の肝は、インターネットを経由するという点だ。これは、以下のような課題を生む可能性がある。
- セキュリティリスク: インターネットに公開されている経路を通るため、万が一、通信経路やエンドポイント自体に脆弱性があった場合、攻撃に晒されるリスクがある。
- トラフィックの可視性: IGWを通るトラフィックは、VPC内のネットワークフローログでは確認できても、AWSサービスへのアクセスそのものを詳細に制御・監視するのには限界がある。
- レイテンシ: インターネットを経由することで、どうしても若干のレイテンシが発生する。特に、大量のデータをS3にアップロード/ダウンロードする場合などは、無視できない影響が出かねない。
- トラフィックコスト: NATゲートウェイなどを使用している場合、IGW経由でのアウトバウンド通信は、その利用料金が発生する。
1.2. ゲートウェイ型VPCエンドポイントの「魔法」
ここで、ゲートウェイ型VPCエンドポイントが、この課題をどう解決してくれるのか見てみよう。
ゲートウェイ型VPCエンドポイントをVPCに作成すると、AWSはVPCのルートテーブルに、特定のAWSサービス(S3やDynamoDBなど)へのトラフィックを、インターネットゲートウェイではなく、VPCエンドポイントへと向かわせるためのルートを追加する。
つまり、通信フローはこう変わる。
1. EC2インスタンス → プライベートIPアドレス
2. EC2インスタンス → サブネットのルートテーブル → ゲートウェイ型VPCエンドポイント**
3. VPCエンドポイント → AWSネットワーク内部 → S3/DynamoDBのエンドポイント
ここでのポイントは、通信がVPCから出ずに、AWSのプライベートネットワーク内を移動するという点だ。これにより、前述のIGW経由の課題を、以下のように解消できる。
- セキュリティ向上: インターネットを経由しないため、外部からの攻撃リスクを低減できる。また、後述するエンドポイントポリシーにより、アクセス元やアクセス先を細かく制御できる。
- トラフィックの分離: IGWを通らないことで、AWSサービスへのアクセスとインターネットへのアクセスを論理的に分離できる。
- レイテンシ低減: AWSネットワーク内を直接移動するため、インターネット経由よりも一般的にレイテンシが低くなる。
- トラフィックコスト削減: NATゲートウェイなどを介さないため、アウトバウンド通信のコストを削減できる場合がある(※VPCエンドポイント自体の利用料金は発生する)。
「なるほど、便利そうだな!」と思った君、その通りだ。だが、この「近道」を正しく理解し、使いこなすためには、ルーティングの仕組みと、その「門番」となるエンドポイントポリシーについて、しっかりと腹落ちさせる必要がある。
2. ルーティングの秘密:プレフィックスリストとルートテーブルの連携
ゲートウェイ型VPCエンドポイントの核となるのは、ルートテーブルとプレフィックスリスト(またはサービス名)の連携だ。ここを理解しないと、通信がどこへ向かうべきか、パケットが迷子になってしまう。
2.1. プレフィックスリストとは? – AWSサービスIPアドレスの「住所録」
S3やDynamoDBのようなAWSのマネージドサービスは、そのエンドポイントのために、AWSが管理するIPアドレスの範囲を使用している。これらのIPアドレスは、リージョンやサービスによって異なる。
ゲートウェイ型VPCエンドポイントを作成する際、AWSはこれらのIPアドレスの範囲を自動的に取得し、VPCのルートテーブルに静的なルートとして追加する。このIPアドレスのリストを、AWSは「プレフィックスリスト」と呼んでいる。
例えば、com.amazonaws.ap-northeast-1.s3 のようなサービス名でエンドポイントを作成すると、AWSは東京リージョン(ap-northeast-1)のS3サービスが利用するIPアドレスのプレフィックスリストを特定し、VPCのルートテーブルに、これらのIPアドレス群への宛先として、VPCエンドポイントを指すルートを追加してくれるわけだ。
2.2. ルートテーブルの「伝言ゲーム」
VPC内のインスタンスが、S3のバケットにアクセスしようとすると、どうなるか?
1. EC2インスタンスは、宛先IPアドレス(S3のパブリックエンドポイントのIPアドレス)を解決する。
2. パケットは、EC2インスタンスが所属するサブネットに紐づいたルートテーブルに渡される。
3. ルートテーブルは、宛先IPアドレスが、VPCエンドポイントのプレフィックスリストに含まれるIPアドレス範囲に一致するかどうかを確認する。
4. 一致した場合: ルートテーブルは、このパケットをVPCエンドポイントへ転送するよう指示する。
5. 一致しなかった場合: 通常のインターネットゲートウェイ(IGW)やNATゲートウェイなど、定義されている別のルートに従って転送される。
この「一致した場合」のルートこそが、ゲートウェイ型VPCエンドポイントの肝だ。
ルートテーブルの設定例(イメージ)
VPCエンドポイントを作成すると、ルートテーブルには以下のようなルートが自動的に追加される(これはあくまでイメージであり、実際の表示とは異なる場合があります)。
| Type | Destination | Target | Description |
| :——- | :——————- | :————————————- | :—————————————– |
| Prefix List | pl-xxxxxxxxxxxxxxxxx | vpce-xxxxxxxxxxxxxxxxx | S3 (ap-northeast-1) へのプレフィックスリスト |
| Gateway | 0.0.0.0/0 | igw-xxxxxxxxxxxxxxxxx | インターネットへのデフォルトルート |
ここで重要なのは、pl-xxxxxxxxxxxxxxxxx がS3(ap-northeast-1)のIPアドレス範囲を指しており、その宛先が vpce-xxxxxxxxxxxxxxxxx、つまり作成したVPCエンドポイントになっていることだ。これにより、S3への通信は、IGWを通らずにVPCエンドポイントへと誘導される。
2.3. 通信フロー(シーケンス)の深掘り
もう少し具体的に、EC2インスタンスからS3バケットへのPUTリクエストを例に、通信フローを見てみよう。
1. DNS解決:
- EC2インスタンスは、
my-bucket.s3.ap-northeast-1.amazonaws.comのIPアドレスをDNSサーバーに問い合わせる。 - VPC内のDNS解決(Route 53 Resolverなど)が有効であれば、AWSのDNSインフラストラクチャは、VPCエンドポイントによって解決されるIPアドレスを返す。これは、S3のパブリックIPアドレスとは異なる、VPCエンドポイント専用のIPアドレス(ただし、これはインターフェイス型エンドポイントの話で、ゲートウェイ型の場合は、実際にはIPアドレスの解決は行われず、ルートテーブルのルーティング情報のみが参照される)。
- (訂正)ゲートウェイ型エンドポイントのDNSについて: ゲートウェイ型エンドポイントの場合、DNS解決自体はS3のパブリックエンドポイントのIPアドレスを返します。しかし、VPCのルートテーブルにVPCエンドポイントへのルートが設定されているため、パケットはIGWではなくVPCエンドポイントへ向かう、という挙動になります。インターフェイス型エンドポイントの場合は、VPC内のENIに紐づくIPアドレスが返されます。この違いは重要です。
2. パケット生成:
- EC2インスタンスは、取得した(またはルーティング情報として参照した)宛先情報に基づき、TCPパケットを生成する。送信元IPアドレスはEC2インスタンスのプライベートIP、宛先IPアドレスはS3エンドポイントのIPアドレスとなる。
3. ルーティング決定:
- パケットはEC2インスタンスのサブネットのルートテーブルに渡される。
- ルートテーブルは、宛先IPアドレス(S3のIPアドレス)が、VPCエンドポイントに紐づいたプレフィックスリストの範囲内にあることを認識する。
- ルートテーブルは、このパケットをVPCエンドポイントへと転送するよう指示する。
4. VPCエンドポイントへの転送:
- パケットは、VPCエンドポイントへとルーティングされる。この時点では、まだVPCのネットワーク内だ。
5. AWSネットワーク内部でのルーティング:
- VPCエンドポイントは、AWSの内部ネットワーク(バックボーンネットワーク)に接続されている。
- エンドポイントは、パケットをS3サービスへとルーティングする。この際、通信はAWSのプライベートネットワーク内で行われるため、インターネットには出ない。
6. S3サービスでの処理:
- S3サービスはリクエストを受け取り、処理を行う。
- レスポンスパケットも、同様にAWSネットワーク内部を通り、VPCエンドポイントを経由して、EC2インスタンスへと返される。
この一連の流れから、ゲートウェイ型VPCエンドポイントは、あくまでルートテーブルのルーティング情報を操作することで、通信経路を「書き換えている」のだと理解できるだろう。
3. エンドポイントポリシー:アクセス制御の「番犬」
さて、通信経路をプライベートにできたところで、次に気になるのは「誰が、何にアクセスできるのか」という制御だ。ここで活躍するのが、「エンドポイントポリシー」だ。
ゲートウェイ型VPCエンドポイントを作成する際に、このポリシーをアタッチすることができる。これは、IAMポリシーに似たJSON形式で記述され、VPCエンドポイントを介して、S3やDynamoDBに対してどのような操作を許可/拒否するかを細かく定義する。
3.1. ポリシーの基本構造
エンドポイントポリシーは、IAMポリシーと同様に、Statement の配列を持ち、各 Statement は Effect(Allow/Deny)、Principal(誰が)、Action(何をするか)、Resource(どのリソースに対して)といった要素で構成される。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowS3ReadAccessToSpecificBucket",
"Effect": "Allow",
"Principal": "*", // または特定のIAMロール/ユーザーを指定
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-specific-bucket",
"arn:aws:s3:::my-specific-bucket/*"
]
},
{
"Sid": "DenyAllExceptSpecificBucket",
"Effect": "Deny",
"NotPrincipal": "*", // 特定のPrincipal以外を対象にする場合
"Action": "s3:*",
"Resource": "*" // すべてのS3リソース
}
]
}
重要なポイント:
- Principal:
*を指定すると、VPC内のすべてのプリンシパル(IAMロールやユーザー)からのアクセスを対象とする。特定のIAMロールやユーザーを指定することも可能だが、ゲートウェイ型エンドポイントでは、通常、VPC内のリソース(EC2インスタンスなど)からアクセスするため、*で構わない場合が多い。 - Action: S3やDynamoDBのAPIアクションを指定する。例えば、
s3:GetObject、s3:PutObject、dynamodb:GetItemなど。 - Resource: アクセスを許可/拒否するリソースを指定する。S3の場合はバケットARN、DynamoDBの場合はテーブルARNを指定する。
*vsarn:aws:s3:::my-specific-bucket/*: S3の場合、バケット自体への操作(ListBucketなど)と、バケット内のオブジェクトへの操作(GetObject、PutObjectなど)でResourceの指定方法が異なる点に注意が必要だ。/*を付けないと、バケット内のオブジェクトへの操作は対象外になる。
3.2. 実践的なポリシー例
例1:特定のS3バケットへの読み取り専用アクセスを許可
VPC内のインスタンスから、特定のS3バケット my-app-data-bucket に対して、オブジェクトの読み取り (GetObject) と、バケット内のオブジェクト一覧の取得 (ListBucket) のみを許可したい場合。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowReadOnlyAccessToMyAppDataBucket",
"Effect": "Allow",
"Principal": "*", // VPC内のどのリソースからでも
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-app-data-bucket", // バケット自体へのListBucket用
"arn:aws:s3:::my-app-data-bucket/*" // バケット内のオブジェクトへのGetObject用
]
}
]
}
例2:特定のDynamoDBテーブルへの読み書きアクセスを許可
VPC内のインスタンスから、DynamoDBテーブル user-profiles に対して、アイテムの取得 (GetItem) と更新 (PutItem) のみを許可したい場合。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowGetPutToUserProfilesTable",
"Effect": "Allow",
"Principal": "*", // VPC内のどのリソースからでも
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem"
],
"Resource": "arn:aws:dynamodb:ap-northeast-1:123456789012:table/user-profiles" // DynamoDBテーブルARN
}
]
}
例3:他のすべてのS3バケットへのアクセスを明示的に拒否
上記のような「許可」ポリシーと組み合わせて、意図しないS3バケットへのアクセスをブロックするために、以下のような「拒否」ポリシーを追加することが考えられる。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyAllS3AccessExceptMySpecificBucket",
"Effect": "Deny",
"Principal": "*", // VPC内のすべてのプリンシパル
"Action": "s3:*", // すべてのS3アクション
"Resource": [
"arn:aws:s3:::*", // すべてのS3バケット
"arn:aws:s3:::*/*" // すべてのS3オブジェクト
],
"Condition": {
"StringNotEquals": {
"s3:ResourceAccount": "123456789012" // 自分のAWSアカウントID
},
"ArnNotLike": {
"aws:SourceArn": [ // 特定のVPCエンドポイントARNを除外 (この例は少し高度)
"arn:aws:s3:::my-app-data-bucket",
"arn:aws:s3:::my-app-data-bucket/*"
]
}
}
}
]
}
注意: 上記のDenyポリシーは、s3:ResourceAccount や aws:SourceArn のような条件を組み合わせることで、より厳密な制御が可能になります。しかし、ゲートウェイ型エンドポイントでは aws:SourceArn による制御は限定的であり、実際には、個別の許可ポリシーを最小権限で設定し、それ以外のアクセスはデフォルトで拒否する、という考え方が基本となります。
3.3. ポリシー評価の順序
IAMポリシーと同様に、エンドポイントポリシーも評価順序があります。
1. 明示的な拒否 (Deny) が最優先: どのポリシーであっても、明示的に「Deny」と書かれていれば、そのリクエストは拒否されます。
2. 明示的な許可 (Allow): Deny がない場合、Allow ポリシーがあれば、そのリクエストは許可されます。
3. デフォルトの拒否: どのポリシーにも該当しない場合は、デフォルトで拒否されます。
つまり、エンドポイントポリシーは、VPCエンドポイントを介したAWSサービスへのアクセスを「門番」のように監視し、許可された操作のみを通す役割を担っているのです。
4. 実践!コードと設定例
理論は十分だろう。ここからは、実際に手を動かして、ゲートウェイ型VPCエンドポイントを構築し、利用する方法を見ていこう。
4.1. VPCエンドポイントの作成(AWS CLI)
AWS CLIを使って、S3とDynamoDBのゲートウェイ型VPCエンドポイントを作成してみよう。
# 変数定義
VPC_ID="vpc-xxxxxxxxxxxxxxxxx" # 対象のVPC ID
REGION="ap-northeast-1" # 対象のリージョン
# S3ゲートウェイ型VPCエンドポイントの作成
aws ec2 create-vpc-endpoint \
--vpc-id $VPC_ID \
--service-name com.amazonaws.$REGION.s3 \
--vpc-endpoint-type Gateway \
--tag-specifications 'ResourceType=vpc-endpoint,Tags=[{Key=Name,Value=MyS3GatewayEndpoint}]' \
--region $REGION
# DynamoDBゲートウェイ型VPCエンドポイントの作成
aws ec2 create-vpc-endpoint \
--vpc-id $VPC_ID \
--service-name com.amazonaws.$REGION.dynamodb \
--vpc-endpoint-type Gateway \
--tag-specifications 'ResourceType=vpc-endpoint,Tags=[{Key=Name,Value=MyDynamoDBGatewayEndpoint}]' \
--region $REGION
解説:
--vpc-id: エンドポイントを作成するVPCを指定します。--service-name: 作成したいAWSサービスのサービス名を指定します。com.amazonaws.<リージョン>.<サービス名>という形式です。--vpc-endpoint-type Gateway: ゲートウェイ型であることを明示します。--tag-specifications: 作成するリソースにタグを付けます。後で管理しやすくなります。
エンドポイント作成後、しばらく待つと available 状態になります。
4.2. ルートテーブルへのルート追加(AWS CLI)
エンドポイントが作成されたら、VPCエンドポイントが作成されたサブネットが紐づいているルートテーブルに、エンドポイントへのルートが自動的に追加されていることを確認する。
まず、作成したエンドポイントのIDを確認する。
aws ec2 describe-vpc-endpoints \
--filters "Name=vpc-id,Values=$VPC_ID" "Name=service-name,Values=com.amazonaws.$REGION.s3" \
--query "VpcEndpoints[0].VpcEndpointId" --output text --region $REGION
# 出力例: vpce-xxxxxxxxxxxxxxxxx (S3用)
aws ec2 describe-vpc-endpoints \
--filters "Name=vpc-id,Values=$VPC_ID" "Name=service-name,Values=com.amazonaws.$REGION.dynamodb" \
--query "VpcEndpoints[0].VpcEndpointId" --output text --region $REGION
# 出力例: vpce-yyyyyyyyyyyyyyyyy (DynamoDB用)
次に、対象のルートテーブルIDを確認する。
# 対象サブネットが属するルートテーブルIDを取得
aws ec2 describe-route-tables \
--filters "Name=association.vpc-id,Values=$VPC_ID" \
--query "RouteTables[?Associations[?SubnetId=='subnet-xxxxxxxxxxxxxxxxx']].RouteTableId" --output text --region $REGION
# subnet-xxxxxxxxxxxxxxxxx は、エンドポイントを利用したいEC2インスタンスが配置されているサブネットID
# 出力例: rtb-zzzzzzzzzzzzzzzzz
そして、ルートテーブルにプレフィックスリストのルートを追加する。
# S3用プレフィックスリストIDを取得
S3_PREFIX_LIST_ID=$(aws ec2 describe-prefix-lists \
--filters "Name=prefix-list-name,Values=com.amazonaws.$REGION.s3" \
--query "PrefixLists[0].PrefixListId" --output text --region $REGION)
# DynamoDB用プレフィックスリストIDを取得
DDB_PREFIX_LIST_ID=$(aws ec2 describe-prefix-lists \
--filters "Name=prefix-list-name,Values=com.amazonaws.$REGION.dynamodb" \
--query "PrefixLists[0].PrefixListId" --output text --region $REGION)
# ルートテーブルにS3エンドポイントへのルートを追加
aws ec2 create-route \
--route-table-id rtb-zzzzzzzzzzzzzzzzz \
--destination-prefix-list-id $S3_PREFIX_LIST_ID \
--vpc-endpoint-id vpce-xxxxxxxxxxxxxxxxx \
--region $REGION
# ルートテーブルにDynamoDBエンドポイントへのルートを追加
aws ec2 create-route \
--route-table-id rtb-zzzzzzzzzzzzzzzzz \
--destination-prefix-list-id $DDB_PREFIX_LIST_ID \
--vpc-endpoint-id vpce-yyyyyyyyyyyyyyyyy \
--region $REGION
解説:
describe-prefix-lists: サービス名からプレフィックスリストIDを取得します。create-route: ルートテーブルに新しいルートを追加します。--destination-prefix-list-id: 宛先としてプレフィックスリストIDを指定します。--vpc-endpoint-id: 転送先としてVPCエンドポイントIDを指定します。
4.3. エンドポイントポリシーの設定(AWS CLI + JSONファイル)
エンドポイントポリシーを設定するには、JSONファイルを作成し、それをAWS CLIで指定します。
まず、ポリシーを記述したJSONファイルを用意します。例えば、s3-endpoint-policy.json という名前で保存します。
s3-endpoint-policy.json:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowSpecificBucketAccess",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-app-data-bucket",
"arn:aws:s3:::my-app-data-bucket/*"
]
}
]
}
次に、このポリシーをS3ゲートウェイ型VPCエンドポイントにアタッチします。
# S3エンドポイントID (先ほど取得したもの)
S3_ENDPOINT_ID="vpce-xxxxxxxxxxxxxxxxx"
REGION="ap-northeast-1"
aws ec2 modify-vpc-endpoint \
--vpc-endpoint-id $S3_ENDPOINT_ID \
--add-route-table-ids rtb-zzzzzzzzzzzzzzzzz \ # 必要に応じてルートテーブルIDを指定
--policy-document file://s3-endpoint-policy.json \ # ポリシーファイルを指定
--region $REGION
解説:
modify-vpc-endpoint: 既存のVPCエンドポイントの設定を変更します。--policy-document: JSONファイルパスを指定して、エンドポイントポリシーを設定/更新します。
4.4. VPC内からのアクセス確認(Python + Boto3)
VPC内のEC2インスタンスから、PythonのBoto3ライブラリを使って、S3バケットにアクセスしてみましょう。
import boto3
import botocore
# AWSリージョンを指定
region_name = 'ap-northeast-1'
bucket_name = 'my-app-data-bucket' # エンドポイントポリシーで許可したバケット名
try:
# S3クライアントを作成
# VPCエンドポイント経由でアクセスされるため、明示的にエンドポイントURLを指定する必要はありません
s3 = boto3.client('s3', region_name=region_name)
# バケット内のオブジェクト一覧を取得 (ListBucketアクション)
print(f"Listing objects in bucket: {bucket_name}")
response_list = s3.list_objects_v2(Bucket=bucket_name)
if 'Contents' in response_list:
for obj in response_list['Contents']:
print(f" - {obj['Key']}")
else:
print(" Bucket is empty or no objects found.")
# オブジェクトをPUTする (PutObjectアクション - 必要であれば)
# test-upload.txt という名前でダミーデータをアップロード
object_key = 'test-upload.txt'
file_content = b'This is a test upload from VPC.'
print(f"Uploading object: {object_key} to bucket: {bucket_name}")
s3.put_object(Bucket=bucket_name, Key=object_key, Body=file_content)
print("Upload successful.")
# オブジェクトをGETする (GetObjectアクション)
print(f"Getting object: {object_key} from bucket: {bucket_name}")
response_get = s3.get_object(Bucket=bucket_name, Key=object_key)
content = response_get['Body'].read()
print(f" Content: {content.decode('utf-8')}")
print("Successfully accessed S3 bucket via VPC endpoint.")
except botocore.exceptions.ClientError as e:
error_code = e.response.get('Error', {}).get('Code')
error_message = e.response.get('Error', {}).get('Message')
print(f"An error occurred: {error_code} - {error_message}")
if error_code == 'AccessDenied':
print("Access Denied. Check your VPC endpoint policy and IAM permissions.")
except Exception as e:
print(f"An unexpected error occurred: {e}")
解説:
- Boto3は、AWS SDK for Pythonです。VPC内のEC2インスタンスにインストールしておけば、簡単にAWSサービスと連携できます。
boto3.client('s3', region_name=region_name): S3クライアントを作成します。- 重要な点: VPCエンドポイントが正しく設定されていれば、特別な設定をしなくても、Boto3(や他のAWS SDK、AWS CLI)は自動的にVPCエンドポイントを経由してS3にアクセスしようとします。これは、SDKが内部的にルーティング情報を参照し、適切なエンドポイント(この場合はVPCエンドポイント)を使用するように動作するためです。
list_objects_v2,put_object,get_object: S3の基本的なAPIアクションです。これらのアクションがエンドポイントポリシーで許可されていることを確認してください。- エラーハンドリング:
ClientErrorをキャッチすることで、アクセス拒否などのAWS固有のエラーを詳細に確認できます。
5. トラブルシューティングの勘所
ゲートウェイ型VPCエンドポイントは強力ですが、設定ミスによるトラブルもつきものです。現場でよく遭遇する問題とその解決策をいくつか紹介しよう。
5.1. 「アクセス拒否」エラーが頻発する!
- 確認ポイント1:エンドポイントポリシー:
- JSONファイルに構文エラーはないか?(AWS CLIで
modify-vpc-endpointを実行する前に、aws ec2 describe-vpc-endpoints --vpc-endpoint-id <ID>で現在のポリシーを確認できます) Principalは適切か?(通常*で良いが、意図しないプリンシパルからのアクセスを制限したい場合は注意)Actionは操作したいAPIアクションを網羅しているか?(s3:GetObjectだけ許可してs3:PutObjectを忘れていないか?)ResourceのARN指定は正しいか?(バケット名、リージョン、アカウントID、/*の有無など)- 許可ポリシーと拒否ポリシーの競合: 複数のポリシーが設定されている場合、評価順序(Deny最優先)を考慮する。
- 確認ポイント2:IAMポリシー:
- EC2インスタンスにアタッチされているIAMロールに、S3やDynamoDBへのアクセス権限が付与されているか?
- エンドポイントポリシーは「VPCエンドポイントを介したアクセス」を制御するもので、IAMポリシーは「AWSアカウント内のリソースへのアクセス」を制御します。両方の許可が必要です。
- 確認ポイント3:ルートテーブル:
- EC2インスタンスが所属するサブネットのルートテーブルに、S3/DynamoDBへのプレフィックスリストのルートが正しく追加されているか?
- ルートの
TargetがVPCエンドポイントIDになっているか? - ターゲットとなったVPCエンドポイントは
available状態か?
5.2. 通信ができない(タイムアウトなど)
- 確認ポイント1:ルートテーブル:
- インターネットゲートウェイ(IGW)へのルートが、S3/DynamoDBのプレフィックスリストのルートよりも優先順位が低くなっているか?(プレフィックスリストのルートがなければ、IGWへ向かってしまう)
- VPCエンドポイント自体が
available状態か?
- 確認ポイント2:ネットワークACL(NACL)とセキュリティグループ:
- VPCエンドポイントは、AWSネットワーク上に存在するため、NACLやセキュリティグループでは直接制御できません。しかし、VPCエンドポイントを利用するEC2インスタンス側のセキュリティグループは、S3/DynamoDBのエンドポイント(パブリックIP)との通信を許可している必要があります。
- (補足) ゲートウェイ型エンドポイントの場合、通信はVPC内からAWSネットワークへ向かうため、EC2インスタンスのセキュリティグループは、S3/DynamoDBのパブリックIPアドレス範囲(これはプレフィックスリストで定義されているIP群)との通信を許可する必要があります。インターフェイス型エンドポイントの場合は、エンドポイントに割り当てられたENIのIPアドレスとの通信を許可します。
- 確認ポイント3:DNS解決:
- VPC内のDNS解決が正しく機能しているか?
- (前述の通り、ゲートウェイ型ではS3/DynamoDBのパブリックIPが返されるが、そのIPへのルーティングがエンドポイントに向かっていることが重要)
5.3. デバッグのヒント
- VPCフローログ: VPCフローログを有効にし、S3/DynamoDBのエンドポイントIPアドレスへのトラフィックが、IGWではなくVPCエンドポイントへ向かっているか、また、パケットがドロップされていないかを確認する。
- AWS CLI/SDKのデバッグ出力: Boto3などのSDKは、通信の詳細を出力するデバッグオプションを持っている場合があります。
traceroute: VPC内のインスタンスからS3のエンドポイントIPアドレスに対してtracerouteを実行し、どこまで到達しているかを確認する。(ただし、UDPポートがブロックされている場合など、正確な経路が表示されないこともあります)
6. まとめ:ゲートウェイ型VPCエンドポイントで、ネットワークを「賢く」する
さて、ここまでゲートウェイ型VPCエンドポイントのルーティング、通信フロー、エンドポイントポリシー、そして実践的な設定方法とトラブルシューティングの勘所まで、じっくりと見てきた。
インターネットゲートウェイを経由せずに、VPC内のリソースからS3やDynamoDBといったAWSのマネージドサービスへアクセスできるこの仕組みは、セキュリティ、パフォーマンス、そしてコスト効率の観点から、非常に強力な武器となる。
- ルーティング: ルートテーブルとプレフィックスリストの連携により、通信経路をVPCネットワーク内部へと「書き換える」。
- ポリシー制御: エンドポイントポリシーにより、VPCエンドポイントを介したアクセスを、きめ細かく制御する。
これらの要素を組み合わせることで、君のVPCネットワークは、より安全で、より効率的で、そして何よりも「賢く」なるはずだ。
もちろん、設定を間違えればパケットは迷子になり、アクセス拒否のエラーに悩まされることになる。だが、今回解説したポイントをしっかりと押さえておけば、そんなトラブルも怖くない。むしろ、それを乗り越えることで、君のSRE/クラウドアーキテクトとしてのスキルは、さらに磨かれていくだろう。
ぜひ、この知識を実際のインフラ設計や運用に活かしてみてほしい。君たちのVPCネットワークが、さらに堅牢で効率的なものになることを願っている!
それでは、また次の現場で会おう!Happy Networking!
コメント