/ cloud

AWS Security Overview - Part IV - VPC Network Access Control Lists


In Part III of my AWS Security Overview Blog Series I discussed Security Groups.

In this blog post I will be covering Network Access Control Lists.

Network Access Control Lists (NACL)

I started at the instance level using security groups, now working outwards towards the VPC edge I will be discussing NACLs.

There will be a diagram at the end depicting the NACLs and where they sit within an AWS network diagram.

NACL Basics

Network ACLs (NACLs) are a list of numbered rules that act as virtual firewall. NACLs sit at the subnet layer. If you recall from the Security Groups post I did, security groups are virtual firewalls at the instance level attached to the elastic network interface (ENI) of the instance.

Since NACLs are attached to a subnet, NACLs would prevent/allow traffic to the instance before it reached the security group traffic for evaluation.

NACLs support both allow and deny rules, where security groups only support allows. The NACL rules are automatically applied to all instances in a subnet that it's associated with.

A VPC automatically comes with a set of default NACLs, which are configured out of the box to allow all inbound and allow all outbound traffic.

There is one exception to this. If you create your own NACL and attach it to a subnet of your choosing, the NACL rules will be set to DENY both inbound and outbound traffic.

NACL Association

Each subnet must be associated with a NACL and a subnet can only be associated with one NACLs, but a NACL can be associated with multiple subnets.

An example of this would be if you created a set of NACL rules that you always assign a web tier subnet, you can attach those NACL rules to each of those web tier subnets where your web servers reside.

Rules - Overview

With NACLs, there are Inbound Rules and Outbound Rules. You can set the following options:

  • Rule Number
  • Type (SSH, HTTP, All TCP, All UDP, etc.)
  • Protocol (TCP, UDP, etc.)
  • Port Range
  • Source IP - CIDR Notation (for Inbound Rules)
  • Destination IP - CIDR Notation (for Outbound Rules)
  • Allow/Deny

You an also tag your NACLs, as well as associate your NACLs to a subnet of your choosing.

Rules - Numbered Order

It is important to know that NACLs rules are processed in ascending numbered order, starting from lowest numbered rule to the highest.

Let's say you want to make a connection to a MY SQL database over port 1433 and your inbound rules looked like the rules here.

At first it's going to check:

  • 100 - No, that doesn't match. This is for port 22
  • 200 - No, that doesn't match. This is for port 80
  • 300 - No, that doesn't match. This if for port 443
  • 400 - Yes, this matches. Ok, it says, "ALLOW", so you're allowed.

If it would have reached rule, * it would have been denied. So if you request something, and it's not met by one of the existing rules, it will be denied once it runs through all of your rules.


You will notice above I spread the rules out by 100. This is recommended by Amazon so you can go back and add rules in-between them.

Remember, it's verified by numbered order, so if you create rules and number them; 1, 2, 3, 4... and you need to go back and insert an ALLOW/DENY between one of them you would have to reorder the entire list of rules. The highest rule number you can use is 32,766 so give yourself some space.

Stateless Firewall

Unlike, Security Groups, which are stateful, NACLs are stateless, which means that return traffic must be explicitly allowed by the rules.

In the Security Groups post I gave an example of ping, and how, even though I didn't explicitly allow ICMP inbound on my security group rules, it was still allowed through and I received a reply.

Using that same example, I will attempt to ping google.com again; however, this time I will have a DENY NACL rule set inbound.

So here are the inbound rules. I created rule 99 that denies ICMP.


Now I will attempt to ping google.com

ping -c 4 google.com

As you can see, unlike with security groups where this would have been successful, I didn't receive a return once the DENY was set in my NACLs.

PING google.com ( 56(84) bytes of data.

--- google.com ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 2999ms

You can also see all traffic outbound it allowed so it was on the inbound where I had issues.


Command Line Tools

The aws cli also supports command line options for NACL. For information about your NACLs, the best command using the cli to run is describe-network-acls.

Running, describe-network-acls will do just that, it returns JSON output describing your network acls. It will tell you the associations to what subnets and NACLId, and for the respective NACL Id, it will provide you a list of rules, protocols, whether it's egress (true or false), the rule action (allow/deny) and the CIDR block assigned to that particular rule.

aws ec2 describe-network-acls

Here is a quick snapshot of some of that output from running, describe-network-acls.

    "NetworkAcls": [
            "Associations": [
                    "SubnetId": "subnet-d592159d",
                    "NetworkAclId": "acl-9dba20e4",
                    "NetworkAclAssociationId": "aclassoc-b44413c4"
            "NetworkAclId": "acl-9dba20e4",
            "VpcId": "vpc-0cb37675",
            "Tags": [],
            "Entries": [
                    "RuleNumber": 100,
                    "Protocol": "-1",
                    "Egress": true,
                    "CidrBlock": "",
                    "RuleAction": "allow"
<removed output for berevity>

Now that you have some information describing your NACLs, let's say you wanted to add a rule. You can accomplish that by using the, create-network-acl-entry command.

So looking at the above, network acl id we see that, acl-9bda20e4 has the following rules assigned.


Now let's say we wanted to add database port inbound over 1433. We can accomplish this via the create-network-acl-entry.

aws ec2 create-network-acl-entry --network-acl-id acl-9dba20e4 --ingress --rule-number 130 --protocol tcp --port-range From=1433,To=1433 --cidr-block --rule-action allow

You can see here now we have the MS SQL port, 1433 allowed as rule 130.


You can also replace existing rules with different values, say for example, if you had a typo on a port number or something.

aws ec2 replace-network-acl-entry

You can also write code and paint a much more detailed picture of you NACLs and their associations, but for now I am just highlighting a point.

There are tools at your disposal if you know where to look. You have a friend and it's called, help.

aws <insert_service_name> help


Here you can see we added NACLs (the blue lock) to our subnet edge.

Traffic would be evaluated here, prior to being evaluated by the security groups attached to the Web Server EC2 instance.



So there is an overview of Network Access Control Lists (NACLs).

In Part V of my AWS Security Overview blog series I will discuss VPC Flow Logs.

If you find this post useful and educational you can donate via PayPal.Me here: $1, $2, $3, $4, $5 or Custom.


Amazon Elastic Compute Cloud User Guide for Windows Instances
Amazon Virtual Private Cloud User Guide Network ACLs