Skip to main content

Firewall Policies

AWS Network Firewall policies define how traffic is inspected and filtered through your firewall. Understanding the different rule types, processing order, and policy configurations is essential for building effective network security controls.

Overview

A Network Firewall policy consists of multiple components that work together to inspect and control network traffic:

  • Stateless Rule Groups: Fast, wire-speed packet filtering based on headers
  • Stateful Rule Groups: Deep packet inspection with connection tracking
  • AWS Managed Rule Groups: Pre-configured threat intelligence from AWS
  • Default Actions: What to do with traffic that doesn't match any rules

Rule Group Capacity Units

Every rule group in AWS Network Firewall has a capacity setting that determines how many rules and how much complexity it can contain. Understanding capacity is critical for planning and scaling your firewall policies.

What are Capacity Units?

Capacity units represent the processing resources required for a rule group. Different rule types consume different amounts of capacity:

Rule TypeCapacity per RuleNotes
Stateless rules~3-5 unitsSimple header matching
5-tuple stateful rules~1-3 unitsBasic stateful filtering
Domain list rules~1 unit per domainHTTP/HTTPS domain filtering
Suricata IPS rules~10-100+ unitsVaries greatly by complexity
Capacity is Immutable

Once a rule group is created, its capacity cannot be changed. You must delete and recreate the rule group with a new capacity value. Plan ahead with generous capacity headroom.

Capacity Considerations

1. Plan for Growth

  • ✓ Always provision 20-30% extra capacity beyond current needs
  • ✓ Consider future rule additions and modifications
  • ✓ Account for rule complexity increases over time

2. Firewall-Level Capacity Limits Each firewall has a total capacity limit of 30,000 units across all rule groups. This includes:

  • Stateless rule groups
  • Stateful rule groups (custom + AWS managed)
# Example: Calculating total capacity
Stateless rule groups: 2,000 units
Custom stateful rule groups: 5,000 units
AWS managed rule groups: 18,000 units
────────────────────────────────────────────
Total: 25,000 units (within 30,000 limit)

3. AWS Managed Rule Group Capacity AWS managed rule groups have fixed capacity values that count toward your 30,000-unit limit:

  • AttackInfrastructureStrictOrder: 15,000 units (largest)
  • ThreatSignaturesBotnetStrictOrder: 4,200 units
  • ThreatSignaturesPhishingStrictOrder: 4,200 units
  • ThreatSignaturesMalwareMobileStrictOrder: 4,000 units
  • Most other managed groups: 200-3,500 units
Managed Rule Group Strategy

When using AWS managed rule groups, start with the most critical ones:

  1. AttackInfrastructureStrictOrder (15,000 units) - comprehensive protection
  2. High-value, low-capacity groups like domain lists (200 units each)
  3. Add additional signature groups based on remaining capacity

4. Suricata Rule Complexity

Suricata IPS rules vary significantly in capacity consumption:

# Simple signature: ~10-20 units
alert tcp any any -> any 4444 (msg:"Metasploit"; sid:1;)

# Complex signature with multiple content matches: ~50-100+ units
alert http any any -> any any (
msg:"SQL Injection";
flow:established,to_server;
content:"SELECT"; http_uri; nocase;
content:"UNION"; http_uri; nocase;
pcre:"/(\%27)|(\')|(\-\-)|(\%23)|(#)/i";
sid:2;
)

Estimating Suricata capacity:

  • Basic rules (protocol/port only): 10-20 units
  • Content matching (1-2 patterns): 30-50 units
  • Complex PCRE/multiple content: 80-150 units
  • Rules with many conditions: 150-300+ units

5. Best Practices

Start conservatively:

  • Stateless groups: 100-500 units per group
  • 5-tuple groups: 100-500 units per group
  • Domain lists: 100-200 units per group
  • Suricata groups: 500-2,000 units per group (depending on complexity)

Monitor capacity usage:

# Check current rule group capacity
aws network-firewall describe-rule-group \
--rule-group-name my-rule-group \
--query 'RuleGroup.RulesSource.RulesString' \
--output text | wc -l

Plan for the 30,000-unit limit:

  • Reserve 15,000+ units for AWS managed rule groups if using comprehensive protection
  • Allocate remaining 15,000 units across custom rule groups
  • Leave buffer for future additions (3,000-5,000 units)

Capacity exhaustion strategy:

  • Create multiple rule groups to distribute capacity
  • Use specific rule groups for different use cases
  • Prioritize critical rules in higher-priority groups
Capacity Planning Failure

If you reach capacity limits, you cannot add more rules without:

  1. Removing existing rules
  2. Creating new rule groups with higher capacity (and migrating rules)
  3. Removing AWS managed rule groups

Plan capacity carefully from the beginning!


## Rule Processing Order

Understanding how rules are processed is critical for designing effective firewall policies.

### Processing Flow

┌─────────────────────────────────────────────────────────┐ │ 1. STATELESS RULES (Wire Speed) │ │ • Header-based filtering │ │ • No connection tracking │ │ • Actions: DROP, PASS, or FORWARD to stateful │ ├─────────────────────────────────────────────────────────┤ │ 2. STATEFUL RULES (Deep Inspection) │ │ • Connection tracking and state awareness │ │ • Application-layer inspection │ │ • Domain filtering, 5-tuple rules, Suricata IPS │ ├─────────────────────────────────────────────────────────┤ │ 3. DEFAULT ACTION │ │ • Applied when no rules match │ │ • Typically: DROP (deny all) or PASS (allow all) │ └─────────────────────────────────────────────────────────┘


### 1. Stateless Rules (First Pass)

Stateless rules are evaluated **first** and provide wire-speed filtering with minimal latency:

**Characteristics:**
- No connection tracking or state awareness
- Based only on packet headers (IP, port, protocol)
- Highest throughput, lowest latency
- Cannot inspect packet payload

**Common Use Cases:**
```hcl
stateless_rule_groups = [
{
name = "block-obvious-threats"
priority = 10
capacity = 100
rules = [
{
priority = 1
actions = ["aws:drop"]
# Block SSH from internet
protocols = ["TCP"]
source_cidrs = ["0.0.0.0/0"]
destination_port = { from_port = 22, to_port = 22 }
destination_cidrs = ["10.0.0.0/8"]
},
{
priority = 2
actions = ["aws:forward_to_sfe"] # Forward to stateful engine
protocols = ["TCP"]
}
]
}
]

Available Actions:

  • aws:drop - Silently drop the packet
  • aws:pass - Allow the packet through
  • aws:forward_to_sfe - Send to stateful engine for deep inspection

2. Stateful Rules (Deep Inspection)

Stateful rules provide deep packet inspection and application awareness:

Characteristics:

  • Connection tracking (understands TCP sessions)
  • Application-layer protocol detection
  • Can inspect packet payload
  • Supports domain filtering and IPS signatures

Stateful Rule Types

Domain List Rules

Filter traffic based on domain names - ideal for egress control:

stateful_rule_groups = [
{
name = "allowed-domains"
rule_group_type = "domain_list"
capacity = 100
priority = 100

domain_list_config = {
type = "ALLOWLIST" # or "DENYLIST"
targets = [
"*.amazonaws.com",
"*.github.com",
"example.com"
]
protocol_types = ["HTTP_HOST", "TLS_SNI"]
}
}
]

List Types:

  • ALLOWLIST: Only specified domains are allowed (deny all others)
  • DENYLIST: Only specified domains are blocked (allow all others)

Protocol Types:

  • HTTP_HOST - Inspects HTTP Host header
  • TLS_SNI - Inspects TLS Server Name Indication (SNI)

Use Cases:

  • ✓ Restrict egress to approved SaaS services
  • ✓ Block known malicious domains
  • ✓ Compliance requirements (only allow business-critical domains)
  • ✓ Prevent data exfiltration to unauthorized destinations

5-Tuple Rules

Traditional firewall rules based on source/destination IP, port, and protocol:

stateful_rule_groups = [
{
name = "application-access"
rule_group_type = "5tuple"
capacity = 100
priority = 200

five_tuple_config = {
rules = [
{
action = "PASS"
protocol = "TCP"
source_cidr = "10.1.0.0/16"
source_port = "ANY"
direction = "FORWARD"
destination_cidr = "10.2.0.0/16"
destination_port = "5432" # PostgreSQL
sid = 1
description = "Allow database access from app tier"
},
{
action = "DROP"
protocol = "TCP"
source_cidr = "10.0.0.0/8"
source_port = "ANY"
direction = "ANY"
destination_cidr = "10.100.0.0/16" # Production VPC
destination_port = "ANY"
sid = 2
description = "Block non-prod to prod traffic"
}
]
}
}
]

Components:

  • Protocol: TCP, UDP, ICMP, or protocol number
  • Source/Destination: IP address or CIDR range
  • Port: Specific port, port range, or "ANY"
  • Direction: FORWARD (outbound) or ANY (bidirectional)
  • Action: PASS, DROP, ALERT, or REJECT

Use Cases:

  • ✓ Control traffic between network tiers (web → app → database)
  • ✓ Enforce network segmentation (production ↔ development)
  • ✓ Allow specific protocols on custom ports
  • ✓ Block unauthorized east-west traffic

Suricata IPS Rules

Advanced threat detection using Suricata-compatible signatures:

stateful_rule_groups = [
{
name = "custom-threat-detection"
rule_group_type = "suricata"
capacity = 500
priority = 300

suricata_config = {
rule_variables = {
HOME_NET = ["10.0.0.0/8", "172.16.0.0/12"]
EXTERNAL_NET = ["0.0.0.0/0"]
HTTP_PORTS = ["80", "443", "8080"]
}

rules_string = <<-EOT
# Detect SQL injection attempts
alert http any any -> $HOME_NET $HTTP_PORTS (
msg:"SQL Injection Attempt";
flow:to_server,established;
content:"SELECT";
http_uri;
sid:1000001;
)

# Block Metasploit default payload
drop tcp any any -> any 4444 (
msg:"Metasploit default payload port";
sid:1000002;
)

# Detect cryptocurrency mining
alert tcp any any -> any [3333,4444,5555,7777] (
msg:"Possible cryptocurrency mining";
sid:1000003;
)
EOT
}
}
]

Rule Variables: Define reusable IP sets and port sets for cleaner rules:

  • $HOME_NET - Your trusted network ranges
  • $EXTERNAL_NET - External networks
  • Custom variables for specific services

Use Cases:

  • ✓ Detect exploitation attempts (SQL injection, XSS)
  • ✓ Identify command & control traffic
  • ✓ Block known attack patterns
  • ✓ Custom threat detection based on your environment
  • ✓ Compliance-specific signature matching

AWS Managed Rule Groups

Pre-configured rule sets maintained and updated by AWS:

aws_managed_rule_groups = [
{
name = "ThreatSignaturesBotnetStrictOrder"
priority = 10
},
{
name = "AbusedLegitMalwareDomainsStrictOrder"
priority = 20
},
{
name = "ThreatSignaturesMalwareStrictOrder"
priority = 30
}
]

Available Rule Groups:

Rule Group Variants

Each rule group is available in two variants: StrictOrder and ActionOrder, depending on your policy's stateful_engine_options_rule_order setting.

AWS provides three categories of managed rule groups:

1. Active Threat Defense Rule Groups

Active threat defense protects against threats known to AWS through deep threat inspection:

Rule GroupDescriptionCapacityFalse Positive Risk
AttackInfrastructureStrictOrderReal-time protection from active attack infrastructure15000Low
Active Threat Defense

This rule group provides the most comprehensive protection with high capacity. It's automatically updated by AWS as new threats are identified.

2. Domain and IP Rule Group

Block HTTP/HTTPS traffic to domains and IPs associated with malware, botnets, or low-reputation sources:

Rule GroupDescriptionCapacityFalse Positive Risk
MalwareDomainsStrictOrderKnown malicious domains200Low
AbusedLegitMalwareDomainsStrictOrderLegitimate domains compromised for malware200Low
BotNetCommandAndControlDomainsStrictOrderActive botnet C2 infrastructure200Low
AbusedLegitBotNetCommandAndControlDomainsStrictOrderLegitimate domains used for botnet C2200Low

3. Threat Signature Rule Groups

Comprehensive threat detection covering malware, exploits, web attacks, phishing, and more:

Botnet Detection:

Rule GroupDescriptionCapacityFalse Positive Risk
ThreatSignaturesBotnetStrictOrderGeneral botnet command & control signatures4200Low
ThreatSignaturesBotnetWebStrictOrderWeb-based botnet traffic3500Low
ThreatSignaturesBotnetWindowsStrictOrderWindows-specific botnet signatures3400Low

Malware Detection:

Rule GroupDescriptionCapacityFalse Positive Risk
ThreatSignaturesMalwareStrictOrderGeneral malware signatures1300Medium
ThreatSignaturesMalwareWebStrictOrderWeb-delivered malware3300Medium
ThreatSignaturesMalwareMobileStrictOrderMobile malware signatures4000Low
ThreatSignaturesMalwareCoinminingStrictOrderCryptocurrency mining traffic2200Low

Attack Detection:

Rule GroupDescriptionCapacityFalse Positive Risk
ThreatSignaturesWebAttacksStrictOrderWeb application attacks (SQLi, XSS, etc.)1400High
ThreatSignaturesDoSStrictOrderDenial of Service attack patterns200Medium
ThreatSignaturesExploitsStrictOrderKnown vulnerability exploits800Medium

Threat Intelligence:

Rule GroupDescriptionCapacityFalse Positive Risk
ThreatSignaturesIOCStrictOrderIndicators of Compromise from threat feeds300Medium
ThreatSignaturesEmergingEventsStrictOrderLatest emerging threats200Medium-High
ThreatSignaturesSuspectStrictOrderSuspicious activity patterns300Medium

Social Engineering & Reconnaissance:

Rule GroupDescriptionCapacityFalse Positive Risk
ThreatSignaturesPhishingStrictOrderPhishing attempt detection4200Medium
ThreatSignaturesScannersStrictOrderNetwork scanning and reconnaissance200Medium
ThreatSignaturesFUPStrictOrderPotentially Unwanted Programs1100Medium

Best Practices:

Getting Started - Essential Protection:

  • ✓ Start with AttackInfrastructureStrictOrder for comprehensive real-time threat defense
  • ✓ Add domain-based protection: MalwareDomainsStrictOrder, BotNetCommandAndControlDomainsStrictOrder
  • ✓ Include low false-positive signature groups: ThreatSignaturesBotnet, ThreatSignaturesMalwareCoinmining

Enhanced Protection - Add Incrementally:

  • ✓ Layer malware detection: ThreatSignaturesMalware, ThreatSignaturesMalwareWeb
  • ✓ Add attack detection: ThreatSignaturesDoS, ThreatSignaturesExploits
  • ✓ Include threat intelligence: ThreatSignaturesIOC, ThreatSignaturesSuspect
  • ✓ Deploy social engineering protection: ThreatSignaturesPhishing

Environment-Specific Rules:

  • ✓ Web applications: Add ThreatSignaturesMalwareWeb, ThreatSignaturesBotnetWeb
  • ✓ Windows environments: Include ThreatSignaturesBotnetWindows
  • ✓ Mobile apps: Use ThreatSignaturesMalwareMobile
  • ✓ Sensitive networks: Add ThreatSignaturesScanners to detect reconnaissance

High-Risk Rules (Test Carefully):

  • ⚠️ ThreatSignaturesWebAttacksStrictOrder - High false positive risk, test separately
  • ⚠️ ThreatSignaturesEmergingEventsStrictOrder - Latest threats but may be unstable
  • ⚠️ Start these in ALERT mode before switching to DROP

Operational Guidelines:

  • ✓ Use override actions to tune specific rules causing false positives
  • ✓ Monitor CloudWatch ALERT logs regularly
  • ✓ Review rule group capacity to ensure sufficient headroom
  • ✓ Update priorities as new rule groups are added
  • ✓ Test new rule groups in non-production environments first

Rule Order: STRICT_ORDER vs DEFAULT_ACTION_ORDER

The stateful_engine_options_rule_order setting determines how stateful rules are processed.

STRICT_ORDER

How it works:

  • Rules are evaluated in priority order (lowest number first)
  • Processing stops when a matching rule is found
  • Similar to traditional firewall behavior
firewall_policy = {
stateful_engine_options_rule_order = "STRICT_ORDER"
}

stateful_rule_groups = [
{
name = "high-priority-rules"
priority = 10 # Evaluated first
},
{
name = "medium-priority-rules"
priority = 20 # Evaluated second
},
{
name = "low-priority-rules"
priority = 30 # Evaluated last
}
]

When to use:

  • ✓ Need predictable, sequential rule evaluation
  • ✓ Specific exceptions should override general rules
  • ✓ Complex policies with conditional logic
  • ✓ Compliance requirements for rule precedence

Example Scenario:

# Priority 10: Allow specific IP to production
# Priority 20: Block all development to production
# Priority 30: General allow rules

With STRICT_ORDER, the specific IP allow (priority 10) is checked before the general block (priority 20).

DEFAULT_ACTION_ORDER

How it works:

  • All PASS rules are evaluated first (any priority)
  • Then all DROP/ALERT rules are evaluated
  • More efficient for simple allow/deny policies
firewall_policy = {
stateful_engine_options_rule_order = "DEFAULT_ACTION_ORDER"
}

stateful_rule_groups = [
{
name = "allow-rules"
priority = 100 # All PASS actions evaluated first
},
{
name = "deny-rules"
priority = 200 # All DROP actions evaluated after
}
]

Processing Logic:

  1. Check all PASS rules (regardless of priority)
  2. If match found → PASS traffic
  3. If no match → Check all DROP/ALERT rules
  4. If match found → DROP/ALERT traffic
  5. If no match → Apply default action

When to use:

  • ✓ Simple allow/deny policies without exceptions
  • ✓ Performance optimization (faster processing)
  • ✓ Clear separation between allow and deny rules
  • ✓ Easier to understand for simple use cases

Example Scenario:

# Allow Rules: *.amazonaws.com, *.github.com
# Deny Rules: *.malicious.com, known bad IPs

With DEFAULT_ACTION_ORDER, all allow rules are checked first, then all deny rules.

Default Actions

Default actions define what happens to traffic that doesn't match any rules.

Stateless Default Actions

firewall_policy = {
stateless_default_actions = ["aws:forward_to_sfe"]
stateless_fragment_default_actions = ["aws:forward_to_sfe"]
}

Options:

  • aws:forward_to_sfe - Send to stateful engine (recommended)
  • aws:pass - Allow all unmatched traffic
  • aws:drop - Drop all unmatched traffic

Best Practice: Use aws:forward_to_sfe to ensure all traffic gets stateful inspection.

Stateful Default Actions

firewall_policy = {
stateful_default_actions = ["aws:drop_established"]
}

Options:

  • aws:drop_established - Drop traffic for established connections (recommended)
  • aws:drop_strict - Drop all traffic that doesn't match rules
  • aws:alert_established - Alert but allow established connections
  • aws:alert_strict - Alert but allow all traffic

Best Practice: Use aws:drop_established for production environments (deny by default).

Stream Exception Policy

Handles packets that don't fit standard TCP flow patterns:

firewall_policy = {
stateful_engine_options_stream_exception_policy = "DROP"
}

Options:

  • DROP - Drop malformed or out-of-order packets (recommended for security)
  • CONTINUE - Allow and continue processing (for troubleshooting)
  • REJECT - Send TCP reset for malformed packets

Policy Design Best Practices

1. Use Stateless Rules for Performance

Block obvious threats at wire speed before stateful inspection:

stateless_rule_groups = [
{
name = "block-common-attacks"
rules = [
# Block SSH/RDP from internet
# Drop packets from known bad IPs
# Rate limit suspicious traffic
]
}
]

2. Layer Your Defense

Combine multiple rule types for defense in depth:

# Layer 1: Stateless - Block obvious threats
# Layer 2: Domain filtering - Control egress
# Layer 3: AWS Managed - Threat intelligence
# Layer 4: Custom Suricata - Application-specific threats
# Layer 5: 5-Tuple - Network segmentation

3. Start Restrictive, Then Open Up

firewall_policy = {
stateful_default_actions = ["aws:drop_established"] # Deny by default
}

stateful_rule_groups = [
{
# Explicitly allow only required traffic
rule_group_type = "domain_list"
domain_list_config = {
type = "ALLOWLIST"
targets = ["*.amazonaws.com"] # Start minimal
}
}
]

4. Use Priorities Strategically

# 1-99:    Stateless rules (wire speed)
# 100-199: Critical exceptions
# 200-299: AWS Managed Rule Groups
# 300-399: Domain filtering
# 400-499: 5-Tuple rules
# 500-599: Custom Suricata rules

5. Monitor and Iterate

  • Review ALERT logs regularly
  • Tune rules based on false positives
  • Use ALERT action before DROP for new rules
  • Implement gradual rollout for policy changes

Common Patterns

Egress Filtering (Allowlist)

firewall_policy = {
stateful_default_actions = ["aws:drop_established"]
}

stateful_rule_groups = [
{
name = "allowed-egress"
rule_group_type = "domain_list"
priority = 100
domain_list_config = {
type = "ALLOWLIST"
targets = [
"*.amazonaws.com",
"*.docker.io",
"*.github.com"
]
}
}
]

East-West Segmentation

stateful_rule_groups = [
{
name = "network-segmentation"
rule_group_type = "5tuple"
priority = 200
five_tuple_config = {
rules = [
{
# Allow prod-to-prod
action = "PASS"
source_cidr = "10.1.0.0/16"
destination_cidr = "10.2.0.0/16"
},
{
# Block dev-to-prod
action = "DROP"
source_cidr = "10.100.0.0/16"
destination_cidr = "10.1.0.0/16"
}
]
}
}
]

Comprehensive Threat Protection

aws_managed_rule_groups = [
{ name = "ThreatSignaturesBotnetStrictOrder", priority = 10 },
{ name = "AbusedLegitMalwareDomainsStrictOrder", priority = 20 },
{ name = "ThreatSignaturesMalwareStrictOrder", priority = 30 }
]

stateful_rule_groups = [
{
name = "custom-threat-detection"
rule_group_type = "suricata"
priority = 300
suricata_config = {
rules_string = <<-EOT
alert http any any -> any any (
msg:"SQL Injection Attempt";
content:"UNION SELECT";
sid:1000001;
)
EOT
}
}
]

Troubleshooting

Rules Not Matching Traffic

  1. Check rule priority and order
  2. Verify CIDR ranges and ports are correct
  3. Review CloudWatch FLOW logs for traffic patterns
  4. Test with ALERT action before DROP

Performance Issues

  1. Move common blocks to stateless rules
  2. Optimize Suricata rule complexity
  3. Reduce number of domain list entries
  4. Consider using AWS Managed Rule Groups

False Positives

  1. Review ALERT logs for specific rule triggers
  2. Add exceptions using STRICT_ORDER with lower priority
  3. Use Suricata rule tuning (threshold, suppress)
  4. Consider switching Web Attacks to ALERT-only

Conclusion

AWS Network Firewall policies provide flexible, powerful network security controls through multiple rule types and processing models. By understanding stateless vs stateful rules, domain filtering, Suricata IPS, and the differences between STRICT_ORDER and DEFAULT_ACTION_ORDER, you can design effective policies that balance security, performance, and operational simplicity. Start with restrictive policies using AWS Managed Rule Groups, layer your defense with multiple rule types, and continuously monitor and tune based on your environment's needs.