# Network ACLs — Cloud Networking: VPCs, Subnets & Peering

Source: https://www.skillbyai.com/en/cloud-networking/cn-nacls

> Use stateless, ordered network ACL rules at the subnet boundary and remember ephemeral ports.

## Stateless, numbered, subnet-wide

A **network access control list (NACL)** filters traffic at the **subnet** boundary rather than at each interface. Each subnet is associated with exactly one NACL. Unlike security groups, NACLs support both **allow and deny** rules, and rules are evaluated in ascending rule-number order: the first rule that matches decides, and the rest are ignored. A final `*` rule denies anything not matched. Most importantly, NACLs are **stateless**. They do not remember connections, so the reply to an allowed inbound request is checked against the outbound rules as an independent packet. Replies go to the client's **ephemeral port**, a high-numbered port chosen by the client's operating system, so outbound rules must allow a range such as 1024-65535. The default NACL allows all traffic in both directions; a custom NACL you create denies everything until you add rules. Many teams leave NACLs permissive and use them only for coarse blocks, such as denying a known bad range.

## First match wins

This simulation evaluates rules in number order. Notice that 198.51.100.7 is *allowed*: rule 100 matches before the deny at 120 is reached.

```python
import ipaddress

# Inbound NACL: evaluated lowest rule number first; first match wins.
rules = [
    (100, "0.0.0.0/0", 443, "ALLOW"),
    (110, "203.0.113.0/24", 22, "ALLOW"),
    (120, "198.51.100.7/32", 443, "DENY"),
    (32767, "0.0.0.0/0", None, "DENY"),  # the implicit '*' rule
]

def evaluate(src, port):
    for num, cidr, rport, action in sorted(rules):
        if ipaddress.ip_address(src) in ipaddress.ip_network(cidr) and rport in (None, port):
            return num, action
    return None

print(evaluate("198.51.100.7", 443))
print(evaluate("203.0.113.9", 22))
print(evaluate("192.0.2.1", 22))
```

Output:

```
(100, 'ALLOW')
(110, 'ALLOW')
(32767, 'DENY')
```

## Put denies before broad allows

To block one address, give the deny a lower number (for example 90) than the broad allow (100). Leave gaps between rule numbers, such as 100, 110, 120, so you can insert rules later.

**Quiz:** A custom NACL allows inbound TCP 443 from anywhere and outbound TCP 443 to anywhere. HTTPS clients time out. What is the most likely cause?

- [x] Responses to client ephemeral ports are blocked outbound
- [ ] Security groups override NACLs
- [ ] NACLs cannot filter port 443
- [ ] The internet gateway is stateless

*Answer:* Responses to client ephemeral ports are blocked outbound. NACLs are stateless. The server's replies go to the client's ephemeral port, not 443, so an outbound rule for 1024-65535 is needed.
