プライベートサブネットからS3にアクセスする3つの方法

目次

プライベートサブネットからS3にアクセスする3つの方法

プライベートサブネット内のEC2インスタンスから、S3バケットにアクセスするための方法は、以下の3つがあります。

  • NATゲートウェイ
  • VPC Endpoint(ゲートウェイタイプ)
  • VPC Endpoint(インターフェイスタイプ)

本ページでは、上記の3つを経由してS3バケットにアクセスする方法を確認します。

構築する環境

Diagram of 3 ways to access S3 from private subnet.

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スタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。

あわせて読みたい
CloudFormationのネストされたスタックで環境を構築する 【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ゲートウェイを確認します。

Detail of NAT Gateway.

正常に作成されています。
CloudFormationで作成したEIPが正常にアタッチされていることもわかります。

2つのVPCエンドポイントを確認します。

Detail of VPC Endpoint 1.
Detail of VPC Endpoint 2.

確かに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の詳細につきましては、以下のページをご確認ください。

あわせて読みたい
LinuxインスタンスにSSM Session Manager経由でアクセスする 【LinuxインスタンスにSSM Session Manager経由でアクセスする】 EC2インスタンスにSSM Session Manager経由でアクセスする構成を確認します。 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エンドポイント)を確認しました。

目次