Skip to content

Tarique-B-DevOps/Secure-Access-to-AWS-Services-via-Gateway-VPC-Endpoints

Repository files navigation

Secure Access to S3 From Private EC2 Instance via Gateway Endpoint for Amazon S3

🔐 Overview

This repository demonstrates secure, internet-isolated access to Amazon S3 from a private EC2 instance using a Gateway VPC Endpoint. It highlights best practices around access control, IAM roles, security groups, and restricted egress — all without relying on NAT Gateways or public IPs. The setup focuses on automation and fine-grained security.

🚪 Why Use a Gateway Endpoint Over an Interface Endpoint?

  • Does not consume ENIs, simplifying network interface management
  • Enables route-based traffic control via route tables
  • More scalable and reliable for high-throughput S3 operations
  • No additional cost for Gateway Endpoints (S3 and DynamoDB) compared to the per-hour and per-GB charges of Interface Endpoints

Key Highlights

⚙️ End-to-end Automation

  • Full infrastructure setup via Terraform
  • S3 object upload, download, verification, and logging done automatically
  • Bastion host auto-configured using file and remote-exec provisioners
  • IAM role and S3 policy created and attached to private EC2 without manual steps
  • Key pair generation and placement handled in provisioning

🔒 Security-focused Design

  • Principle of least privilege applied throughout
  • Instance Role: EC2 instances are associated with a tightly-scoped IAM role, granting only the necessary permissions for interacting with S3 (GetObject, PutObject, DeleteObject).
  • Security Groups:
    • Security groups are used to control inbound and outbound traffic.
    • SSH access to the Bastion host is restricted to a specific IP (your local IP), ensuring only authorized users can access it.
    • The private EC2 instance allows SSH only from the Bastion host’s security group, enforcing a strict access path.
    • Outbound access from the private EC2 is restricted to only Amazon S3 using a Gateway VPC endpoint.
  • Bucket Policy:
    • The S3 bucket policy is defined to ensure only authorized IAM roles and instances can access the objects.
    • The policy allows GetObject, PutObject, and DeleteObject actions, restricted to specific resources (the designated S3 bucket).
    • Public access to the bucket is blocked, ensuring that data cannot be accessed by external sources.

Provisioning Resources and Testing

1. Provision Resources

Initialize Terraform and apply the configuration to create all necessary infrastructure components automatically.

terraform init && terraform apply -auto-approve

Image

2. Verify Resource Configuration

Confirm that the infrastructure is properly set up by reviewing the following key configurations:

Private Routes

Image

Private Instance's Outbound Traffic

Image

S3 Bucket Policy

Image

File Upload Confirmation

Image


3. SSH and Test

Perform connectivity and functionality testing by accessing the instances and validating S3 operations:

Find the SSH commands from outputs by running following command:

terraform output
  • SSH into the Bastion host from your local machine.
  • From the Bastion host, SSH into the private EC2 instance.
  • Check the files in /tmp — you should see file.txt uploaded and downloaded.txt retrieved from S3.
  • View the content of downloaded.txt to verify the download succeeded.
  • Delete the file from the S3 bucket using the AWS CLI.

Image

4. Destroy Resources

Clean up all provisioned infrastructure by running the destroy command:

terraform destroy -auto-approve

aws_iam_role_policy_attachment.attach: Destroying... [id=secure-app-ec2-s3-role-20250604164907712900000001]
aws_route_table_association.public: Destroying... [id=rtbassoc-0ace4ac671c713893]
aws_s3_bucket_policy.allow_vpce_only: Destroying... [id=secure-app-0gtl41ovm2mo-bucket]
aws_route_table_association.private: Destroying... [id=rtbassoc-005f5dbd9dd769878]
aws_instance.bastion: Destroying... [id=i-0e9e19333e83eafb9]
aws_instance.private_instance: Destroying... [id=i-0800699d2f140d0f6]
aws_s3_bucket_policy.allow_vpce_only: Destruction complete after 2s
aws_iam_role_policy_attachment.attach: Destruction complete after 3s
aws_iam_policy.s3_access: Destroying... [id=arn:aws:iam::policy/secure-app-s3-access-policy]
aws_route_table_association.private: Destruction complete after 3s
aws_iam_policy.s3_access: Destruction complete after 0s
aws_route_table_association.public: Destruction complete after 3s
aws_route_table.public: Destroying... [id=rtb-03052248b91be0165]
aws_route_table.public: Destruction complete after 2s
aws_internet_gateway.igw: Destroying... [id=igw-08921a9f18796bd7a]
aws_instance.private_instance: Still destroying... [id=i-0800699d2f140d0f6, 00m11s elapsed]
aws_instance.bastion: Still destroying... [id=i-0e9e19333e83eafb9, 00m11s elapsed]
aws_internet_gateway.igw: Still destroying... [id=igw-08921a9f18796bd7a, 00m10s elapsed]
aws_instance.bastion: Still destroying... [id=i-0e9e19333e83eafb9, 00m21s elapsed]
aws_instance.private_instance: Still destroying... [id=i-0800699d2f140d0f6, 00m21s elapsed]
aws_internet_gateway.igw: Still destroying... [id=igw-08921a9f18796bd7a, 00m20s elapsed]
aws_instance.private_instance: Destruction complete after 25s
aws_iam_instance_profile.ec2_s3_profile: Destroying... [id=secure-app-ec2-instance-profile]
aws_subnet.private: Destroying... [id=subnet-0e968859aa633dff0]
aws_security_group.private_sg: Destroying... [id=sg-0ee10a647c7395a2b]
aws_s3_bucket.storage: Destroying... [id=secure-app-0gtl41ovm2mo-bucket]
aws_iam_instance_profile.ec2_s3_profile: Destruction complete after 1s
aws_iam_role.ec2_s3_access: Destroying... [id=secure-app-ec2-s3-role]
aws_subnet.private: Destruction complete after 2s
aws_s3_bucket.storage: Destruction complete after 2s
random_string.str: Destroying... [id=0gtl41ovm2mo]
random_string.str: Destruction complete after 0s
aws_iam_role.ec2_s3_access: Destruction complete after 1s
aws_security_group.private_sg: Destruction complete after 2s
aws_vpc_endpoint.s3_gateway: Destroying... [id=vpce-0dc52738c6efa7b05]
aws_instance.bastion: Still destroying... [id=i-0e9e19333e83eafb9, 00m31s elapsed]
aws_vpc_endpoint.s3_gateway: Destruction complete after 7s
aws_route_table.private: Destroying... [id=rtb-006ad95be72e4f426]
aws_route_table.private: Destruction complete after 1s
aws_internet_gateway.igw: Still destroying... [id=igw-08921a9f18796bd7a, 00m30s elapsed]
aws_instance.bastion: Still destroying... [id=i-0e9e19333e83eafb9, 00m41s elapsed]
aws_internet_gateway.igw: Still destroying... [id=igw-08921a9f18796bd7a, 00m40s elapsed]
aws_instance.bastion: Still destroying... [id=i-0e9e19333e83eafb9, 00m51s elapsed]
aws_internet_gateway.igw: Still destroying... [id=igw-08921a9f18796bd7a, 00m50s elapsed]
aws_instance.bastion: Still destroying... [id=i-0e9e19333e83eafb9, 01m01s elapsed]
aws_internet_gateway.igw: Still destroying... [id=igw-08921a9f18796bd7a, 01m00s elapsed]
aws_instance.bastion: Destruction complete after 1m11s
aws_key_pair.ec2_kp: Destroying... [id=secure-app-kp]
aws_subnet.public: Destroying... [id=subnet-05a14ddd29f1c19c5]
local_file.kpf: Destroying... [id=fcd2274c6947e38fde19c0ab8fe188c83fce1884]
aws_security_group.bastion_sg: Destroying... [id=sg-04389e150a574b556]
local_file.kpf: Destruction complete after 0s
aws_key_pair.ec2_kp: Destruction complete after 1s
tls_private_key.pk: Destroying... [id=8b36cc32432e01babd85e9eb61108c05d5598982]
tls_private_key.pk: Destruction complete after 0s
aws_internet_gateway.igw: Destruction complete after 1m8s
aws_subnet.public: Destruction complete after 2s
aws_security_group.bastion_sg: Destruction complete after 3s
aws_vpc.network: Destroying... [id=vpc-0ae86da50e32081df]
aws_vpc.network: Destruction complete after 1s

Destroy complete! Resources: 23 destroyed.

About

Secure Access to S3 From Private EC2 Instance via Gateway Endpoint for Amazon S3

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages