DEV Community

josepraveen
josepraveen

Posted on

🔒 Secure Amazon S3 Access using VPC Endpoint and IAM Role (Hands-on AWS Lab)

One of the most common security mistakes in AWS is exposing Amazon S3 buckets to the public. While public access may be useful for testing, production workloads should always access S3 privately through AWS networking and IAM.

In this hands-on lab, we'll start with a publicly accessible S3 bucket, verify that anyone can access its contents, and then progressively secure it using:

  • Amazon S3
  • VPC Gateway Endpoint
  • IAM Role for EC2
  • Bucket Policy with VPC Endpoint restriction

By the end of this tutorial, your S3 bucket will only be accessible from resources inside your VPC.


Architecture

                Internet
                    |
          (Access Denied)
                    X
                    |
         -----------------------
         |     Amazon S3       |
         -----------------------
                    ^
                    |
          Gateway VPC Endpoint
                    |
             ----------------
             |     VPC      |
             |              |
             |   EC2 + IAM  |
             ----------------
Enter fullscreen mode Exit fullscreen mode

Step 1: Create an S3 Bucket

Navigate to Amazon S3 from the AWS Console.

Click Create bucket.

Use the bucket name:

ml-lab-bucket-secure-ml-101
Enter fullscreen mode Exit fullscreen mode

For this lab:

  • Disable Block all public access

accept

  • Accept the warning
  • Create the bucket

At this point the bucket is capable of serving public objects.


Step 2: Allow Public Read Access

Open the bucket and navigate to:

Permissions → Bucket Policy

Paste the following policy.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadAccess",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::ml-lab-bucket-secure-ml-101/*"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

bucket policy

Save the policy.

This policy allows anyone on the internet to download objects from the bucket.


Step 3: Upload a Test File

Open the Objects tab.

Upload:

raw_v1.csv
Enter fullscreen mode Exit fullscreen mode

upload

After uploading:

  1. Open the object.
  2. Copy its Object URL.
  3. Paste it into a browser.

The CSV downloads successfully without authentication.

✅ This confirms the bucket is publicly accessible.


Step 4: Create a VPC Gateway Endpoint for Amazon S3

Search for VPC in the AWS Console.

Navigate to:

Endpoints
Enter fullscreen mode Exit fullscreen mode

Click Create endpoint.

Configuration:

Name

my-ps-lab-endpoint-01
Enter fullscreen mode Exit fullscreen mode

Service:

com.amazonaws.us-east-1.s3
Enter fullscreen mode Exit fullscreen mode

select service

Type:

Gateway
Enter fullscreen mode Exit fullscreen mode

Choose:

  • Your VPC
  • Your Route Table

Create the endpoint.


Step 5: Verify the Route Table

Navigate to:

VPC → Route Tables
Enter fullscreen mode Exit fullscreen mode

Open the selected route table.

Under Routes, verify a route similar to:

vpce-xxxxxxxx
Enter fullscreen mode Exit fullscreen mode

endpoint id

Copy the endpoint ID.

You'll use it later in the bucket policy.


Step 6: Launch an EC2 Instance

Open EC2.

Launch a new instance.

Example name:

my-ps-lab-instance-01
Enter fullscreen mode Exit fullscreen mode

Choose:

  • Your VPC
  • Your subnet
  • Default key pair

ec2 instance

Launch the instance.


Step 7: Create an IAM Role

Navigate to IAM → Roles.

Create a new role.

Configuration:

Trusted Entity

AWS Service
Enter fullscreen mode Exit fullscreen mode

Use Case

EC2
Enter fullscreen mode Exit fullscreen mode

Permissions:

AmazonS3FullAccess
Enter fullscreen mode Exit fullscreen mode

s3 access

Role Name

my-iam-role-lab-01
Enter fullscreen mode Exit fullscreen mode

Create the role.


Step 8: Add an Inline IAM Policy

Open the IAM role.

Choose:

Add permissions
→ Create Inline Policy
→ JSON
Enter fullscreen mode Exit fullscreen mode

Paste:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:ListBucket"],
      "Resource": "arn:aws:s3:::ml-lab-bucket-secure-ml-101"
    },
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject"],
      "Resource": "arn:aws:s3:::ml-lab-bucket-secure-ml-101/*"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Policy name:

my-secure-policy
Enter fullscreen mode Exit fullscreen mode

policy

Create the policy.

Although the role already has AmazonS3FullAccess, this inline policy demonstrates how to grant least-privilege permissions for a specific bucket.


Step 9: Attach the IAM Role to EC2

Go to the EC2 console.

Select the instance.

Choose:

Actions
→ Security
→ Modify IAM Role
Enter fullscreen mode Exit fullscreen mode

Attach:

my-iam-role-lab-01
Enter fullscreen mode Exit fullscreen mode

role update

Update the role.


Step 10: Test Access from EC2

Connect to the EC2 instance using EC2 Instance Connect.

Run:

aws s3 ls s3://ml-lab-bucket-secure-ml-101
Enter fullscreen mode Exit fullscreen mode

bucket access

Expected output:

raw_v1.csv
Enter fullscreen mode Exit fullscreen mode

You can also test another bucket:

aws s3 ls s3://your-s3-bucket-name
Enter fullscreen mode Exit fullscreen mode

At this stage, EC2 can access the bucket successfully.


Step 11: Block Public Access

Return to the S3 bucket.

Navigate to:

Permissions
Enter fullscreen mode Exit fullscreen mode

Edit Block Public Access.

Enable:

Block all public access
Enter fullscreen mode Exit fullscreen mode

block access

Save the settings.

Type:

confirm
Enter fullscreen mode Exit fullscreen mode

to apply the changes.

The bucket is no longer publicly accessible.


Step 12: Restrict Access to the VPC Endpoint

Now we'll ensure that only requests coming through our VPC Endpoint are allowed.

Replace the bucket policy with:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyIfNotFromVPCE",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::ml-lab-bucket-secure-ml-101",
        "arn:aws:s3:::ml-lab-bucket-secure-ml-101/*"
      ],
      "Condition": {
        "StringNotEquals": {
          "aws:sourceVpce": "vpce-xxxxxxxx"
        }
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Replace:

vpce-xxxxxxxx
Enter fullscreen mode Exit fullscreen mode

with your own VPC Endpoint ID.

Save the policy.


Step 13: Validate the Security

Test from Browser

Open the Object URL again.

Expected result:

Access Denied
Enter fullscreen mode Exit fullscreen mode

denied

The internet can no longer reach your bucket.


Test from EC2

Return to the EC2 terminal.

Run:

aws s3 ls s3://ml-lab-bucket-secure-ml-101
Enter fullscreen mode Exit fullscreen mode

Expected output:

raw_v1.csv
Enter fullscreen mode Exit fullscreen mode

bucket access

The EC2 instance continues to access the bucket successfully because traffic flows through the VPC Gateway Endpoint.


What We Achieved

In this lab, we transformed an insecure S3 bucket into a private, production-ready storage solution.

✔ Created an Amazon S3 bucket

✔ Allowed public object access

✔ Verified public accessibility

✔ Created a Gateway VPC Endpoint

✔ Launched an EC2 instance

✔ Assigned an IAM Role

✔ Granted least-privilege bucket permissions

✔ Enabled Block Public Access

✔ Restricted S3 access using a VPC Endpoint policy

✔ Verified that only workloads inside the VPC could access the bucket


Top comments (0)