Network Security
TSC mapping: CC6.6 (Logical access restrictions from outside system boundaries), CC6.7 (Data transmission controls)
Network controls are a heavily-evidenced area for SOC 2. Auditors will review VPC designs, security group rules, and WAF configurations. The key principle: deny by default, allow by exception.
1. VPC Design — Defence in Depth
Delete the default VPC
The default VPC has permissive settings and should not be used for production workloads. Auditors flag its existence in active regions.
# List default VPCs across all regions
aws ec2 describe-vpcs \
--filters Name=isDefault,Values=true \
--query 'Vpcs[*].[VpcId,OwnerId]' \
--output table
# Delete the default VPC (after removing its subnets, IGW, etc.)
aws ec2 delete-vpc --vpc-id vpc-xxxxxxxxxxxxxxxxx
Recommended subnet layout
VPC: 10.0.0.0/16
├── Public subnets (10.0.0.0/20, 10.0.16.0/20) ← Load balancers only
├── Private subnets (10.0.128.0/20, 10.0.144.0/20) ← Application layer
└── Data subnets (10.0.192.0/20, 10.0.208.0/20) ← RDS, ElastiCache
Key rules:
- Application servers must be in private subnets with no direct internet access.
- Outbound internet access from private subnets goes through NAT Gateway, not an IGW.
- Data layer subnets have no route to the internet at all.
AWS Config rule:
| Rule | What it checks |
|---|---|
vpc-flow-logs-enabled | Flow Logs enabled for all VPCs |
Reference: Amazon VPC documentation →
2. Security Groups — Least Privilege
Security groups are stateful firewalls. SOC 2 auditors specifically look for 0.0.0.0/0 inbound rules on administrative ports.
Audit for unrestricted access
# Find security groups allowing inbound SSH (port 22) from anywhere
aws ec2 describe-security-groups \
--filters "Name=ip-permission.from-port,Values=22" \
"Name=ip-permission.cidr,Values=0.0.0.0/0" \
--query 'SecurityGroups[*].[GroupId,GroupName]' \
--output table
# Find security groups allowing inbound RDP (port 3389) from anywhere
aws ec2 describe-security-groups \
--filters "Name=ip-permission.from-port,Values=3389" \
"Name=ip-permission.cidr,Values=0.0.0.0/0" \
--query 'SecurityGroups[*].[GroupId,GroupName]' \
--output table
Revoke an overly permissive rule
aws ec2 revoke-security-group-ingress \
--group-id sg-xxxxxxxxxxxxxxxxx \
--protocol tcp \
--port 22 \
--cidr 0.0.0.0/0
Recommended security group pattern
Load Balancer SG:
Inbound: 443 from 0.0.0.0/0 (HTTPS only)
80 from 0.0.0.0/0 (redirect to HTTPS)
Outbound: 8080 to App SG
App SG:
Inbound: 8080 from Load Balancer SG
Outbound: 5432 to DB SG
443 to 0.0.0.0/0 (external API calls, via NAT)
DB SG:
Inbound: 5432 from App SG
Outbound: (no outbound needed)
AWS Config rules:
| Rule | What it checks |
|---|---|
restricted-ssh | No SG allows unrestricted SSH inbound |
restricted-common-ports | No SG allows unrestricted access on common admin ports |
vpc-sg-open-only-to-authorized-ports | Only specific ports allowed from internet |
3. Network ACLs
NACLs are stateless and operate at the subnet level — a second layer of defence behind security groups.
Recommended NACL rules for private subnets:
| Rule # | Type | Protocol | Port | Source | Allow/Deny |
|---|---|---|---|---|---|
| 100 | Inbound | TCP | 1024-65535 | 0.0.0.0/0 | ALLOW (return traffic) |
| 200 | Inbound | TCP | 443 | 10.0.0.0/16 | ALLOW (internal HTTPS) |
| 32766 | Inbound | All | All | 0.0.0.0/0 | DENY |
| 100 | Outbound | TCP | 443 | 0.0.0.0/0 | ALLOW |
| 200 | Outbound | TCP | 1024-65535 | 0.0.0.0/0 | ALLOW (ephemeral ports) |
| 32766 | Outbound | All | All | 0.0.0.0/0 | DENY |
4. VPC Flow Logs
Flow Logs capture source/destination IPs, ports, protocols, and allow/deny decisions for all traffic through every ENI. Required for CC7.2 (monitoring) evidence and feeds GuardDuty threat detection.
# Enable for all VPCs in a region (run per region)
for vpc in $(aws ec2 describe-vpcs --query 'Vpcs[*].VpcId' --output text); do
aws ec2 create-flow-logs \
--resource-type VPC \
--resource-ids $vpc \
--traffic-type ALL \
--log-destination-type cloud-watch-logs \
--log-group-name /aws/vpc/flowlogs \
--deliver-logs-permission-arn arn:aws:iam::<account>:role/flowlogs-role
echo "Enabled flow logs for $vpc"
done
Verify:
aws ec2 describe-flow-logs \
--filter "Name=resource-id,Values=vpc-xxxxxxxxxxxxxxxxx" \
--query 'FlowLogs[*].[FlowLogId,FlowLogStatus,LogDestinationType]'
Reference: VPC Flow Logs documentation →
5. AWS WAF — Web Application Firewall
WAF protects public-facing load balancers, API Gateways, and CloudFront distributions from OWASP Top 10 attacks. Required for CC6.6 (external threat protection).
# Create a WAF Web ACL with the AWS Managed Rule Groups
aws wafv2 create-web-acl \
--name soc2-web-acl \
--scope REGIONAL \
--default-action Allow={} \
--rules '[
{
"Name": "AWSManagedRulesCommonRuleSet",
"Priority": 1,
"OverrideAction": {"None": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "CommonRuleSet"
},
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesCommonRuleSet"
}
}
},
{
"Name": "AWSManagedRulesKnownBadInputsRuleSet",
"Priority": 2,
"OverrideAction": {"None": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "KnownBadInputs"
},
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesKnownBadInputsRuleSet"
}
}
}
]' \
--visibility-config \
SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=soc2-web-acl \
--region us-east-1
# Associate with an ALB
aws wafv2 associate-web-acl \
--web-acl-arn arn:aws:wafv2:<region>:<account>:regional/webacl/soc2-web-acl/<id> \
--resource-arn arn:aws:elasticloadbalancing:<region>:<account>:loadbalancer/app/<name>/<id>
Enable WAF logging to S3 or CloudWatch:
aws wafv2 put-logging-configuration \
--logging-configuration \
ResourceArn=arn:aws:wafv2:<region>:<account>:regional/webacl/soc2-web-acl/<id>,\
LogDestinationConfigs=arn:aws:firehose:<region>:<account>:deliverystream/aws-waf-logs-soc2
Reference: AWS WAF documentation →
6. Encryption in Transit
Enforce TLS on all public-facing and internal service communication. This is required for CC6.7.
Enforce HTTPS on S3
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyNonHTTPS",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
],
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
}
]
}
Enforce TLS 1.2+ on ALB
# Set the SSL policy on an HTTPS listener
aws elbv2 modify-listener \
--listener-arn arn:aws:elasticloadbalancing:...:listener/app/... \
--ssl-policy ELBSecurityPolicy-TLS13-1-2-2021-06
Use AWS Certificate Manager for public certificates
# Request a public certificate (auto-renewed)
aws acm request-certificate \
--domain-name "api.example.com" \
--validation-method DNS \
--subject-alternative-names "*.example.com"
AWS Config rules:
| Rule | What it checks |
|---|---|
alb-http-to-https-redirection-check | ALB HTTP listeners redirect to HTTPS |
acm-certificate-expiration-check | ACM certs expire in more than 90 days |
elb-tls-https-listeners-only | ELB listeners use only HTTPS/TLS |
Reference: AWS WAF documentation → · Amazon VPC documentation →
SOC 2 Evidence for Network Security
| Evidence item | How to collect |
|---|---|
| VPC and subnet configuration | aws ec2 describe-vpcs, aws ec2 describe-subnets |
| Security group rules | aws ec2 describe-security-groups |
| Flow Logs configuration | aws ec2 describe-flow-logs |
| WAF Web ACL rules | aws wafv2 list-web-acls + get-web-acl |
| ALB TLS policy | aws elbv2 describe-listeners |
| Config rule compliance (network rules) | AWS Config console → Compliance by rule |
| ACM certificate list | aws acm list-certificates |