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, firewall rules, WAF configurations, and TLS policies. The core principle: deny by default, allow by exception.
1. VPC Design — Defence in Depth
Block default VPC creation at the organization level
# Enforce Organization Policy: prevent default network creation in new projects
gcloud resource-manager org-policies set-policy \
--organization=<org-id> \
- <<'EOF'
name: organizations/<org-id>/policies/compute.skipDefaultNetworkCreation
spec:
rules:
- enforce: true
EOF
Create a custom VPC with private subnets
# Create a custom-mode VPC (no auto-created subnets)
gcloud compute networks create prod-vpc \
--project=<project-id> \
--subnet-mode=custom \
--bgp-routing-mode=regional \
--mtu=1460
# Create private subnets with VPC Flow Logs enabled
gcloud compute networks subnets create snet-app \
--project=<project-id> \
--network=prod-vpc \
--region=us-central1 \
--range=10.0.2.0/24 \
--enable-private-ip-google-access \
--enable-flow-logs \
--logging-aggregation-interval=interval-5-sec \
--logging-flow-sampling=1.0
gcloud compute networks subnets create snet-data \
--project=<project-id> \
--network=prod-vpc \
--region=us-central1 \
--range=10.0.3.0/24 \
--enable-private-ip-google-access \
--enable-flow-logs
Recommended subnet layout:
VPC: prod-vpc (custom mode)
├── snet-lb (10.0.1.0/24) ← External load balancer only
├── snet-app (10.0.2.0/24) ← Application layer — no external IPs
├── snet-data (10.0.3.0/24) ← Cloud SQL, Memorystore (Private Service Access)
└── snet-proxy (10.0.4.0/24) ← Managed proxy subnet for Internal Load Balancer
Enforce no external IPs on VMs via Organization Policy:
gcloud resource-manager org-policies set-policy \
--project=<project-id> \
- <<'EOF'
name: projects/<project-id>/policies/compute.vmExternalIpAccess
spec:
rules:
- denyAll: true
EOF
Reference: VPC networks → · Organization policy constraints →
2. VPC Firewall Rules — Least Privilege
GCP firewall rules are stateful and applied at the network level. SOC 2 auditors specifically look for rules allowing 0.0.0.0/0 inbound on admin ports (22, 3389).
Audit for unrestricted admin access
# Find firewall rules allowing SSH (22) or RDP (3389) from the internet
gcloud compute firewall-rules list \
--project=<project-id> \
--filter="direction=INGRESS AND disabled=false" \
--format="table(name,network,sourceRanges,allowed[].ports.flatten())" | \
grep -E "(:22|:3389|all)"
# Delete an overly permissive rule
gcloud compute firewall-rules delete allow-ssh-from-internet \
--project=<project-id>
Recommended firewall rule pattern
# Allow inbound HTTPS only to the load balancer tier
gcloud compute firewall-rules create allow-https-lb \
--project=<project-id> \
--network=prod-vpc \
--direction=INGRESS \
--priority=100 \
--action=ALLOW \
--rules=tcp:443 \
--source-ranges=0.0.0.0/0 \
--target-tags=load-balancer
# Allow app tier to receive from load balancer only (using network tags)
gcloud compute firewall-rules create allow-app-from-lb \
--project=<project-id> \
--network=prod-vpc \
--direction=INGRESS \
--priority=200 \
--action=ALLOW \
--rules=tcp:8080 \
--source-tags=load-balancer \
--target-tags=app-server
# Allow data tier to receive from app tier only
gcloud compute firewall-rules create allow-db-from-app \
--project=<project-id> \
--network=prod-vpc \
--direction=INGRESS \
--priority=300 \
--action=ALLOW \
--rules=tcp:5432 \
--source-tags=app-server \
--target-tags=db-server
# Deny all other inbound (implicit in GCP, but make it explicit for audit visibility)
gcloud compute firewall-rules create deny-all-ingress-explicit \
--project=<project-id> \
--network=prod-vpc \
--direction=INGRESS \
--priority=65534 \
--action=DENY \
--rules=all \
--source-ranges=0.0.0.0/0
# Allow Google health check ranges (required for load balancers)
gcloud compute firewall-rules create allow-health-checks \
--project=<project-id> \
--network=prod-vpc \
--direction=INGRESS \
--priority=50 \
--action=ALLOW \
--rules=tcp \
--source-ranges=35.191.0.0/16,130.211.0.0/22 \
--target-tags=app-server
Reference: VPC firewall rules → · Firewall policies →
3. Identity-Aware Proxy (IAP) — Secure Admin Access
IAP replaces VPN and bastion hosts for accessing internal GCP resources. It verifies identity and context before allowing TCP tunnel access — no admin ports need to be open to the internet.
# Enable IAP for TCP tunneling (replaces SSH/RDP over the internet)
gcloud services enable iap.googleapis.com \
--project=<project-id>
# Create the firewall rule allowing IAP's IP range only for SSH
gcloud compute firewall-rules create allow-ssh-iap \
--project=<project-id> \
--network=prod-vpc \
--direction=INGRESS \
--priority=100 \
--action=ALLOW \
--rules=tcp:22 \
--source-ranges=35.235.240.0/20 \
--target-tags=app-server
# Grant a user IAP tunnel access to a specific VM
gcloud projects add-iam-policy-binding <project-id> \
--member="user:[email protected]" \
--role="roles/iap.tunnelResourceAccessor"
# Connect to a VM via IAP (no public IP required)
gcloud compute ssh vm-prod \
--tunnel-through-iap \
--project=<project-id> \
--zone=us-central1-a
Reference: Identity-Aware Proxy → · IAP TCP forwarding →
4. Cloud Armor — Web Application Firewall
Cloud Armor protects external HTTPS load balancers from OWASP Top 10 attacks, DDoS, and IP-based threats. Required for CC6.6 external threat protection.
# Create a Cloud Armor security policy in prevention mode
gcloud compute security-policies create prod-waf-policy \
--description="SOC 2 WAF policy — OWASP Top 10 protection" \
--project=<project-id>
# Enable the pre-configured OWASP Top 10 rule set
gcloud compute security-policies rules create 1000 \
--security-policy=prod-waf-policy \
--expression="evaluatePreconfiguredExpr('xss-v33-stable')" \
--action=deny-403 \
--description="Block XSS attacks"
gcloud compute security-policies rules create 1001 \
--security-policy=prod-waf-policy \
--expression="evaluatePreconfiguredExpr('sqli-v33-stable')" \
--action=deny-403 \
--description="Block SQL injection"
gcloud compute security-policies rules create 1002 \
--security-policy=prod-waf-policy \
--expression="evaluatePreconfiguredExpr('rce-v33-stable')" \
--action=deny-403 \
--description="Block RCE attempts"
gcloud compute security-policies rules create 1003 \
--security-policy=prod-waf-policy \
--expression="evaluatePreconfiguredExpr('lfi-v33-stable')" \
--action=deny-403 \
--description="Block LFI attacks"
# Attach the security policy to the backend service
gcloud compute backend-services update prod-backend \
--security-policy=prod-waf-policy \
--global \
--project=<project-id>
# Verify the policy is attached
gcloud compute backend-services describe prod-backend \
--global \
--format="value(securityPolicy)" \
--project=<project-id>
Reference: Cloud Armor documentation → · Pre-configured WAF rules →
5. Private Google Access and VPC Service Controls
Private Google Access allows VMs without external IPs to reach Google APIs (Cloud Storage, BigQuery, Secret Manager) over Google's internal network.
VPC Service Controls creates a security perimeter around GCP services to prevent data exfiltration — even if credentials are compromised.
# Enable Private Google Access on a subnet
gcloud compute networks subnets update snet-app \
--region=us-central1 \
--enable-private-ip-google-access \
--project=<project-id>
# Create a VPC Service Controls perimeter (restricts data movement)
gcloud access-context-manager perimeters create prod-perimeter \
--title="Production Service Perimeter" \
--resources="projects/<project-number>" \
--restricted-services="storage.googleapis.com,bigquery.googleapis.com,secretmanager.googleapis.com" \
--policy=<access-policy-id>
# Verify Private Google Access is enabled
gcloud compute networks subnets describe snet-app \
--region=us-central1 \
--format="value(privateIpGoogleAccess)" \
--project=<project-id>
Reference: Private Google Access → · VPC Service Controls →
6. TLS Enforcement
# Create a strict SSL policy (TLS 1.2+, modern cipher suites)
gcloud compute ssl-policies create prod-ssl-policy \
--profile=MODERN \
--min-tls-version=TLS_1_2 \
--project=<project-id>
# Apply the SSL policy to an HTTPS target proxy
gcloud compute target-https-proxies update prod-https-proxy \
--ssl-policy=prod-ssl-policy \
--project=<project-id>
# Enforce HTTPS-only access to Cloud Storage buckets
# (set uniform bucket-level access and deny allUsers on http)
gcloud storage buckets update gs://prod-bucket \
--uniform-bucket-level-access
# Use Organization Policy to enforce secure transport on GCS
gcloud resource-manager org-policies set-policy \
--organization=<org-id> \
- <<'EOF'
name: organizations/<org-id>/policies/storage.requireTls
spec:
rules:
- enforce: true
EOF
Reference: SSL policies → · Cloud Storage retention and access →
SOC 2 Evidence for Network Security
| Evidence item | How to collect |
|---|---|
| VPC and subnet configuration | gcloud compute networks list, gcloud compute networks subnets list |
| Firewall rules for all VPCs | gcloud compute firewall-rules list --project=<id> |
| VPC Flow Logs configuration | gcloud compute networks subnets list --format="table(name,enableFlowLogs)" |
| Cloud Armor policy and rules | gcloud compute security-policies describe prod-waf-policy |
| IAP configuration and access grants | gcloud compute firewall-rules list --filter="sourceRanges:35.235.240.0" |
| SSL policy | gcloud compute ssl-policies describe prod-ssl-policy |
| VPC Service Controls perimeters | gcloud access-context-manager perimeters list |
| SCC network misconfiguration findings | GCP Console → Security Command Center → Findings → Category: Firewall |