by Valts Ausmanis · August 14, 2024
AWS network security is one of the foundations that should be taken seriously, especially when you deploy your AWS resources in Amazon Virtual Private Cloud (VPC). There are two main building blocks that allow you to control access to AWS resources within the VPC - security groups and network access control lists (NACLs). While both play a fundamental role in controlling inbound and outbound traffic to your AWS resources, there are important differences between them. In this article, we will take a closer look at the differences between security groups and network ACLs.
A security group acts as a virtual firewall that controls the traffic allowed to reach and leave AWS resources (like EC2 instances, load balancers, RDS database instances, VPC endpoint interfaces, Redis instances, etc.) within your VPC.
When creating new inbound and outbound rules, you need to specify the following:
In the example below, we have added two rules to a security group that allow TCP traffic on ports 443 and 80 from any IP address. Since these ports are used for HTTP/HTTPS traffic, this means that if you run your own web servers on public EC2 instances with this security group assigned, anyone could now access your website.
Network access control list (NACL) acts as a virtual firewall to allow or deny inbound or outbound traffic at subnet level. Similar to security groups, you can create new inbound and outbound rules for network ACL by providing:
In the example below, you can see the default NACL inbound rules (by default associated to all the subnets in VPC). The first rule allows all inbound traffic from any source, while the last rule denies all traffic and would be effective if no other allow rules are specified.
While both NACL and security group serve the same purpose of controlling inbound and outbound traffic to and from AWS resources, there are differences between the two. Let's compare them by describing each difference with practical examples.
Security groups are assigned at the instance level, more specifically to a network interface. Every EC2 instance, RDS, Redis, etc., deployed in a specific subnet in the VPC has a network interface assigned.
Network ACLs are associated at the subnet level. All the rules in a specific network ACL automatically apply to all instances deployed in the associated subnet.
Now, let’s see how this difference works in practice. We’ll deploy two EC2 instances in public subnets and assign them a security group that allows ICMP traffic from any IP address. We'll use the default network access control list assigned to the subnets, which allows all inbound and outbound traffic.
We will now attempt to ping the EC2 instance at its assigned public IP address (ex. 54.217.161.49) to check if we can establish communication:
As shown in the screenshot above, we successfully communicated with the newly created instance.
Now, let’s modify the NACL rule to deny ICMP traffic for all resources deployed in the associated subnets, that includes both newly created EC2 instances in our case.
Then, run the same command (e.g., ping 54.217.161.49
) to check if we can still communicate with the EC2 instance.
As shown in the screenshot above, the change in the NACL inbound rules has disabled all ICMP traffic, and this rule overrides the one in the security group.
Security groups are stateful, meaning that if an inbound rule allows specific traffic (e.g., TCP 443), it automatically allows the corresponding outbound traffic, regardless of the outbound rules.
Network ACLs are stateless, which means separate rules are required for inbound and outbound traffic. For example, if you allow TCP 443 inbound traffic, it does not automatically permit the same outbound traffic. This means that, for a TCP 443 requester (ex. open a website via browser) to receive a response, we must add a separate TCP 443 allow outbound rule.
Now, let’s deploy a new EC2 instance with the same security group with inbound rule for ICMP traffic from any IP address.
We will modify the default NACL to allow inbound ICMP traffic without a corresponding outbound rule:
When we try to ping the EC2 instance – it’s not possible to communicate:
This clearly shows why a network ACL is “stateless” and that both inbound and outbound rules must be created to allow specific traffic to reach or leave AWS resources.
Security groups aggregate all the rules before deciding to allow specific traffic. For example, if an EC2 instance has two security groups—one allowing inbound traffic for TCP 80 and another allowing inbound traffic for TCP 443—this means that both ports 80 and 443 are available for inbound TCP traffic.
However, NACLs evaluate rules in order based on the rule number, starting with the lowest, when deciding whether to allow or deny specific traffic.
Let’s look at a practical example: we'll use the same deployed EC2 instance and existing security group, and update the NACL inbound rules by adding a "deny all inbound traffic" rule, but with a higher rule number (“102”) than the one used for the ICMP rule:
After the changes we can still successfully ping the EC2 instance because the ICMP allow rule has the higher number than deny all rule:
When you add an inbound or outbound rule to a security group, it always means that you are allowing traffic from a specific source or to a specific destination.
For network ACLs, there is an additional parameter “Allow/Deny”, when creating inbound or outbound rules. This parameter defines whether the specific traffic (e.g., TCP 443) from the source/destination (e.g., 0.0.0.0/0) should be allowed or denied.
For a security group, as a source or destination for rules, you can provide:
For NACLs, as the source or destination for rules, you can only provide a range of IPv4/IPv6 addresses in CIDR block notation.
When using security groups, it's common to specify a security group as the source or destination in the rules. This means that you allow specific traffic to or from all AWS resources assigned to that security group.
Now, to demonstrate using a security group as a source, let's create a load balancer with a security group that allows TCP traffic on ports 80 and 443 from all IP addresses:
And create EC2 instances that allow traffic from the load balancer's security group:
These security group rules allow EC2 instances to receive inbound traffic (TCP 80/443) only from the load balancer because of the specified source group ID (sg-0b7db3359ca04bcce).
One subnet can have one network ACL assigned, whereas one network interface can have multiple security groups assigned.
There are limits on the number of:
By using multiple security groups, you gain more granular control over inbound/outbound traffic. You can create security groups with a least privilege approach, defining the minimum necessary permissions for specific AWS resources.
When you create a VPC or use the default one, it comes with a default security group and a network ACL. Default NACL is associated with all subnets in the specific VPC, and the default security group is used when you provision new EC2 instances, load balancers, etc., without specifying your own security groups.
The default configurations are different:
I guess anyone (including AWS), who has worked on configuring VPC network traffic will agree that using security groups should be considered the primary approach to controlling inbound and outbound traffic to your VPCs. The main reason is that security groups are more flexible than network ACLs, offering features like stateful filtering and the ability to specify other security groups as the source or destination.
Some even compare NACLs to "...physical networking where nearly all controls were managed by subnet and VLAN boundaries". I would agree that for the majority of VPC network configurations, security groups should be used—just because they’re much more versatile and allow a single network interface to have multiple groups, enabling more granular and least-privilege access control. However, I would still use network ACLs as secondary control, with rules that should be common for all instances, such as denying a specific subset of traffic (e.g., SSH on port 22) to all instances deployed in specific subnet/s.
Looking for a tool to automatically visualize your Security Groups and Network ACLs?
Try out Cloudviz.io and visualize your AWS cloud environment in seconds
As experienced AWS architects and developers, our goal is to provide users an easy way to create stunning AWS architecture diagrams and detailed technical documentation. Join us to simplify your diagramming process and unleash the beauty of your cloud infrastructure
Support
Contact
Copyright © 2019 - 2024 Cloudviz Solutions SIA