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 Type | Capacity per Rule | Notes |
|---|---|---|
| Stateless rules | ~3-5 units | Simple header matching |
| 5-tuple stateful rules | ~1-3 units | Basic stateful filtering |
| Domain list rules | ~1 unit per domain | HTTP/HTTPS domain filtering |
| Suricata IPS rules | ~10-100+ units | Varies greatly by complexity |
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 unitsThreatSignaturesPhishingStrictOrder: 4,200 unitsThreatSignaturesMalwareMobileStrictOrder: 4,000 units- Most other managed groups: 200-3,500 units
When using AWS managed rule groups, start with the most critical ones:
AttackInfrastructureStrictOrder(15,000 units) - comprehensive protection- High-value, low-capacity groups like domain lists (200 units each)
- 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
If you reach capacity limits, you cannot add more rules without:
- Removing existing rules
- Creating new rule groups with higher capacity (and migrating rules)
- 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 packetaws:pass- Allow the packet throughaws: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 headerTLS_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) orANY(bidirectional) - Action:
PASS,DROP,ALERT, orREJECT
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:
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 Group | Description | Capacity | False Positive Risk |
|---|---|---|---|
AttackInfrastructureStrictOrder | Real-time protection from active attack infrastructure | 15000 | Low |
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 Group | Description | Capacity | False Positive Risk |
|---|---|---|---|
MalwareDomainsStrictOrder | Known malicious domains | 200 | Low |
AbusedLegitMalwareDomainsStrictOrder | Legitimate domains compromised for malware | 200 | Low |
BotNetCommandAndControlDomainsStrictOrder | Active botnet C2 infrastructure | 200 | Low |
AbusedLegitBotNetCommandAndControlDomainsStrictOrder | Legitimate domains used for botnet C2 | 200 | Low |
3. Threat Signature Rule Groups
Comprehensive threat detection covering malware, exploits, web attacks, phishing, and more:
Botnet Detection:
| Rule Group | Description | Capacity | False Positive Risk |
|---|---|---|---|
ThreatSignaturesBotnetStrictOrder | General botnet command & control signatures | 4200 | Low |
ThreatSignaturesBotnetWebStrictOrder | Web-based botnet traffic | 3500 | Low |
ThreatSignaturesBotnetWindowsStrictOrder | Windows-specific botnet signatures | 3400 | Low |
Malware Detection:
| Rule Group | Description | Capacity | False Positive Risk |
|---|---|---|---|
ThreatSignaturesMalwareStrictOrder | General malware signatures | 1300 | Medium |
ThreatSignaturesMalwareWebStrictOrder | Web-delivered malware | 3300 | Medium |
ThreatSignaturesMalwareMobileStrictOrder | Mobile malware signatures | 4000 | Low |
ThreatSignaturesMalwareCoinminingStrictOrder | Cryptocurrency mining traffic | 2200 | Low |
Attack Detection:
| Rule Group | Description | Capacity | False Positive Risk |
|---|---|---|---|
ThreatSignaturesWebAttacksStrictOrder | Web application attacks (SQLi, XSS, etc.) | 1400 | High |
ThreatSignaturesDoSStrictOrder | Denial of Service attack patterns | 200 | Medium |
ThreatSignaturesExploitsStrictOrder | Known vulnerability exploits | 800 | Medium |
Threat Intelligence:
| Rule Group | Description | Capacity | False Positive Risk |
|---|---|---|---|
ThreatSignaturesIOCStrictOrder | Indicators of Compromise from threat feeds | 300 | Medium |
ThreatSignaturesEmergingEventsStrictOrder | Latest emerging threats | 200 | Medium-High |
ThreatSignaturesSuspectStrictOrder | Suspicious activity patterns | 300 | Medium |
Social Engineering & Reconnaissance:
| Rule Group | Description | Capacity | False Positive Risk |
|---|---|---|---|
ThreatSignaturesPhishingStrictOrder | Phishing attempt detection | 4200 | Medium |
ThreatSignaturesScannersStrictOrder | Network scanning and reconnaissance | 200 | Medium |
ThreatSignaturesFUPStrictOrder | Potentially Unwanted Programs | 1100 | Medium |
Best Practices:
Getting Started - Essential Protection:
- ✓ Start with
AttackInfrastructureStrictOrderfor 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
ThreatSignaturesScannersto 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:
- Check all PASS rules (regardless of priority)
- If match found → PASS traffic
- If no match → Check all DROP/ALERT rules
- If match found → DROP/ALERT traffic
- 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 trafficaws: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 rulesaws:alert_established- Alert but allow established connectionsaws: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
- Check rule priority and order
- Verify CIDR ranges and ports are correct
- Review CloudWatch FLOW logs for traffic patterns
- Test with ALERT action before DROP
Performance Issues
- Move common blocks to stateless rules
- Optimize Suricata rule complexity
- Reduce number of domain list entries
- Consider using AWS Managed Rule Groups
False Positives
- Review ALERT logs for specific rule triggers
- Add exceptions using STRICT_ORDER with lower priority
- Use Suricata rule tuning (threshold, suppress)
- 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.