Most cloud filtering outages and most silent exposures come from the same root cause: engineers assume a rule is evaluated the way their last firewall worked, when the cloud evaluates it differently. A stateless AWS network ACL drops return traffic that a stateful security group would have allowed, an Azure subnet NSG denies a flow before the NIC NSG ever sees it, and a Google Cloud hierarchical policy can allow traffic that a project's VPC firewall rules were written to block. This guide compares stateful and stateless cloud filtering in depth: how each layer is evaluated, which one wins, why ephemeral ports and asymmetric rules break things, and which design patterns hold up at scale. Platforms are listed alphabetically (AWS, Azure, Google Cloud); order is never a ranking.
Key takeaways
- Stateful and stateless solve different problems. A stateful filter tracks a connection and lets the reply back in automatically; a stateless filter judges every packet alone, so each allowed request needs a matching rule for the response on ephemeral ports.
- Evaluation order is the real comparison. AWS security groups evaluate all rules and allow on any match, AWS network ACLs stop at the lowest-numbered match, Azure NSGs stop at the first match by priority, and Google Cloud walks a fixed sequence of policies before VPC firewall rules or after them.
- Both layers must agree. Traffic that crosses two filters (subnet and instance, subnet NSG and NIC NSG, hierarchical policy and VPC rule) is only delivered if every layer it meets allows it, so troubleshoot with the effective rules, not one rule list.
- Rule changes do not always cut live sessions. Tracked connections survive the removal of the rule that allowed them on all three platforms; AWS documents a network ACL deny as the way to stop a tracked flow immediately.
- Use the instance layer for intent and the subnet or organisation layer for guardrails. Write application allow lists against workload identities (security group references, application security groups, service accounts) and keep coarse, rarely changing denies at the outer layer.
The short answer
A security group (AWS), a network security group (Azure) and a VPC firewall rule or firewall policy (Google Cloud) are all stateful: allow a request and the reply is let back in. An AWS network ACL is stateless and attached to a subnet: it allows or denies each packet in each direction independently, in rule-number order. AWS describes the security group as the instance-level control and the network ACL as the subnet-level control, and says you can secure instances with security groups alone and add network ACLs as an additional layer.
Azure and Google Cloud do not have a separate stateless subnet ACL in the AWS sense. On Azure the subnet layer is another stateful NSG, plus Azure Virtual Network Manager security admin rules applied to whole virtual networks. On Google Cloud the outer layer is the hierarchical firewall policy attached to the organisation or folders. So the cross-cloud question is less "stateful or stateless" and more "which layer decides first, and which layer is allowed to override the other".
For AWS defaults see the Amazon VPC Security Guide; for cross-cloud design see the Cloud Network Security Guide.
How each platform evaluates rules
| Platform and control | Attached to | State | Rule actions | Evaluation |
|---|---|---|---|---|
| AWS security group | Network interface (instance level) | Stateful | Allow only | All rules evaluated before deciding; any matching allow permits the traffic |
| AWS network ACL | Subnet (one ACL per subnet, one ACL can serve many subnets) | Stateless | Allow and deny | Ascending rule number, first match applies; a final * rule denies anything unmatched and cannot be deleted |
| Azure network security group | Subnet or network interface | Stateful (flow records) | Allow and deny | Priority 100 to 4096, lowest first, processing stops at first match; default rules at 65000, 65001 and 65500 |
| Azure Virtual Network Manager security admin rule | Virtual networks in a network group | Applied before NSGs | Allow, Always allow, Deny | Priority 1 to 4096; Allow continues to NSGs, Always allow and Deny stop evaluation |
| Google Cloud VPC firewall rule | VPC network, targeted by network tag or service account | Stateful (connection tracking) | Allow and deny | Priority 0 to 65535 (default 1000), highest priority wins; at equal priority deny beats allow |
| Google Cloud firewall policy (hierarchical, global or regional network) | Organisation, folder, or network | Stateful | Allow, deny, goto_next, apply_security_profile_group | Unique priorities within a policy, first match; allow or deny stops all evaluation, goto_next passes to the next step |
AWS: route table, then network ACL, then security group
AWS describes inbound traffic from an internet gateway as being routed to a subnet by the route table, then filtered by the subnet's network ACL, then filtered by the security group on the instance. Inside the security group there is no order: all rules are evaluated, and because rules are allow only, a broad rule in any attached group opens the port for the interface. Inside the network ACL order is everything: the lowest-numbered match applies regardless of any later rule. AWS advises giving a deny rule a lower number than a broader allow it must override.
Azure: subnet NSG and NIC NSG in series
For inbound traffic Azure evaluates the NSG on the subnet first, then the NSG on the network interface; outbound traffic meets the NIC NSG first and the subnet NSG second. The same order applies inside a subnet, and both NSGs must allow a flow: if the subnet NSG denies inbound traffic, the NIC NSG never evaluates it. Above both sit security admin rules from Azure Virtual Network Manager, evaluated before any NSG, so a central Deny on a port holds even when an application team allows it.
Google Cloud: a fixed sequence of policy steps
Google documents the order exactly. With the default enforcement order AFTER_CLASSIC_FIREWALL, a packet meets hierarchical firewall policies (organisation first, then folders down to the project's folder), then regional system firewall policies, then VPC firewall rules, then global network firewall policies, then regional network firewall policies, and finally the implied action. Setting networkFirewallPolicyEnforcementOrder to BEFORE_CLASSIC_FIREWALL moves VPC firewall rules after the network policies; hierarchical and regional system policies always run first. If every step passes, the implied action denies ingress to a VM interface and allows egress.
Why stateless filtering breaks: ephemeral ports and asymmetric rules
Every client connection uses a high, temporary source port for the reply. A stateful filter remembers the outbound request and lets the reply in on that port. A stateless AWS network ACL does not, so AWS states that for every rule you add there must be an inbound or outbound rule that allows the response traffic. The client chooses the port range, and the range differs by client:
| Client (AWS documentation) | Ephemeral port range |
|---|---|
| Many Linux kernels, including Amazon Linux | 32768 to 61000 |
| Windows Server 2008 and later | 49152 to 65535 |
| Windows through Windows Server 2003 | 1025 to 5000 |
| Elastic Load Balancing | 1024 to 65535 |
| NAT gateway | 1024 to 65535 |
| AWS Lambda functions | 1024 to 65535 |
AWS recommends opening 1024 to 65535 for return traffic so that every client type works, and placing any deny rules for specific ports in that range earlier in the table than the allow. Four failure modes follow from this:
- Linux-only return range: an ACL written for 32768 to 61000 works in testing from Linux hosts, then breaks for traffic arriving through a load balancer or NAT gateway, which use 1024 to 65535. The symptom is intermittent: only some connections fail.
- Asymmetric rule pairs: an inbound allow for TCP 443 on the web subnet without the matching outbound allow to the client range. The TCP handshake never completes, and security group logs look clean because the drop happens at the subnet.
- Forgotten initiator direction: instances in a subnet that call out (package mirrors, APIs, databases in another subnet) need an outbound allow to the destination port and an inbound allow for the reply on the instance's own ephemeral range. Teams that think only about inbound service ports break outbound calls.
- Two subnets, four rules: traffic between an application subnet and a database subnet crosses two ACLs. A single flow needs an outbound and inbound rule on each side, four in total, and any one missing rule fails the connection.
Stateful filters avoid all four. On Azure and Google Cloud the equivalent asymmetry is two stateful layers in series, such as a subnet NSG allowing what the NIC NSG denies; the fix is the same: list every layer a packet meets and confirm each allows it.
Connection tracking and live sessions
Stateful means rule changes behave differently from what many responders expect.
- AWS: tracked connections continue until they time out even after the allowing rule is removed. AWS documents adding a network ACL deny to stop them immediately, because ACLs are stateless. A flow is untracked only when rules allow all traffic (0.0.0.0/0 or ::/0) in one direction and all response traffic on all ports in the other; untracked flows stop as soon as the rule changes. On Nitro instances the idle TCP established timeout defaults to 432,000 seconds on most instance types (350 seconds on Nitro v6, excluding P6e-GB200) and can be set per network interface between 60 and 432,000 seconds.
- Azure: NSG rule changes apply only to new connections. Microsoft's example is an SSH session that stays connected after the allowing rule is removed, while new SSH attempts are blocked.
- Google Cloud: Cloud NGFW checks the connection tracking table before evaluating rules, and an existing tracking entry takes precedence over a later deny rule. Tracking stays active while at least one packet passes every 10 minutes, and return traffic for an allowed connection cannot be denied by a rule.
For incident response, removing an attacker's inbound rule does not end their session. On AWS a subnet ACL deny ends it; on Azure and Google Cloud also act at the workload (isolate the VM, revoke credentials).
What none of these filters see
Each platform exempts some platform traffic from its filters by default, and these exemptions are where filtering assumptions fail.
| Platform | Traffic outside the filter by default | What to use instead |
|---|---|---|
| AWS | Security groups do not filter Amazon DNS, DHCP, EC2 instance metadata, ECS task metadata, Windows licence activation, Amazon Time Sync Service and reserved router addresses | Require IMDSv2 with a hop limit; filter DNS with Route 53 Resolver DNS Firewall |
| Azure | NSGs do not filter platform traffic to 168.63.129.16 and 169.254.169.254 by default | Deny rules using the AzurePlatformDNS, AzurePlatformIMDS and AzurePlatformLKM service tags, after testing (blocking IMDS breaks managed identity token requests) |
| Google Cloud | Traffic to and from the metadata server at 169.254.169.254 (and fd20:ce::254) and traffic that stays inside the VM are not subject to firewall rules | Scope the attached service account tightly and control what can make requests from the VM |
The metadata exemption is the important one: a web flaw that makes a server fetch its own metadata endpoint returns cloud credentials, and no security group, NSG or firewall rule sees it. That is the pattern in the Capital One breach, where a firewall's role credentials were read through the metadata service. See the Cloud Instance Metadata Services Compared Guide and the EC2 Instance Metadata Service Hardening Guide.
Where identity fits
The strongest cloud rules name a workload identity instead of an address. AWS security groups can reference another security group, so the database allows "the application tier" rather than a list of IPs. Azure application security groups let NSG rules use a group of VMs as source or destination. Google Cloud VPC firewall rules can target by service account or by network tag, and Google notes that a service account target is controlled through the Service Account User role and needs a stop and restart to change, unlike a tag. Treat the permission to change tags, groups or rules as privileged. Adversaries who change cloud firewall rules map to ATT&CK T1686 Disable or Modify System Firewall. See the Google Cloud VPC and Cloud NGFW Security Guide and the Azure Virtual Network Security Guide.
Design patterns that hold up
- Intent at the workload, guardrails outside: write per-application allow lists in security groups, NIC-level or subnet NSGs with application security groups, or VPC firewall rules targeted by service account. Put a short list of organisation-wide denies (remote administration ports from the internet, known bad ranges) in an AWS network ACL, an Azure security admin Deny rule or a Google Cloud hierarchical policy. Application teams then cannot override the guardrail.
- One NSG layer per path on Azure: follow Microsoft's advice to attach an NSG to the subnet or the NIC, not both, unless you have a written reason. Two layers double the rules and the troubleshooting.
- Simple, numbered network ACLs on AWS: number in steps of 10 or 100 so a deny can be inserted above an allow, open return traffic on 1024 to 65535, and keep the rule count well below the quota. AWS sets a default of 20 rules per ACL per direction, adjustable to 40, and notes that raising it may affect network performance.
- Few groups, referenced not duplicated: AWS defaults to 60 rules per security group and 5 groups per interface, and the product of the two cannot exceed 1,000. Reference tier groups instead of listing addresses.
- Everything as code: manage rules through reviewed infrastructure as code and scan before merge. See the Checkov and IaC Scanners Guide.
Decision guidance: when to use each layer
| Need | AWS | Azure | Google Cloud |
|---|---|---|---|
| Per-application allow list | Security group referencing tier groups | NSG rules using application security groups | VPC firewall rule or network firewall policy targeted by service account |
| Organisation-wide deny that teams cannot override | Network ACL deny (per subnet), managed centrally | Security admin Deny rule | Hierarchical firewall policy deny at organisation or folder |
| Cut an active tracked session now | Network ACL deny for the flow | Rule change plus action at the workload | Rule change plus action at the workload |
| Allow traffic a project must not block | Central account owns the security group | Security admin Always allow | Hierarchical policy allow |
Small organisations can run on workload-level stateful rules alone, with flow logs and change alerts, adding an AWS ACL only for a specific block list. Large organisations should separate the guardrail owner (central team) from the rule owner (application team) using the layer built for that split.
Worked scenario: a three-tier application on each platform
A web tier behind a load balancer calls an application tier on TCP 8443, which calls a database on TCP 5432. Administrators reach hosts only through a bastion service.
- AWS: the web group allows 443 from the load balancer group; the application group allows 8443 from the web group; the database group allows 5432 from the application group. A shared network ACL on all three subnets denies 22 and 3389 from 0.0.0.0/0 at rule 100, then allows inbound and outbound to the VPC range and return traffic on 1024 to 65535. A return rule written as 32768 to 61000 would fail load balancer traffic.
- Azure: one NSG per subnet with application security groups for web, app and data; an explicit deny for VirtualNetwork to VirtualNetwork at priority 4000 so the 65000 default no longer flattens the network; allows for 8443 and 5432 between groups above it. A security admin Deny for 22, 3389 and 445 from the internet sits above all of it.
- Google Cloud: each tier runs as its own service account. VPC firewall rules allow 8443 to the app service account from the web service account and 5432 to the database service account from the app service account. An organisation-level hierarchical policy denies 22 and 3389 from 0.0.0.0/0 and ends with goto_next for everything else.
The review question is always the same: for each flow, list every layer it crosses in order, and confirm no lower layer can override a guardrail.
Implementation and rollback
- Inventory: export every security group, network ACL, NSG, security admin configuration, VPC firewall rule and firewall policy, with attachment points. Flag rules open to 0.0.0.0/0 or ::/0 and Azure subnets relying on the 65000 defaults.
- Observe: turn on flow logs for two to four weeks to learn real flows before tightening.
- Tighten the workload layer first: replace address-based rules with group, application security group or service account references, environment by environment.
- Add guardrails second: deploy the organisation-wide denies in a non-production scope, then production. Test with new connections, because tracked sessions will not show the effect.
- Verify reachability: check effective rules before and after each change (on Azure, effective security rules, IP flow verify and the Virtual Network Manager network verifier).
- Roll back: every change is a code commit; rollback is a redeploy of the previous version. Keep a pre-approved emergency change to widen an AWS ACL return range or remove a guardrail rule.
Detection and measurement
- Log: every rule and policy change in the control-plane audit log, alerting on rules that open administration ports to the internet.
- Metric: internet-open admin rules. Rules allowing 22, 3389 or 445 from 0.0.0.0/0 or ::/0 across all three layers. Target zero.
- Metric: guardrail coverage. Percentage of networks covered by an organisation-level deny (ACL, security admin rule or hierarchical policy). Target 100%.
- Metric: identity-based rules. Percentage of workload rules that reference groups, application security groups or service accounts instead of raw addresses.
- Metric: unexplained denies. Weekly denied internal flows; spikes after a change point to missing return rules.
Common failure patterns
- Removing a rule and calling it containment: tracked sessions continue on all three platforms.
- Trusting the default "allow internal" rule: Azure's AllowVNetInBound and AllowVnetOutBound at 65000 permit everything inside the VirtualNetwork tag until you add your own deny.
- Tags as security boundaries: Google Cloud network tags can be changed by anyone with edit rights on the instance.
- One wide group among many: on AWS any allow in any attached group opens the interface; one legacy group with a broad rule undoes the rest.
Framework mapping
| Framework | Reference | How the controls in this guide apply |
|---|---|---|
| NIST SP 800-53 Rev 5 | SC-7 Boundary Protection; SC-7(5) Deny by Default, Allow by Exception; AC-4 Information Flow Enforcement; CM-7 Least Functionality | Layered stateful and stateless filtering, explicit denies above permissive defaults, only required ports |
| NIST CSF 2.0 | PR.IR-01 | Networks protected from unauthorised logical access |
| CIS Controls v8.1 | Control 12 Network Infrastructure Management; Control 13 Network Monitoring and Defense | Rules as code, flow logging, alerts on rule changes |
| ISO/IEC 27001:2022 Annex A | A.8.20 Networks security; A.8.22 Segregation of networks | Tier segmentation and central guardrails |
| MITRE ATT&CK v20 | T1686 Disable or Modify System Firewall | Detect unauthorised rule and policy changes |
Practitioner checklist
- Every flow is documented with each layer it crosses, in the platform's evaluation order.
- AWS network ACLs allow return traffic on 1024 to 65535 and number rules with gaps.
- Azure subnets carry an explicit internal deny above the 65000 defaults, with NSGs on subnet or NIC, not both.
- Google Cloud sensitive tiers are targeted by service account, not network tag, and the enforcement order setting is a deliberate choice.
- Organisation-wide denies live in the layer application teams cannot override.
- Incident playbooks cover tracked sessions and metadata access, which filters do not stop.
- All rules are managed as code, and console changes in production raise alerts.
Standards and references
- AWS: Infrastructure security in Amazon VPC (security groups and network ACLs compared)
- AWS: Custom network ACLs and ephemeral ports
- AWS: Security group connection tracking
- Microsoft Learn: How network security groups filter network traffic
- Microsoft Learn: Security admin rules in Azure Virtual Network Manager
- Google Cloud: Policy and rule evaluation order
- Google Cloud: VPC firewall rules
- NIST: SP 800-53 Rev. 5 Security and Privacy Controls
Related NHI Mgmt Group resources: Cloud Network Security Guide, Amazon VPC Security Guide, Azure Virtual Network Security Guide, Google Cloud VPC and Cloud NGFW Security Guide, VLAN Segmentation vs Firewall Zones.