3 ways to access S3 from private subnet

3 ways to access S3 from private subnet

3 ways to access S3 from private subnet

There are three ways to access S3 buckets from an EC2 instance in a private subnet.

  • NAT Gateway
  • VPC Endpoint (Gateway Type)
  • VPC Endpoint (interface type)

This page will review how to access S3 buckets via the above three.

Environment

Diagram of 3 ways to access S3 from private subnet.

Create one private subnet in each of the three AZs.

Place an EC2 instance on each subnet.
The instance will be the latest Amazon Linux 2.

Create NAT gateways and gateway/interface type VPC endpoints.
Configure security groups, route tables, etc. to allow each instance to communicate to S3 through them.

This configuration will be built in the ap-northeast-1 (Tokyo) region.

CloudFormation template files

The above configuration is built with CloudFormation.
The CloudFormation templates are placed at the following URL

https://github.com/awstut-an-r/awstut-saa/tree/main/03/003

Explanation of key points of template files

NAT Gateway

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)

Create a NAT gateway on the public subnet.
Configure the route table as follows so that instances on the private subnet can communicate with the S3 bucket via the NAT gateway.

  • Route table for private subnets: set default route for NAT Gateway
  • Route table for public subnets: set default route for Internet Gateway

VPC Endpoints

Gateway Type

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)

To create a gateway type endpoint, specify a route table for the RouteTableIds property.
No special routes need to be specified for the route table.
By associating it with a VPC endpoint, the route will automatically be registered.

Interface Type

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)

When creating an interface type endpoint, specify a security group in the SecurityGroupIds property.
This content allows 443/tcp to be sent from the security group applied to the instance.

When creating a VPC endpoint for S3 of interface type, the PrivateDnsEnabled property is the key.

Amazon S3 interface endpoints do not support the private DNS feature of interface endpoints.

Accessing Amazon S3 interface endpoints

In accordance with the above, this property is set to “false”.

(Reference) S3 bucket

Resources:
  Bucket:
    Type: AWS::S3::Bucket
    Properties:
      AccessControl: Private
      BucketName: !Ref Prefix
Code language: YAML (yaml)

Create an S3 bucket.
No special configuration is required.

(Reference) EC2 instance

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)

Place one EC2 instance on each subnet.

The following are the IAM roles for the instance

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)

The contents allow all actions to the aforementioned S3 bucket.

Architecting

Use CloudFormation to build this environment and check its actual behavior.

Create CloudFormation stacks and check the resources in the stacks

Create CloudFormation stacks.
For information on how to create stacks and check each stack, please refer to the following pages.

https://awstut.com/en/2021/12/11/cloudformations-nested-stack

After reviewing the resources in each stack, information on the main resources created in this case is as follows

  • Instance 1: i-08d2a290c677f6f0a
  • Instance 2: i-0986da9c1408db96d
  • Instance 3: i-0b53eeb09bd833752
  • S3 bucket: saa-03-003
  • NAT gateway: nat-053aa8d5766c63a66
  • Gateway type VPC endpoint: vpce-03a8194a5c16630d4
  • Interface type VPC endpoint: vpce-0c1106da79eb97528

Check each resource from the AWS Management Console.

Check the NAT gateway.

Detail of NAT Gateway.

It has been created successfully.
You can also see that the EIP created by CloudFormation has been successfully attached.

Check the two VPC endpoints.

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

You can indeed see that a gateway type/interface type VPC endpoint has been created.

operation check

Access S3 bucket via NAT Gateway

Now that you are ready, access instance 1.
SSM Session Manager is used for access.

% aws ssm start-session --target i-08d2a290c677f6f0a
...
sh-4.2$
Code language: Bash (bash)

For more information on SSM Session Manager, please refer to the following page.

https://awstut.com/en/2021/12/11/accessing-a-linux-instance-via-ssm-session-manager

After creating the file for testing, check the contents of the bucket.

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)

Indeed, we were able to access the S3 bucket via the NAT gateway.

Access S3 buckets via Gateway Type VPC endpoint

Similarly, use SSM Session Manager to access instance 2.

% aws ssm start-session --target i-0986da9c1408db96d
...
sh-4.2$

sh-4.2$ touch /home/ssm-user/test2.txt
Code language: Bash (bash)

Follow the same procedure as before to check for communication.

There is one caveat when accessing S3 via gateway type VPC endpoints.

AWS CLI を使用して Amazon S3 にリクエストを実行する場合は、デフォルトリージョンをバケットと同じリージョンに設定するか、またはリクエストで –region パラメータを使用します。

Amazon S3 におけるエンドポイント

The official AWS explanation of why a region must be specified is as follows

Traffic that’s destined for the service (Amazon S3 or DynamoDB) in a different Region goes to the internet gateway because prefix lists are specific to a Region.

Gateway endpoints

In other words, if you do not explicitly specify a region, traffic will be routed to Internet Gateway.
However, if the instance is on a private subnet and there is no route to the Internet Gateway, as in this configuration, even if a VPC endpoint is provided, the traffic will not be routed to it and will not be accessible.

Specify the region according to the above.
In this configuration, the region is “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)

We were indeed able to access the S3 bucket via a gateway-type VPC endpoint.

Access S3 buckets via Interface Type VPC endpoint

Similarly, use SSM Session Manager to access instance 3.

% aws ssm start-session --target i-0b53eeb09bd833752
...
sh-4.2$

sh-4.2$ touch /home/ssm-user/test3.txt
Code language: Bash (bash)

Follow the same procedure as before to check for communication.

There are two caveats when accessing S3 via an interface type VPC endpoint.

The first is the need to specify the region and endpoint URL.

Use the –region and –endpoint-url parameters to access S3 buckets, S3 access points, or S3 control APIs through S3 interface endpoints.

Accessing buckets and S3 access points from S3 interface endpoints

Second, the value specified in the endpoint URL is the endpoint-specific S3 DNS name.

you can find the DNS name of a VPC endpoint. In this example, the VPC endpoint ID (vpce-id) is vpce-0e25b8cdd720f900e and the DNS name is *.vpce-0e25b8cdd720f900e-argc85vg.s3.us-east-1.vpce.amazonaws.com. Remember to replace * when using the DNS name. For example, to access a bucket, use a DNS name like this bucket.vpce-0e25b8cdd720f900e-argc85vg.s3.us-east-1.vpce.amazonaws.com.

Accessing buckets and S3 access points from S3 interface endpoints

Specify the region and endpoint URL according to the above. In this configuration, the region is “ap-northeast-1” and the endpoint URL is “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)

We were indeed able to access the S3 bucket via an interface type VPC endpoint.

summary

We identified three ways to access S3 from a private subnet (NAT gateway, gateway type VPC endpoint, interface type VPC endpoint).