プライベートサブネットからS3にアクセスする3つの方法
プライベートサブネット内のEC2インスタンスから、S3バケットにアクセスするための方法は、以下の3つがあります。
- NATゲートウェイ
- VPC Endpoint(ゲートウェイタイプ)
- VPC Endpoint(インターフェイスタイプ)
本ページでは、上記の3つを経由してS3バケットにアクセスする方法を確認します。
構築する環境
3つのAZに1つずつプライベートサブネットを作成します。
各サブネットにEC2インスタンスを配置します。
インスタンスは最新のAmazon Linux 2とします。
NATゲートウェイ、ゲートウェイ/インターフェイスタイプのVPCエンドポイントを作成します。
各インスタンスにそれらを通じてS3向けに通信できるように、セキュリティグループやルートテーブル等を設定します。
なお今回の構成はap-northeast-1(東京)リージョンで構築します。
CloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。
https://github.com/awstut-an-r/awstut-saa/tree/main/03/003
テンプレートファイルのポイント解説
NATゲートウェイ
Resources:
IGW:
Type: AWS::EC2::InternetGateway
IGWAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref IGW
EIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
NATGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt EIP.AllocationId
SubnetId: !Ref PublicSubnet
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: !Ref CidrIp1
VpcId: !Ref VPC
AvailabilityZone: !Sub "${AWS::Region}${AvailabilityZone1}"
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: !Ref CidrIp2
VpcId: !Ref VPC
AvailabilityZone: !Sub "${AWS::Region}${AvailabilityZone1}"
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
RouteToInternet:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref IGW
PublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet
RouteTableId: !Ref PublicRouteTable
PrivateRouteTable1:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
RouteToNATGateway:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable1
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NATGateway
PrivateSubnetRouteTableAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet1
RouteTableId: !Ref PrivateRouteTable1
Code language: YAML (yaml)
パブリックサブネット上にNATゲートウェイを作成します。
プライベートサブネット上のインスタンスが、NATゲートウェイを経由してS3バケットと通信できるように、ルートテーブルを以下の通りに設定します。
- プライベートサブネット用のルートテーブル:NATゲートウェイ向けにデフォルトルートを設定
- パブリックサブネット用のルートテーブル:インターネットゲートウェイ向けにデフォルトルートを設定
VPCエンドポイント
ゲートウェイタイプ
Resources:
GatewayS3Endpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
RouteTableIds:
- !Ref PrivateRouteTable2
ServiceName: !Sub "com.amazonaws.${AWS::Region}.s3"
VpcEndpointType: Gateway
VpcId: !Ref VPC
PrivateRouteTable2:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Code language: YAML (yaml)
ゲートウェイタイプのエンドポイントを作成する場合は、RouteTableIdsプロパティにルートテーブルを指定します。
ルートテーブルは特別なルートは指定する必要はありません。
VPCエンドポイントに関連づけることで、自動的にルートが登録されます。
インターフェイスタイプ
Resources:
InterfaceS3Endpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
PrivateDnsEnabled: false
SecurityGroupIds:
- !Ref EndpointSecurityGroup
ServiceName: !Sub "com.amazonaws.${AWS::Region}.s3"
SubnetIds:
- !Ref PrivateSubnet3
VpcEndpointType: Interface
VpcId: !Ref VPC
EndpointSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "${Prefix}-EndpointSecurityGroup"
GroupDescription: Allow HTTPS from InstanceSecurityGroup.
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: !Ref HTTPSPort
ToPort: !Ref HTTPSPort
SourceSecurityGroupId: !Ref InstanceSecurityGroup
Code language: YAML (yaml)
インターフェイスタイプのエンドポイントを作成する場合は、SecurityGroupIdsプロパティにセキュリティグループを指定します。
インスタンスに適用したセキュリティグループを送信元とする443/tcpを許可する内容です。
インターフェイスタイプのS3用VPCエンドポイントを作成する場合、PrivateDnsEnabledプロパティがポイントです。
Amazon S3 インターフェイスエンドポイントは、インターフェイスエンドポイントのプライベート DNS 機能をサポートしていません。
Amazon S3 インターフェイスエンドポイントへのアクセス
上記に従い、本プロパティを「false」とします。
(参考) S3バケット
Resources:
Bucket:
Type: AWS::S3::Bucket
Properties:
AccessControl: Private
BucketName: !Ref Prefix
Code language: YAML (yaml)
S3バケットを作成します。
特別な設定は不要です。
(参考) EC2インスタンス
Resources:
Instance1:
Type: AWS::EC2::Instance
Properties:
IamInstanceProfile: !Ref InstanceProfile
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
NetworkInterfaces:
- DeviceIndex: 0
SubnetId: !Ref PrivateSubnet1
GroupSet:
- !Ref InstanceSecurityGroup
Instance2:
Type: AWS::EC2::Instance
Properties:
IamInstanceProfile: !Ref InstanceProfile
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
NetworkInterfaces:
- DeviceIndex: 0
SubnetId: !Ref PrivateSubnet2
GroupSet:
- !Ref InstanceSecurityGroup
Instance3:
Type: AWS::EC2::Instance
Properties:
IamInstanceProfile: !Ref InstanceProfile
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
NetworkInterfaces:
- DeviceIndex: 0
SubnetId: !Ref PrivateSubnet3
GroupSet:
- !Ref InstanceSecurityGroup
Code language: YAML (yaml)
各サブネットにEC2インスタンスを1台配置します。
以下がインスタンス用のIAMロールです。
Resources:
InstanceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- ec2.amazonaws.com
Policies:
- PolicyName: AccessS3Policy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- s3:*
Resource:
- !Ref BucketArn
- !Sub "${BucketArn}/*"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
Code language: YAML (yaml)
先述のS3バケットへの全アクションを許可する内容です。
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。
各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。
- インスタンス1:i-08d2a290c677f6f0a
- インスタンス2:i-0986da9c1408db96d
- インスタンス3:i-0b53eeb09bd833752
- S3バケット:saa-03-003
- NATゲートウェイ:nat-053aa8d5766c63a66
- ゲートウェイタイプのVPCエンドポイント:vpce-03a8194a5c16630d4
- インターフェイスタイプのVPCエンドポイント:vpce-0c1106da79eb97528
AWS Management Consoleから各リソースを確認します。
NATゲートウェイを確認します。
正常に作成されています。
CloudFormationで作成したEIPが正常にアタッチされていることもわかります。
2つのVPCエンドポイントを確認します。
確かにS3向けにゲートウェイタイプ/インターフェイスタイプのVPCエンドポイントが作成されていることがわかります。
動作確認
NATゲートウェイを経由してS3バケットにアクセス
準備が整いましたので、インスタンス1にアクセスします。
アクセスはSSM Session Managerを使用します。
% aws ssm start-session --target i-08d2a290c677f6f0a
...
sh-4.2$
Code language: Bash (bash)
SSM Session Managerの詳細につきましては、以下のページをご確認ください。
テスト用のファイルを作成後、バケットの中身を確認します。
sh-4.2$ touch /home/ssm-user/test1.txt
sh-4.2$ aws s3 cp /home/ssm-user/test1.txt s3://saa-03-003
upload: /home/ssm-user/test1.txt to s3://saa-03-003/test1.txt
sh-4.2$ aws s3 ls s3://saa-03-003
2023-01-23 13:03:01 0 test1.txt
Code language: Bash (bash)
確かにNATゲートウェイ経由で、S3バケットにアクセスすることができました。
ゲートウェイタイプのVPCエンドポイントを経由してS3バケットにアクセス
同様にSSM Session Managerを使用して、インスタンス2にアクセスします。
% aws ssm start-session --target i-0986da9c1408db96d
...
sh-4.2$
sh-4.2$ touch /home/ssm-user/test2.txt
Code language: Bash (bash)
先ほどと同様の手順で疎通を確認します。
ゲートウェイタイプのVPCエンドポイントを経由してS3にアクセスする場合は、注意点が1つあります。
AWS CLI を使用して Amazon S3 にリクエストを実行する場合は、デフォルトリージョンをバケットと同じリージョンに設定するか、またはリクエストで –region パラメータを使用します。
Amazon S3 におけるエンドポイント
なおリージョンを指定しなければならない理由について、AWS公式では以下の通りに説明しています。
同じリージョンでこの AWS のサービスを宛先とするサブネットからのトラフィックはエンドポイントに移動し、インターネットゲートウェイには移動しません。他のすべてのインターネットトラフィック (他のサービスを宛先とするトラフィックや、他のリージョンの AWS のサービスを宛先とするトラフィックなど) は、インターネットゲートウェイに移動します。
ゲートウェイ VPC エンドポイント
つまりリージョンを明示的に指定しない場合は、トラフィックがインターネットゲートウェイ向けにルーティングされることになります。
ただ今回の構成のようにプライベートサブネットにインスタンスがあり、かつインターネットゲートウェイへのルートが存在しない場合は、たとえVPCエンドポイントが用意されていたとしても、そちらにトラフィックがルーティングされないため、アクセス不可となります。
上記に従い、リージョンを指定します。
今回の構成では、リージョンは「ap-northeast-1」です。
sh-4.2$ aws s3 cp /home/ssm-user/test2.txt s3://saa-03-003 --region ap-northeast-1
upload: /home/ssm-user/test2.txt to s3://saa-03-003/test2.txt
sh-4.2$ aws s3 ls s3://saa-03-003 --region ap-northeast-1
2023-01-23 13:03:01 0 test1.txt
2023-01-24 11:03:59 0 test2.txt
Code language: Bash (bash)
確かにゲートウェイタイプのVPCエンドポイント経由で、S3バケットにアクセスすることができました。
インターフェイスタイプのVPCエンドポイントを経由してS3バケットにアクセス
同様にSSM Session Managerを使用して、インスタンス3にアクセスします。
% aws ssm start-session --target i-0b53eeb09bd833752
...
sh-4.2$
sh-4.2$ touch /home/ssm-user/test3.txt
Code language: Bash (bash)
先ほどと同様の手順で疎通を確認します。
インターフェイスタイプのVPCエンドポイントを経由してS3にアクセスする場合は、注意点が2つあります。
1つ目はリージョンとエンドポイントURLを指定する必要があるという点です。
–region および –endpoint-url パラメータを使用して、S3 インターフェイスエンドポイントを介して S3 バケット、S3 アクセスポイント、または S3 コントロール API にアクセスします。
S3 インターフェイスエンドポイントからのバケットおよび S3 アクセスポイントへのアクセス
2つ目はエンドポイントURLに指定する値は、エンドポイント固有のS3 DNS名である点です。
ここでは、VPC エンドポイントの DNS 名を確認できます。この例では、VPC エンドポイント ID (vpce−id) は vpce-0e25b8cdd720f900e で、DNS 名は .vpce-0e25b8cdd720f900e-argc85vg.s3.us-east-1.vpce.amazonaws.com です。DNS 名を使用するときに、 を忘れずに置き換えてください。例えば、バケットにアクセスするには、DNS 名は bucket.vpce-0e25b8cdd720f900e-argc85vg.s3.us-east-1.vpce.amazonaws.com のようになります。
S3 インターフェイスエンドポイントからのバケットおよび S3 アクセスポイントへのアクセス
上記に従い、リージョンおよびエンドポイントURLを指定します。今回の構成では、リージョンは「ap-northeast-1」、エンドポイントURLは「https://bucket.vpce-0c1106da79eb97528-h8nms9fr.s3.ap-northeast-1.vpce.amazonaws.com」です。
sh-4.2$ aws s3 cp /home/ssm-user/test3.txt s3://saa-03-003/ --region ap-northeast-1 --endpoint-url https://bucket.vpce-0c1106da79eb97528-h8nms9fr.s3.ap-northeast-1.vpce.amazonaws.com
upload: ../../home/ssm-user/test3.txt to s3://saa-03-003/test3.txt
sh-4.2$ aws s3 ls s3://saa-03-003/ --region ap-northeast-1 --endpoint-url https://bucket.vpce-0c1106da79eb97528-h8nms9fr.s3.ap-northeast-1.vpce.amazonaws.com
2023-01-23 13:03:01 0 test1.txt
2023-01-24 11:03:59 0 test2.txt
2023-01-24 11:18:26 0 test3.txt
Code language: Bash (bash)
確かにインターフェイスタイプのVPCエンドポイント経由で、S3バケットにアクセスすることができました。
まとめ
プライベートサブネットからS3にアクセスする3つの方法(NATゲートウェイ、ゲートウェイタイプのVPCエンドポイント、インターフェイスタイプのVPCエンドポイント)を確認しました。