Outbound Network Egress Restriction (SSRF Blast Radius Containment)
Description
Network-layer egress filtering that confines application workloads to only the external destinations they require, blocking outbound connections to internal networks, cloud metadata services, and localhost. Implemented via host-based firewall rules (firewalld/nftables), Kubernetes NetworkPolicy egress rules, or cloud security group outbound rules. Complements application-level SSRF prevention (CME-1304) by providing defense-in-depth: even if the application's URL validation is bypassed through DNS rebinding, IPv6-mapped addresses, URL parser differentials, or HTTP redirect chains, the network layer blocks the resulting connection to internal resources. Specifically blocks egress to 169.254.0.0/16 (link-local/cloud metadata), 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 (RFC1918), and fd00::/8 (ULA IPv6) from application workloads that should only communicate externally.
CVSS Vector Impacts
| Metric | Transition | Rationale |
|---|---|---|
| Confidentiality (C) | H → L | Network egress rules block the SSRF payload from reaching internal services, cloud metadata, or localhost regardless of application-layer bypass techniques; exfiltration of internal data is prevented at the transport layer |
| Scope (S) | C → U | SSRF can no longer pivot to affect internal services beyond the vulnerable component; scope change from the application to the internal network is blocked by network-layer isolation |
CWE Relationships
Verification
Verify that application workloads cannot initiate outbound connections to RFC1918 ranges, link-local addresses, or localhost
# Expected: Drop/reject rules for RFC1918 and link-local ranges in the output chain
# Expected: Egress rules present that restrict destination CIDRs to required external endpoints only
# Expected: Connection refused, timed out, or blocked — cloud metadata endpoint unreachable from application workload
# Expected: Outbound block rules present for RFC1918 and link-local ranges
Remediation
printf '#!/usr/sbin/nft -f\ntable inet egress_restrict {\n chain output {\n type filter hook output priority 0; policy accept;\n ip daddr 169.254.0.0/16 drop\n ip daddr 10.0.0.0/8 drop\n ip daddr 172.16.0.0/12 drop\n ip daddr 192.168.0.0/16 drop\n ip daddr 127.0.0.0/8 drop\n ip6 daddr fd00::/8 drop\n ip6 daddr ::1 drop\n }\n}\n' > /etc/nftables/cme-208-egress.nft
grep -q 'cme-208-egress.nft' /etc/sysconfig/nftables.conf 2>/dev/null || echo 'include "/etc/nftables/cme-208-egress.nft"' >> /etc/sysconfig/nftables.conf
nft -f /etc/nftables/cme-208-egress.nft
kubectl apply -n <namespace> -f - <<'EOF'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: cme-208-default-deny-egress
spec:
podSelector: {}
policyTypes:
- Egress
EOF
kubectl apply -n <namespace> -f - <<'EOF'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: cme-208-allow-dns
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
EOF
kubectl apply -n <namespace> -f - <<'EOF'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: cme-208-allow-external
spec:
podSelector:
matchLabels:
app: <app_label>
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: <allowed_cidr>
except:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
- 169.254.0.0/16
EOF
New-NetFirewallRule -DisplayName 'CME-208 Block Egress RFC1918 10/8' -Direction Outbound -Action Block -RemoteAddress 10.0.0.0/8 -Protocol Any
New-NetFirewallRule -DisplayName 'CME-208 Block Egress RFC1918 172.16/12' -Direction Outbound -Action Block -RemoteAddress 172.16.0.0/12 -Protocol Any
New-NetFirewallRule -DisplayName 'CME-208 Block Egress RFC1918 192.168/16' -Direction Outbound -Action Block -RemoteAddress 192.168.0.0/16 -Protocol Any
New-NetFirewallRule -DisplayName 'CME-208 Block Egress Link-Local/Metadata' -Direction Outbound -Action Block -RemoteAddress 169.254.0.0/16 -Protocol Any