In an increasingly connected world, the need for robust security measures for cloud infrastructures is constantly growing. Applications that are accessible over the internet must be secured in a way that prevents unnecessary exposure of sensitive backend components. A fully public Virtual Private Cloud (VPC) architecture may be sufficient for a quick start, but it comes with significant risks: Every service is potentially accessible from the outside, increasing the attack surface for potential threats.
AWS provides best practices to mitigate these risks, including separating a subnet into public and private subnets [1]:
- Public Subnets: For resources that need direct internet access, such as load balancers or bastion hosts. These subnets are connected via an Internet Gateway (IGW) and are secured using Security Groups and Network ACLs (NACLs).
- Private Subnets: For internal components such as ECS tasks, databases, or backend services. These have no direct internet connection and use a NAT Gateway or NAT Instance to enable outbound access to the internet. Access is strictly controlled using whitelisting rules in Security Groups and NACLs for additional security.
In this blog post, I will explain how I gradually restructured my existing, fully public AWS network architecture. The goal was to create a secure setup that combines public and private subnets to better protect internal systems from unwanted access. I will discuss the motivation behind this change, describe the initial state of the architecture, and explain the specific modifications I implemented to make my live chat application more secure and future-proof. n
Current State
Currently, my live chat application is based on a simple but poorly secured network architecture (Figure 1): All resources are within a Virtual Private Cloud (VPC) and operate exclusively in a public subnet. The Application Load Balancer (ALB) is directly connected to the internet and routes incoming requests to my ECS Fargate service, which also runs in this public subnet.
This setup enables a quick and easy deployment but presents several security risks. Since the entire application runs in a public subnet, all components can communicate directly with the internet. While this simplifies access and communication, it also creates a major vulnerability. Potentially sensitive backend components are exposed and thus vulnerable to attack.
At the moment to regulate incoming traffic, I use a security group that allows only necessary ports, such as port 3000 for the live chat service and port 80 for the ALB. However, these ports are currently open to all public traffic. In addition, IAM roles and policies ensure that ECS tasks run correctly and that CloudFront can access S3 content to serve static files. Despite these measures, the backend remains unnecessarily exposed.
With the upcoming transition to a new architecture, I aim to address this weakness. By separating public and private subnets, internal services can be shielded from direct external access, allowing only necessary traffic through public interfaces. This will significantly improve the security architecture of my live chat application, creating a clearer structure: A public frontend remains accessible, while backend components like AWS Fargate reside in more secure private subnets.
New Architecture
To close these security gaps, I implemented a new structure that separates public and private subnets (Figure 2):
- Load Balancer in Public Subnets: The ALB remains in the public subnets and continues to be accessible via an Internet Gateway. These subnets are configured to automatically assign public IP addresses to instances.
- Backend Services in Private Subnets: The ECS tasks running my backend now operate in private subnets, which have no direct internet access. They can only initiate outbound traffic via a NAT Gateway, for example, for updates or external API calls. Incoming traffic reaches the backend exclusively through the ALB.
- Targeted Access Control with Security Groups: A Security Group for the ALB allows inbound traffic on ports 80 and 443. The Security Group for ECS tasks only accepts connections from the ALB, ensuring that direct access from the internet is blocked.
- Routing and NAT Gateway: Public subnets route their internet traffic through the Internet Gateway, while private subnets send outbound traffic through a NAT Gateway located in a public subnet. This setup allows private services to access the internet without being directly reachable themselves.
An additional optional step to enhance security is implementing an AWS Web Application Firewall (WAF) in front of the Load Balancer. The WAF helps defend against Layer-7 attacks, enforce IP rules, and set rate limits.
Conclusion
By transitioning to a combination of public and private subnets, I have significantly increased the security of my live chat application:
✅ Backend services are no longer directly accessible
✅ Reduced attack surface
✅ Better separation between public and internal components
✅ Targeted control over network traffic
With these adjustments, my infrastructure is now far more resilient to potential attacks and better prepared for the future.
Literatur
[1] https://docs.aws.amazon.com/vpc/latest/userguide/vpc-security-best-practices.html
Leave a Reply
You must be logged in to post a comment.