Securing Cloud Applications with Google Cloud Armor: Enterprise WAF and DDoS Protection

Executive Summary: Google Cloud Armor provides enterprise-grade DDoS protection and web application firewall (WAF) capabilities that integrate seamlessly with Cloud Load Balancing. This comprehensive guide explores Cloud Armor’s security capabilities, from preconfigured WAF rules and custom security policies to adaptive protection and bot management. After implementing security architectures for enterprises handling millions of requests daily, I’ve found Cloud Armor delivers exceptional protection against both volumetric attacks and application-layer threats while maintaining low latency. Organizations should leverage Cloud Armor’s managed rulesets, rate limiting, and geographic controls while implementing proper logging and incident response procedures from the start.

Cloud Armor Architecture: Defense in Depth

Cloud Armor operates at Google’s global edge network, inspecting traffic before it reaches your applications. This edge-based architecture provides several advantages: attacks are mitigated close to their source, reducing latency impact on legitimate traffic; Google’s massive network capacity absorbs volumetric DDoS attacks that would overwhelm traditional infrastructure; and security policies apply consistently across all regions without per-region configuration.

Security policies attach to backend services in Cloud Load Balancing, enabling granular control over which backends receive protection. Each policy contains ordered rules that evaluate requests against conditions (IP ranges, geographic locations, request attributes) and take actions (allow, deny, rate limit, redirect). Rules evaluate in priority order, with the first matching rule determining the action. Default rules handle traffic that doesn’t match any explicit rule.

Cloud Armor offers two tiers: Standard and Managed Protection Plus. Standard provides basic DDoS protection and custom security policies. Managed Protection Plus adds adaptive protection (ML-based attack detection), advanced DDoS protection with SLA guarantees, and access to Google’s security operations team for incident response. For production workloads, Managed Protection Plus provides the comprehensive protection enterprises require.

WAF Rules and Threat Protection

Cloud Armor’s preconfigured WAF rules protect against OWASP Top 10 vulnerabilities including SQL injection, cross-site scripting (XSS), remote code execution, and local file inclusion. These rules are maintained by Google’s security team and updated regularly to address new attack patterns. Enable preconfigured rules as a baseline, then tune sensitivity levels based on your application’s false positive rate.

Custom rules use Common Expression Language (CEL) for flexible matching conditions. Match on request headers, query parameters, cookies, request body, and geographic attributes. Combine conditions with logical operators for complex policies. For example, block requests from specific countries that also contain suspicious patterns, or allow requests from known good IPs regardless of other rule matches.

Rate limiting controls request volume from individual clients or across client groups. Configure rate limits based on IP address, HTTP headers, or custom keys derived from request attributes. Rate limiting is essential for protecting against credential stuffing, API abuse, and application-layer DDoS attacks that bypass volumetric protections. Set appropriate thresholds based on legitimate traffic patterns to avoid blocking real users.

Production Terraform Configuration

Here’s a comprehensive Terraform configuration for Cloud Armor with WAF rules, rate limiting, and geographic controls:

# Cloud Armor Enterprise Configuration
terraform {
  required_version = ">= 1.5.0"
  required_providers {
    google = { source = "hashicorp/google", version = "~> 5.0" }
  }
}

variable "project_id" { type = string }

# Cloud Armor Security Policy
resource "google_compute_security_policy" "web_policy" {
  name        = "web-security-policy"
  description = "Enterprise web application security policy"
  
  # Enable Adaptive Protection (requires Managed Protection Plus)
  adaptive_protection_config {
    layer_7_ddos_defense_config {
      enable = true
      rule_visibility = "STANDARD"
    }
  }

  # Rule 1: Allow known good IPs (highest priority)
  rule {
    action   = "allow"
    priority = 100
    
    match {
      versioned_expr = "SRC_IPS_V1"
      config {
        src_ip_ranges = [
          "203.0.113.0/24",  # Corporate office
          "198.51.100.0/24"  # Partner network
        ]
      }
    }
    
    description = "Allow trusted IP ranges"
  }

  # Rule 2: Block known bad IPs
  rule {
    action   = "deny(403)"
    priority = 200
    
    match {
      versioned_expr = "SRC_IPS_V1"
      config {
        src_ip_ranges = [
          "192.0.2.0/24"  # Known malicious range
        ]
      }
    }
    
    description = "Block known malicious IPs"
  }

  # Rule 3: Geographic blocking
  rule {
    action   = "deny(403)"
    priority = 300
    
    match {
      expr {
        expression = "origin.region_code == 'XX'"  # Replace with actual country codes
      }
    }
    
    description = "Block traffic from restricted regions"
  }

  # Rule 4: SQL Injection protection
  rule {
    action   = "deny(403)"
    priority = 1000
    
    match {
      expr {
        expression = "evaluatePreconfiguredExpr('sqli-v33-stable')"
      }
    }
    
    description = "Block SQL injection attacks"
  }

  # Rule 5: XSS protection
  rule {
    action   = "deny(403)"
    priority = 1100
    
    match {
      expr {
        expression = "evaluatePreconfiguredExpr('xss-v33-stable')"
      }
    }
    
    description = "Block cross-site scripting attacks"
  }

  # Rule 6: Remote Code Execution protection
  rule {
    action   = "deny(403)"
    priority = 1200
    
    match {
      expr {
        expression = "evaluatePreconfiguredExpr('rce-v33-stable')"
      }
    }
    
    description = "Block remote code execution attacks"
  }

  # Rule 7: Local File Inclusion protection
  rule {
    action   = "deny(403)"
    priority = 1300
    
    match {
      expr {
        expression = "evaluatePreconfiguredExpr('lfi-v33-stable')"
      }
    }
    
    description = "Block local file inclusion attacks"
  }

  # Rule 8: Rate limiting - general
  rule {
    action   = "rate_based_ban"
    priority = 2000
    
    match {
      versioned_expr = "SRC_IPS_V1"
      config {
        src_ip_ranges = ["*"]
      }
    }
    
    rate_limit_options {
      conform_action = "allow"
      exceed_action  = "deny(429)"
      
      rate_limit_threshold {
        count        = 1000
        interval_sec = 60
      }
      
      ban_duration_sec = 300
      ban_threshold {
        count        = 5000
        interval_sec = 300
      }
    }
    
    description = "Rate limit: 1000 req/min, ban after 5000 req/5min"
  }

  # Rule 9: Rate limiting - login endpoint
  rule {
    action   = "rate_based_ban"
    priority = 2100
    
    match {
      expr {
        expression = "request.path.matches('/api/auth/.*')"
      }
    }
    
    rate_limit_options {
      conform_action = "allow"
      exceed_action  = "deny(429)"
      
      rate_limit_threshold {
        count        = 10
        interval_sec = 60
      }
      
      ban_duration_sec = 600
    }
    
    description = "Strict rate limit on authentication endpoints"
  }

  # Rule 10: Bot detection (reCAPTCHA Enterprise)
  rule {
    action   = "deny(403)"
    priority = 3000
    
    match {
      expr {
        expression = "request.headers['user-agent'].contains('bot') && !request.headers['user-agent'].contains('Googlebot')"
      }
    }
    
    description = "Block obvious bot traffic (except Googlebot)"
  }

  # Default rule: Allow all other traffic
  rule {
    action   = "allow"
    priority = 2147483647
    
    match {
      versioned_expr = "SRC_IPS_V1"
      config {
        src_ip_ranges = ["*"]
      }
    }
    
    description = "Default allow rule"
  }
}

# Backend service with Cloud Armor attached
resource "google_compute_backend_service" "web_backend" {
  name                  = "web-backend"
  protocol              = "HTTP"
  port_name             = "http"
  timeout_sec           = 30
  security_policy       = google_compute_security_policy.web_policy.id
  
  health_checks = [google_compute_health_check.web_health.id]
  
  backend {
    group           = google_compute_instance_group_manager.web_mig.instance_group
    balancing_mode  = "UTILIZATION"
    capacity_scaler = 1.0
  }
  
  log_config {
    enable      = true
    sample_rate = 1.0
  }
}

resource "google_compute_health_check" "web_health" {
  name               = "web-health-check"
  check_interval_sec = 5
  timeout_sec        = 5
  
  http_health_check {
    port         = 80
    request_path = "/health"
  }
}

# Placeholder for instance group manager
resource "google_compute_instance_group_manager" "web_mig" {
  name               = "web-mig"
  base_instance_name = "web"
  zone               = "us-central1-a"
  target_size        = 2
  
  version {
    instance_template = google_compute_instance_template.web_template.id
  }
  
  named_port {
    name = "http"
    port = 80
  }
}

resource "google_compute_instance_template" "web_template" {
  name_prefix  = "web-template-"
  machine_type = "e2-medium"
  
  disk {
    source_image = "debian-cloud/debian-11"
    auto_delete  = true
    boot         = true
  }
  
  network_interface {
    network = "default"
  }
  
  lifecycle {
    create_before_destroy = true
  }
}

Python SDK for Security Policy Management

This Python implementation demonstrates enterprise patterns for Cloud Armor including policy management, rule updates, and security monitoring:

"""Cloud Armor Manager - Enterprise Python Implementation"""
from dataclasses import dataclass
from typing import List, Dict, Optional
from google.cloud import compute_v1
from google.cloud import logging_v2
import logging
from datetime import datetime, timedelta

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@dataclass
class SecurityRule:
    priority: int
    action: str
    description: str
    expression: str = None
    src_ip_ranges: List[str] = None
    rate_limit: Dict = None

@dataclass
class ThreatEvent:
    timestamp: datetime
    source_ip: str
    rule_matched: str
    action: str
    request_path: str
    country: str

class CloudArmorManager:
    """Enterprise Cloud Armor security policy management."""
    
    def __init__(self, project_id: str):
        self.project_id = project_id
        self.policies_client = compute_v1.SecurityPoliciesClient()
        self.logging_client = logging_v2.Client(project=project_id)
    
    def list_policies(self) -> List[str]:
        """List all security policies in project."""
        request = compute_v1.ListSecurityPoliciesRequest(
            project=self.project_id
        )
        policies = self.policies_client.list(request=request)
        return [p.name for p in policies]
    
    def get_policy_rules(self, policy_name: str) -> List[Dict]:
        """Get all rules in a security policy."""
        request = compute_v1.GetSecurityPolicyRequest(
            project=self.project_id,
            security_policy=policy_name
        )
        policy = self.policies_client.get(request=request)
        
        rules = []
        for rule in policy.rules:
            rules.append({
                'priority': rule.priority,
                'action': rule.action,
                'description': rule.description,
                'match': str(rule.match)
            })
        return sorted(rules, key=lambda x: x['priority'])
    
    def add_ip_block_rule(self, policy_name: str, ip_ranges: List[str],
                         priority: int, description: str) -> bool:
        """Add IP blocking rule to policy."""
        rule = compute_v1.SecurityPolicyRule(
            priority=priority,
            action="deny(403)",
            description=description,
            match=compute_v1.SecurityPolicyRuleMatcher(
                versioned_expr="SRC_IPS_V1",
                config=compute_v1.SecurityPolicyRuleMatcherConfig(
                    src_ip_ranges=ip_ranges
                )
            )
        )
        
        try:
            request = compute_v1.AddRuleSecurityPolicyRequest(
                project=self.project_id,
                security_policy=policy_name,
                security_policy_rule_resource=rule
            )
            operation = self.policies_client.add_rule(request=request)
            operation.result()
            logger.info(f"Added IP block rule at priority {priority}")
            return True
        except Exception as e:
            logger.error(f"Failed to add rule: {e}")
            return False
    
    def remove_rule(self, policy_name: str, priority: int) -> bool:
        """Remove rule from policy by priority."""
        try:
            request = compute_v1.RemoveRuleSecurityPolicyRequest(
                project=self.project_id,
                security_policy=policy_name,
                priority=priority
            )
            operation = self.policies_client.remove_rule(request=request)
            operation.result()
            logger.info(f"Removed rule at priority {priority}")
            return True
        except Exception as e:
            logger.error(f"Failed to remove rule: {e}")
            return False
    
    def get_blocked_requests(self, hours: int = 24) -> List[ThreatEvent]:
        """Get blocked requests from Cloud Logging."""
        filter_str = f'''
            resource.type="http_load_balancer"
            jsonPayload.enforcedSecurityPolicy.outcome="DENY"
            timestamp >= "{(datetime.utcnow() - timedelta(hours=hours)).isoformat()}Z"
        '''
        
        events = []
        for entry in self.logging_client.list_entries(filter_=filter_str):
            payload = entry.payload
            events.append(ThreatEvent(
                timestamp=entry.timestamp,
                source_ip=payload.get('jsonPayload', {}).get('remoteIp', 'unknown'),
                rule_matched=payload.get('jsonPayload', {}).get('enforcedSecurityPolicy', {}).get('name', 'unknown'),
                action='DENY',
                request_path=payload.get('httpRequest', {}).get('requestUrl', 'unknown'),
                country=payload.get('jsonPayload', {}).get('remoteIpCountry', 'unknown')
            ))
        
        return events
    
    def get_attack_summary(self, hours: int = 24) -> Dict:
        """Get summary of attacks in time period."""
        events = self.get_blocked_requests(hours)
        
        summary = {
            'total_blocked': len(events),
            'by_country': {},
            'by_rule': {},
            'top_attackers': {}
        }
        
        for event in events:
            # Count by country
            country = event.country
            summary['by_country'][country] = summary['by_country'].get(country, 0) + 1
            
            # Count by rule
            rule = event.rule_matched
            summary['by_rule'][rule] = summary['by_rule'].get(rule, 0) + 1
            
            # Count by IP
            ip = event.source_ip
            summary['top_attackers'][ip] = summary['top_attackers'].get(ip, 0) + 1
        
        # Sort top attackers
        summary['top_attackers'] = dict(
            sorted(summary['top_attackers'].items(), 
                   key=lambda x: x[1], reverse=True)[:10]
        )
        
        return summary
    
    def auto_block_attackers(self, policy_name: str, 
                            threshold: int = 100,
                            hours: int = 1) -> List[str]:
        """Automatically block IPs exceeding threshold."""
        events = self.get_blocked_requests(hours)
        
        # Count requests per IP
        ip_counts = {}
        for event in events:
            ip = event.source_ip
            ip_counts[ip] = ip_counts.get(ip, 0) + 1
        
        # Block IPs exceeding threshold
        blocked = []
        for ip, count in ip_counts.items():
            if count >= threshold:
                # Find available priority
                rules = self.get_policy_rules(policy_name)
                used_priorities = {r['priority'] for r in rules}
                priority = 500
                while priority in used_priorities:
                    priority += 1
                
                if self.add_ip_block_rule(
                    policy_name, [f"{ip}/32"], priority,
                    f"Auto-blocked: {count} blocked requests in {hours}h"
                ):
                    blocked.append(ip)
                    logger.info(f"Auto-blocked {ip} ({count} requests)")
        
        return blocked

Cost Optimization and Monitoring

Cloud Armor pricing includes policy evaluation charges per million requests and data processing charges per GB. Standard tier provides basic protection at lower cost, while Managed Protection Plus adds a monthly subscription fee plus per-request charges for advanced features. For high-traffic applications, the per-request costs can be significant—optimize by using efficient rule ordering (most selective rules first) and avoiding unnecessary rule evaluations.

Logging configuration significantly impacts both visibility and costs. Enable logging on backend services to capture security events, but consider sampling for high-traffic applications. Export logs to BigQuery for long-term analysis and compliance requirements. Set up log-based metrics and alerts for security events like sudden increases in blocked requests or new attack patterns.

Adaptive Protection provides ML-based attack detection but requires Managed Protection Plus subscription. The feature analyzes traffic patterns and automatically suggests rules during attacks. For organizations without dedicated security teams, Adaptive Protection provides valuable automated threat response. Review suggested rules before applying to avoid blocking legitimate traffic.

Cloud Armor Architecture - showing edge protection, WAF rules, and backend integration
Cloud Armor Enterprise Architecture – Illustrating edge-based DDoS protection, WAF rule evaluation, rate limiting, and integration with Cloud Load Balancing for comprehensive application security.

Key Takeaways and Best Practices

Cloud Armor provides essential protection for internet-facing applications with its combination of DDoS mitigation and WAF capabilities. Enable preconfigured WAF rules as a baseline, then tune based on your application’s specific requirements. Implement rate limiting on sensitive endpoints like authentication and API routes to prevent abuse.

Use geographic controls judiciously—blocking entire countries can impact legitimate users. Implement proper logging and monitoring to detect attacks early and measure the effectiveness of your security policies. The Terraform and Python examples provided here establish patterns for production-ready Cloud Armor deployments that protect applications from both volumetric and application-layer attacks.


Discover more from Code, Cloud & Context

Subscribe to get the latest posts sent to your email.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.