This post explains how small and mid-sized organizations can implement deny-by-default network controls across AWS, Azure, and GCP to satisfy NIST SP 800-171 Rev.2 / CMMC 2.0 Level 2 requirement SC.L2-3.13.6 β with concrete steps, configuration examples, and operational controls you can apply today.
What SC.L2-3.13.6 Requires and how "deny-by-default" maps to cloud controls
SC.L2-3.13.6 expects that systems deny network traffic by default and only permit explicitly authorized flows. In cloud environments that translates to: block all inbound and/or outbound traffic except for explicitly allowed ports, protocols, sources, and destinations; maintain an approval trail for exceptions; and log network activity for verification. The practical goal is least-privilege network access enforced by cloud-native controls (security groups, firewall policies, NSGs, VPC firewall rules, NACLs, etc.) plus monitoring and policy enforcement (Config/Policy engines, flow logs, alerting).
Step 1 β Inventory, intent, and risk-based whitelist
Before changing rules, inventory all application and admin network flows: which servers need outbound HTTPS, which services need to receive traffic on port 443, which management ports need to be open for a limited admin subnet or bastion only. Create a minimal whitelist that maps: (source CIDR / source tag) -> (destination CIDR / destination tag) -> protocol -> port -> purpose and owner. This is your authorization artifact for SC.L2-3.13.6 evidence. For small businesses, start by listing critical services (web front end, API, database, management) and annotate business need and justification for each allowed flow.
Cloud-specific implementation examples
AWS (VPC / Security Groups / NACLs / Network Firewall)
Best practice in AWS is βdeny by absenceβ with stateful security groups and optional stateless NACL denies for edge protection. Security groups are evaluated as an implicit deny for anything not explicitly allowed; that fits the control. Implementation steps:
- Remove permissive default rules and avoid using the VPC default security group for production.
- Create per-role security groups (web-sg, app-sg, db-sg) with only necessary inbound rules; e.g., web-sg inbound 443 from 0.0.0.0/0 if public HTTPS is required; db-sg inbound 5432 only from app-sg (use security-group source reference).
- Restrict egress where possible (default SG egress=allow all; replace it with explicit allow egress rules such as 443 to required external services).
- Use NACLs on public subnets to place explicit DENY entries for known bad IP ranges or to deny all inbound except your required port ranges. NACL rules are numbered 1-32766 and are evaluated by rule number; include a broad DENY at the appropriate place when needed.
# minimal AWS security group example (Terraform/HCL-like pseudocode)
resource "aws_security_group" "web" {
name = "web-sg"
vpc_id = aws_vpc.main.id
# allow inbound HTTPS from Internet
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# allow egress only to specific endpoints
egress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"] # e.g., internal services or necessary external IPs
}
}
Azure (NSG / Azure Firewall / Azure Policy)
Azure NSGs include default rules but you should create explicit custom NSGs and apply them at subnet or NIC level to enforce deny-by-default behavior. NSG rules use priorities 100β4096 (lower evaluated first) and default rules exist with reserved priorities (AllowVNet, AllowAzureLoadBalancer, DenyAll at priority 65500). Recommended steps:
- Remove or override permissive rules on default networks; apply NSGs with no inbound allow except approved entries. Use lower-numbered custom rules for explicit allows (e.g., priority 100 allow 443 from Internet if needed) and rely on default DenyAll to block the rest.
- Use Azure Firewall to centralize egress and application-level filtering for outbound HTTP/S and DNS; configure application rules and network rules with explicit allow lists and a default deny.
- Enforce NSG and Firewall deployment via Azure Policy initiatives so new subnets/vnets cannot be created without baseline protections.
# Azure NSG example (pseudo-ARM snippet)
{
"name": "nsg-web",
"properties": {
"securityRules": [
{
"name": "Allow-HTTPS-Internet",
"properties": {
"priority": 100,
"protocol": "Tcp",
"access": "Allow",
"direction": "Inbound",
"sourceAddressPrefix": "*",
"sourcePortRange": "*",
"destinationAddressPrefix": "*",
"destinationPortRange": "443"
}
}
// remainder denied by default
]
}
}
GCP (VPC Firewall rules / Hierarchical Firewall Policies)
GCP firewall rules evaluate by priority (0β65535). The default network historically included "allow" rules β remove these in production VPCs. For deny-by-default:
- Start with an empty VPC firewall policy and create explicit allow rules for necessary flows (lower numeric priority = evaluated earlier).
- Use "deny" rules with a lower priority number to override broader allows if needed. To implement a true deny-by-default at the organization level, create a hierarchical firewall policy that applies a final DENY rule (high priority value) for undesired traffic, then add allowed rules for authorized flows at lower-number priorities.
- Use VPC Service Controls and Private Google Access/Private Service Connect to avoid exposing managed services publicly.
# GCP firewall rule (gcloud-like)
gcloud compute firewall-rules create allow-web-https \
--network=prod-vpc \
--allow=tcp:443 \
--source-ranges=0.0.0.0/0 \
--priority=1000
# Then ensure no "allow-icmp" or "allow-rdp" broad rules exist
Operational controls, monitoring, and evidence for auditors
Configuration is not enough β enforce and monitor. Enable VPC Flow Logs (AWS VPC Flow Logs, Azure NSG Flow Logs via Network Watcher, GCP VPC Flow Logs) and ship to a long-term archive (S3/Blob Storage/GCS) for retention and audit. Use cloud-native detection services (GuardDuty, Azure Defender/Threat Protection, GCP Security Command Center) and alert on anomalous flows. Enforce the network baseline using policy-as-code: AWS Config + Config Rules, Azure Policy initiatives, GCP Organization Policies / Forseti (or built-in firewall policies). Keep IaC templates (Terraform/ARM/Bicep/Cloud Deployment Manager) and PR-based change control as compliance evidence of approved exceptions.
Small-business scenarios and practical tips
Scenario A β Small web app with DB: Place the web tier in a public subnet with a web-sg allowing 443 from Internet. Place the DB in a private subnet with db-sg allowing DB port only from app-sg. Use an SSM Session Manager (AWS) or Azure Bastion (Azure) or Identity-Aware Proxy + IAM (GCP) for admin access so you never open SSH/RDP to the Internet. Scenario B β Remote workforce: avoid opening RDP/SSH; use managed VPN or clientless solutions (SSO + Identity-Aware Proxy) and restrict access by identity and conditional access policies. For small teams, default to blocking all outbound except necessary services (updates, package repo, SaaS APIs) and use a single egress proxy/Firewall to centralize and log traffic.
Compliance tips, best practices and common pitfalls
- Start with a whitelist and change-control process: every allow must have a ticket, an owner, and an expiration where feasible.
- Use tags and naming conventions for security groups / NSGs / firewall rules so rules can be audited by automation (e.g., name "allow-web-443-prod-OWNER").
- Automate drift detection with Config/Policy and remediate with automation (Lambda/Functions/DeployIfNotExists policy).
- Test with simulated attacks and traffic generators before and after rule changes to ensure business continuity.
- Beware of "wide-open" egress β attackers use outbound channels to exfiltrate data; restrict and monitor egress.
- Keep a minimal management plane open: prefer managed session solutions (SSM, Bastion, IAP) rather than persistent admin ports to the internet.
Risk of not implementing deny-by-default controls
Without deny-by-default controls you increase risk of lateral movement, data exfiltration, and unauthorized access; permissive rules are a top cause of cloud breaches. For organizations pursuing DoD contracts, non-compliance with SC.L2-3.13.6 can lead to failing CMMC assessments and loss of contracting eligibility. Operationally, permissive egress rules amplify the blast radius of compromised credentials (malware calling home, API keys used to exfiltrate). From an evidence perspective, lacking documented approvals and logs will fail auditors even if the technical controls exist.
Summary
To meet NIST SP 800-171 Rev.2 / CMMC 2.0 Level 2 requirement SC.L2-3.13.6, adopt a deny-by-default posture across AWS, Azure and GCP by: building an authorized whitelist, implementing least-privilege security groups/NSGs/firewall policies, centralizing egress controls, using policy-as-code to enforce baseline, and enabling flow/logging and monitoring for audit evidence. For small businesses, apply pragmatic controls like bastion/session-manager-based admin access, per-role security groups, and a central egress firewall β and codify all exceptions with owners and review windows to maintain compliance and reduce risk.