Use S3 bucket policy to allow or deny access on an address basis – aws:SourceIp

TOC

Use S3 bucket policy to allow or deny access on an address basis – aws:SourceIp

In the S3 bucket policy, consider setting access restrictions based on global addresses.

In this case, the IP address condition operators (IpAddress, NotIpAddress) can be used.

IP address condition operators let you construct Condition elements that restrict access based on comparing a key to an IPv4 or IPv6 address or range of IP addresses. You use these with the aws:SourceIp key.

IP address condition operators

This time, access the S3 bucket with the bucket policy set from an EC2 instance to check the behavior.

Environment

Diagram of using S3 bucket policy to allow or deny access on address basis - aws:SourceIp

Create four EC2 instances.
The instances will be the latest Amazon Linux 2, two in each of two private subnets.
One instance on each subnet will be given full access to S3 and the other will have no privileges at all.
All instances access the Internet via a NAT Gateway located on the public subnet of each AZ.
This means that when each instance accesses the outside world, it will communicate using the EIP assigned to the NAT Gateway.

Create four buckets.
Set the bucket policy for each as follows

settingBucket 1Bucket 2Bucket 3Bucket 4
ActionAllowDenyAllowDeny
IP address condition operatorsIpAddressIpAddressNotIpAddressNotIpAddress
aws:SourceIpEIP1EIP1EIP1EIP1

Use the IP address condition operator and the aws:SourceIp key to set address-based restrictions.
The target address is the EIP1 assigned to the NAT Gateway.

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-soa/tree/main/04/004

Explanation of key points of template files

S3

Bucket

Resources:
  Bucket1:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub "${Prefix}-bucket-01"

  Bucket2:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub "${Prefix}-bucket-02"

  Bucket3:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub "${Prefix}-bucket-03"
      PublicAccessBlockConfiguration:
        BlockPublicAcls: false
        BlockPublicPolicy: false
        IgnorePublicAcls: false
        RestrictPublicBuckets: false

  Bucket4:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub "${Prefix}-bucket-04"
Code language: YAML (yaml)

Create four buckets.

Only bucket 3 is configured differently from the others.
This is a setting to allow public access.
By default, public access is restricted by the S3 Block Public Access.

However, the bucket policy for bucket 3, described below, would allow access on a wide range, resulting in a configuration inconsistency.
To resolve this inconsistency, disable the public access block feature.

If this setting is not made, the creation of bucket 3 and bucket policy will fail and the following error message will be displayed.

You either don’t have permissions to edit the bucket policy, or your bucket policy grants a level of public access that conflicts with your Block Public Access settings. To edit a bucket policy, you need s3:PutBucketPolicy permissions. To review which Block Public Access settings are turned on, view your account and bucket settings. Learn more about Identity and access management in Amazon S3

Bucket Policy

Check the bucket policies one by one.

Resources:
  BucketPolicy1:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref Bucket1
      PolicyDocument:
        Statement:
          - Principal: "*"
            Action: s3:*
            Effect: Allow
            Resource:
              - !Sub "arn:aws:s3:::${Bucket1}"
              - !Sub "arn:aws:s3:::${Bucket1}/*"
            Condition:
              IpAddress:
                aws:SourceIp:
                  - !Ref EIPPublicIp1
Code language: YAML (yaml)

Bucket policy for bucket 1.
Full access is allowed for access from EIP1.

Resources:
  BucketPolicy2:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref Bucket2
      PolicyDocument:
        Statement:
          - Principal: "*"
            Action: s3:*
            Effect: Deny
            Resource:
              - !Sub "arn:aws:s3:::${Bucket2}"
              - !Sub "arn:aws:s3:::${Bucket2}/*"
            Condition:
              IpAddress:
                aws:SourceIp:
                  - !Ref EIPPublicIp1
Code language: YAML (yaml)

Bucket policy for bucket 2.
Deny all actions if accessed from EIP1.

Resources:
  BucketPolicy3:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref Bucket3
      PolicyDocument:
        Statement:
          - Principal: "*"
            Action: s3:*
            Effect: Allow
            Resource:
              - !Sub "arn:aws:s3:::${Bucket3}"
              - !Sub "arn:aws:s3:::${Bucket3}/*"
            Condition:
              NotIpAddress:
                aws:SourceIp:
                  - !Ref EIPPublicIp1
Code language: YAML (yaml)

Bucket policy for bucket 3.
Full access is allowed when accessing from other than EIP1.

Resources:
  BucketPolicy4:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref Bucket4
      PolicyDocument:
        Statement:
          - Principal: "*"
            Action: s3:*
            Effect: Deny
            Resource:
              - !Sub "arn:aws:s3:::${Bucket4}"
              - !Sub "arn:aws:s3:::${Bucket4}/*"
            Condition:
              NotIpAddress:
                aws:SourceIp:
                  - !Ref EIPPublicIp1
Code language: YAML (yaml)

Bucket policy for bucket 4.
Reject all actions if accessed from other than EIP1.

EC2

Resources:
  Instance1:
    Type: AWS::EC2::Instance
    Properties:
      IamInstanceProfile: !Ref InstanceProfile1
      ImageId: !Ref ImageId
      InstanceType: !Ref InstanceType
      NetworkInterfaces:
        - DeviceIndex: 0
          SubnetId: !Ref InstanceSubnet1
          GroupSet:
            - !Ref InstanceSecurityGroup

  Instance2:
    Type: AWS::EC2::Instance
    Properties:
      IamInstanceProfile: !Ref InstanceProfile2
      ImageId: !Ref ImageId
      InstanceType: !Ref InstanceType
      NetworkInterfaces:
        - DeviceIndex: 0
          SubnetId: !Ref InstanceSubnet1
          GroupSet:
            - !Ref InstanceSecurityGroup

  InstanceProfile1:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: /
      Roles:
        - !Ref InstanceRole1

  InstanceProfile2:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: /
      Roles:
        - !Ref InstanceRole2

  InstanceRole1:
    Type: AWS::IAM::Role
    DeletionPolicy: Delete
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - ec2.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      Policies:
        - PolicyName: AccessS3Policy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action: s3:*
                Resource:
                  - !Sub "arn:aws:s3:::${Bucket1}"
                  - !Sub "arn:aws:s3:::${Bucket1}/*"
                  - !Sub "arn:aws:s3:::${Bucket2}"
                  - !Sub "arn:aws:s3:::${Bucket2}/*"
                  - !Sub "arn:aws:s3:::${Bucket3}"
                  - !Sub "arn:aws:s3:::${Bucket3}/*"
                  - !Sub "arn:aws:s3:::${Bucket4}"
                  - !Sub "arn:aws:s3:::${Bucket4}/*"

  InstanceRole2:
    Type: AWS::IAM::Role
    DeletionPolicy: Delete
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - ec2.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
Code language: YAML (yaml)

We will create four EC2 instances, but we will cover two instances in one subnet.
The remaining instances are only different subnets, as the configuration itself is identical.

The IAM role is the key point.
The IAM role for one instance allows full access to S3.
The IAM role for the other instance will not allow anything regarding S3.

(Reference) NAT Gateway

Resources:
  EIP1:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc

  EIP2:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc

  NATGateway1:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt EIP1.AllocationId
      SubnetId: !Ref PublicSubnet1

  NATGateway2:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt EIP2.AllocationId
      SubnetId: !Ref PublicSubnet2

  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Sub "${AWS::Region}${AvailabilityZone1}"
      CidrBlock: !Ref CidrIp1
      VpcId: !Ref VPC

  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Sub "${AWS::Region}${AvailabilityZone2}"
      CidrBlock: !Ref CidrIp2
      VpcId: !Ref VPC
Code language: YAML (yaml)

Place one NAT Gateway in each of the two AZs.
Attach an EIP to each gateway.
The bucket policy settings described above refer to this EIP1 address.

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 see the following page.

あわせて読みたい
CloudFormation’s nested stack 【How to build an environment with a nested CloudFormation stack】 Examine nested stacks in CloudFormation. CloudFormation allows you to nest stacks. Nested ...

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

  • Bucket 1: soa-04-004-bucket-01
  • Bucket 2: soa-04-004-bucket-02
  • Bucket 3: soa-04-004-bucket-03
  • Bucket 4: soa-04-004-bucket-04
  • Instance 1: i-04dbf87640c773eec
  • Instance 2: i-0d716220234453122
  • Instance 3: i-0ac64c5ec684c0501
  • Instance 4: i-059a4434e92df1367
  • NAT Gateway1: nat-0a3531dbe283cec59
  • NAT Gateway2: nat-0e9a9f6e5a324ac4d
  • EIP1: 35.74.75.17
  • EIP2: 52.194.136.88

Check each resource from the AWS Management Console.

Check the NAT gateway.

Detail of NAT Gateway 1.
Detail of NAT Gateway 2.

Two NAT Gateways have been created.
You can see that each has an EIP attached.

Check the bucket policy.

Detail of S3 1.
Detail of S3 2.
Detail of S3 3.
Detail of S3 4.

Each bucket policy is set as specified in the CloudFormation templates.

Operation Check

Now that we are ready, we can access each instance and check the actual behavior.
The instances are accessed using SSM Session Manager.

For more information on Session Manager, please see the following pages

あわせて読みたい
Accessing Linux instance via SSM Session Manager 【Configure Linux instances to be accessed via SSM Session Manager】 We will check a configuration in which an EC2 instance is accessed via SSM Session Manag...

Instance 1

Access each bucket from instance 1.
The IAM role on instance 1 allows full S3 access.
And when accessing the buckets, EIP1 (35.74.75.17) attached to the NAT Gateway is used.

Access to the bucket is performed via the AWS CLI.

sh-4.2$ aws s3 ls s3://soa-04-004-bucket-01

sh-4.2$ aws s3 ls s3://soa-04-004-bucket-02
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied

sh-4.2$ aws s3 ls s3://soa-04-004-bucket-03

sh-4.2$ aws s3 ls s3://soa-04-004-bucket-04
Code language: Bash (bash)

Bucket 1 was accessible.
This is because the IAM role and bucket policy explicitly allow access.

Bucket 2 was not accessible.
This is because the IAM role allows access but the bucket policy explicitly denies access.

Bucket 3 was accessible.
This is not explicitly allowed in the bucket policy, but is explicitly allowed in the IAM role.

Bucket 4 was accessible.
This is not explicitly allowed in the bucket policy, but is explicitly allowed in the IAM role.

Instance 2

Access each bucket from instance 2.
The IAM role on instance 2 does not allow access to S3.
And when accessing the buckets, EIP1 (35.74.75.17) attached to the NAT Gateway is used.

sh-4.2$ aws s3 ls s3://soa-04-004-bucket-01

sh-4.2$ aws s3 ls s3://soa-04-004-bucket-02
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied

sh-4.2$ aws s3 ls s3://soa-04-004-bucket-03
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied

sh-4.2$ aws s3 ls s3://soa-04-004-bucket-04
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied
Code language: Bash (bash)

Bucket 1 was accessible.
This is because the bucket policy explicitly allows access.

Bucket 2 was not accessible.
This is because the bucket policy explicitly denies access.

Bucket 3 was not accessible.
This is because access is not explicitly allowed by the IAM role and bucket policy.
Access is denied because there is no setting that explicitly allows it.

Bucket 4 was not accessible.
This is because access is not explicitly allowed by the IAM role and bucket policy.
Access is denied because there is no setting that explicitly allows it.

Instance 3

Access each bucket from instance 3.
The IAM role for instance 3 allows full S3 access. Also, when accessing the buckets, EIP2 (52.194.136.88) attached to the NAT Gateway is used.

sh-4.2$ aws s3 ls s3://soa-04-004-bucket-01

sh-4.2$ aws s3 ls s3://soa-04-004-bucket-02

sh-4.2$ aws s3 ls s3://soa-04-004-bucket-03

sh-4.2$ aws s3 ls s3://soa-04-004-bucket-04
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied
Code language: Bash (bash)

Bucket 1 was accessible.
This is not explicitly allowed in the bucket policy, but is explicitly allowed in the IAM role.

Bucket 2 was accessible.
This is because it is not explicitly allowed in the bucket policy, but is explicitly allowed in the IAM role.

Bucket 3 was accessible.
This is because the IAM role and bucket policy explicitly allow access.

Bucket 4 was not accessible.
This is because the IAM role allows access but the bucket policy explicitly denies access.

Instance 4

Access each bucket from instance 4.
The IAM role on instance 4 does not allow access to S3.
And when accessing the buckets, EIP2 (52.194.136.88) attached to the NAT Gateway is used.

sh-4.2$ aws s3 ls s3://soa-04-004-bucket-01
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied

sh-4.2$ aws s3 ls s3://soa-04-004-bucket-02
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied

sh-4.2$ aws s3 ls s3://soa-04-004-bucket-03

sh-4.2$ aws s3 ls s3://soa-04-004-bucket-04
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied
Code language: Bash (bash)

Bucket 1 was not accessible.
This is because access is not explicitly allowed by the IAM role and bucket policy.
Access is denied because there is no setting that explicitly allows it.

Bucket 2 was not accessible.
This is because access is not explicitly allowed by the IAM role and bucket policy.
Access is denied because there is no setting that explicitly allows it.

Bucket 3 was accessible.
This is not explicitly allowed in the IAM role, but is explicitly allowed in the bucket policy.

Bucket 4 was not accessible.
This is because access is explicitly denied by the bucket policy.

Summary

The following is a summary of the results of the operation checks.

InstanceNAT GatewayBucket1 Allow from EIP 1Bucket2 Deny from EIP 1Bucket3 Allow from Not EIP 1Bucket4 Deny from Not EIP 1
Instance1 IAM Role: S3 Full AccessEIP 1OKNGOKOK
Instance2 IAM Role: S3 NoneEIP 1OKNGNGNG
Instance3 IAM Role: S3 Full AccessEIP 2OKOKOKNG
Instance4 IAM Role: S3 NoneEIP 2NGNGOKNG
TOC