In the first blog in this series, we discussed setting up IAM properly. Now we’re moving on to the second step, avoiding direct internet access to AWS resources.
When AWS resources like EC2 instances or S3 buckets are directly accessible via the Internet, they are vulnerable to attack. For example, brute force attacks on SSH login, denial of service (DOS) attacks on server resources via Layer 3, 4, or 7 flooding, or the inadvertent disclosure of data on an S3 bucket. Thankfully, AWS offers tools that can virtually eliminate each of these threats. Let’s discuss how to protect resources that have traditionally been placed in the demilitarized zone (DMZ) of a public subnet.
Put all EC2 instances in private subnets
Despite the advent of network address translation (NAT) (i.e., the mapping of a public IP address to a private IP address), many businesses put publicly accessible resources in the DMZ. This enables direct connectivity to resources by assigning public IP addresses to them. In turn, through domain name system (DNS) resolution, website names are translated to these IP addresses which enables connectivity. Ordinarily, resources placed in a DMZ are webservers. Although some companies out of convenience, or lack of security awareness, will also place database, application, and file servers in the DMZ. If adequate access control lists (ACLs) and security groups are not in place to restrict access by IP source, IP destination, protocol, and port number, these resources are vulnerable to attack.
Fortunately, there is no longer a need to place EC2 instances in a public subnet. This includes bastion hosts that are used to access EC2 instances in private subnets. Rather than associate a public IP address with EC2 instances, an elastic load balancer (ELB) can be used instead.
The ELB is a virtual appliance that terminates webserver bound traffic via a public IP address and passes that traffic to EC2 instances or corresponding containers, if applicable, that reside in a public subnet. Neither the AWS customer using the load balancer, nor any external party can directly access the load balancer, so it is not vulnerable to attack. Furthermore, depending on whether the traffic being terminated on the ELB is Layer 4 (Transport layer of the OSI) or HTTP (Layer 7), AWS offers two separate ELBs to accommodate the applicable traffic. These ELB options are Network Load Balancer (Layer 4) and Application Load Balancer (Layer 7). As the diagram and step-by-step description from AWS below reveals, virtualized server resources that reside in private subnets cannot be directly accessed by the outside world.
Complete traffic flow diagram
The following diagram combines the inbound and return traffic flows to provide a complete illustration of load balancer routing.
Traffic from the internet flows in to the Elastic IP address, which is dynamically created when you deploy an internet-facing Application Load Balancer.
The Application Load Balancer is associated with two public subnets in the scenario that’s illustrated. The Application Load Balancer uses its internal logic to determine which target group and instance to route the traffic to.
The Application Load Balancer routes the request to the EC2 instance through a node that’s associated with the public subnet in the same Availability Zone.
The route table routes the traffic locally within the VPC, between the public subnet and the private subnet, and to the EC2 instance.
The EC2 instance in the private subnet routes the outbound traffic through the route table.
The route table has a local route to the public subnet. It reaches the Application Load Balancer on the node in the corresponding public subnet, by following the path back the way the traffic entered.
The Application Load Balancer routes traffic out through its public Elastic IP address.
The public subnet’s route table has a default route pointing to an internet gateway, which routes the traffic back out to the internet.
Load balancer subnets and routing, Load balancer subnets and routing – AWS Prescriptive Guidance (amazon.com).
Importantly, even with an ELB in place, it is imperative to configure appropriate ACLs and security groups. Only legitimate traffic should be allowed in and out of the virtual private cloud (VPC). If the load balancer improperly allows all traffic in and out of the private subnet where the EC2 instances reside, much of the benefit of restricting direct Internet access to them can be lost.
Moreover, EC2 instances behind an ELB can still be vulnerable to Layer 3, Layer 4, or Layer 7 DoS attacks. An ELB merely eliminates the ability for people from the Internet to directly access your instances. To stop Layer 3 and Layer 4 Distributed Denial of Service (DDoS) attacks, AWS offers AWS Shield. This service is offered at two levels – basic and advanced. Basic service is free, and it monitors and restricts Layer 3 and Layer 4 traffic. Hence, before traffic ever hits your ELB, it is being monitored and filtered with AWS’ DDoS mitigation technology. For advanced coverage and features, AWS offers AWS Shield Advanced for an additional cost. With Shield Advanced, you have access to a 24/7 AWS Shield Response Team, advanced reporting, and cost protection associated with the increase of AWS resources used during an attack. You can learn more about AWS Shield here: Managed DDoS protection – AWS Shield Features – Amazon Web Services.
For Layer 7 DoS mitigation, AWS offers a Web Application Firewall (WAF). Per AWS, this service “lets you create rules to filter web traffic based on conditions that include IP addresses, HTTP headers and body, or custom URIs… In addition, AWS WAF makes it easy to create rules that block common web exploits like SQL injection and cross site scripting.” If your business utilizes AWS Shield Advanced, AWS WAF is included in the monthly cost. You can learn more about AWS WAF here: Features – AWS WAF – Amazon Web Services (AWS).
Notably, some DoS events are not malicious but are rather the result of a company’s web services going viral. If too much traffic hits all at once, content can be inaccessible. For both static and dynamic content, AWS offers a content delivery network (CDN) called CloudFront. Thus, rather than scale your EC2 instances behind an ELB vertically or horizontally for increased demand, content can be offloaded to CloudFront where it is cached and, if need be, made globally available. This protects your virtualized server resources and your wallet, too. You can learn more about AWS CloudFront here: Low-Latency Content Delivery Network (CDN) – Amazon CloudFront – Amazon Web Services.
How to securely access EC2 instances in private subnets
Up to this point, we have discussed how you can protect your EC2 instances from being accessed from the outside world. Rightfully so, you may be wondering how systems administrators can access instances to manage them if there is no public IP address for SSH or RDP connectivity? Normally, a bastion host would be provisioned in a public subnet for access to resources in a private subnet. However, by provisioning an EC2 instance in a public subnet as a bastion host, no matter how hardened the instance is, it is creating an unnecessary vulnerability.
The simple remedy to getting access to EC2 instances in private subnets is AWS Systems Manager. There is no need to open SSH or RDP ports in the private subnet either. Through the AWS console, AWS can programmatically establish SSH or RDP access to EC2 instances. Without SSH or RDP ports open, even if an internal EC2 instance was compromised, it would not be possible for a malicious actor to capitalize on stolen key pairs to access an instance or perform a brute force attack on the root account either. Accordingly, the only users permitted to access the EC2 instance, would be those users with the appropriate IAM user, group, or role permissions. To learn more about AWS Systems Manager, click here: Centralized Operations Hub – AWS Systems Manager – Amazon Web Services.
Finally, you may also be wondering how EC2 instances in a private subnet can access the Internet for software downloads, patches, and maintenance if they do not have a public IP address? Previously, for instances in private subnets to access the Internet, an EC2 NAT instance in a public subnet would need to be provisioned. Internet bound traffic from instances in the private subnet would be routed through the NAT instance.
However, like bastion hosts, EC2 NAT instances pose unnecessary security risk. The solution to routing Internet based traffic to and from instances in private subnets is by using AWS NAT Gateways. Like ELBs, NAT Gateways are virtualized appliances that are not accessible to AWS customers, or external parties. Unlike NAT instances, they are not provisioned with predefined CPU, RAM, and throughput either. Rather, they scale dynamically to handle whatever workload is thrown at them. Consequently, EC2 instances in private subnets can securely access the Internet without the threat associated with a NAT instance in a public subnet. To learn more about AWS NAT Gateways, click here: NAT gateways – Amazon Virtual Private Cloud.
Now that we have learned how to protect EC2 instances and vicariously the services that leverage them like containers, applications, and databases, let’s discuss how to secure S3 Buckets.
Keep S3 buckets private or restrict public access using CloudFront.
Over the years, many news stories have revealed the blunders of companies that publicly expose their customers’ data by publishing it in public S3 buckets. As anyone who has recently provisioned an S3 bucket will know, AWS has made it exceedingly difficult to repeat this error. With warning prompts and conspicuous red, “danger, Will Robinson!” icons, AWS lets you know when an S3 Bucket is public.
For obvious reasons, data that companies do not want the whole world to know should never be placed in a public S3 bucket. This includes personally identifiable information (PII), health information, credit card account details, trade secrets, and any other proprietary data. Even with encryption in place, which we will discuss in Step 3, there is no reason to ever make this type of data publicly available.
For S3 data that is publicly available, direct access to the objects should be restricted. There are a few reasons why. First, entities may not want their customers to access objects with the AWS S3 URL. Instead, they may want their customers to access objects using their custom domain. Second, entities may not want their customers to have unlimited access to S3 objects. Instead, they may prefer to use pre-signed URLs to limit how long end users can access objects. Finally, entities may not want to pay unnecessary costs for end users reading or downloading S3 objects directly from a bucket. The remedy to these problems is to make public S3 buckets accessible only via CloudFront.
This is achieved by configuring S3 to only accept GET or POST requests from CloudFront. Hence, objects in a public S3 bucket are inaccessible to the outside world. To learn more about AWS CloudFront and S3 Bucket integration, click here: Restricting access to an Amazon S3 origin – Amazon CloudFront.
Now that we know how to properly secure EC2 instances and S3 buckets by restricting direct access via the Internet, the next, and last blog in this series will discuss our final step – encryption.