Skip to content

AWS WebACL API Proposal #1816

@vandjelk

Description

@vandjelk

AWS WebACL API Proposal

Overview

This proposal defines the API for AWS WebACL (Web Application Firewall) functionality in Cloud Manager. The API will be exposed through the cloud-resources.kyma-project.io/v1beta1 API group as a Kubernetes CRD, enabling customers to protect their applications from common web exploits.

Proposed API Structure

apiVersion: cloud-resources.kyma-project.io/v1beta1
kind: AwsWebAcl
metadata:
  name: example-webacl
  namespace: default
spec:
  defaultAction:      # Required: allow or block
    allow: {}
  
  visibilityConfig:   # Required: CloudWatch metrics
    cloudWatchMetricsEnabled: true
    metricName: example-webacl-metrics
    sampledRequestsEnabled: true
  
  description: "..."  # Optional
  rules: []           # Optional: max 100 rules

Proposed Statement Types

The following statement types will be supported for inline rule definitions:

  • geoMatch - Country-based filtering with ForwardedIPConfig support
  • rateBased - Rate limiting per IP address
  • byteMatch - Pattern matching in request fields
  • managedRuleGroup - AWS-managed rule sets (OWASP, Bot Control, etc.)
  • labelMatch - Match based on labels from previous rules
  • sizeConstraint - Size-based filtering (DDoS protection)
  • sqliMatch - SQL injection detection
  • xssMatch - Cross-site scripting detection
  • regexMatch - Regular expression pattern matching
  • asnMatch - Autonomous System Number matching
  • ipSetReference - IP address filtering via separate AwsIPSet resource (see IPSet section below)

Design Decisions

MetricName: Decision Required

Context:

  • Each AWS WebACL publishes metrics to CloudWatch under the AWS/WAFV2 namespace
  • Multiple WebACLs can share the same MetricName - they are distinguished by the WebACL dimension
  • Multiple Kyma instances (Shoots) can share the same AWS account in production environments
  • Multiple WebACLs per Shoot are common (one per namespace/application)
  • AWS tags do NOT appear as CloudWatch dimensions - only MetricName, WebACL, Region, Rule, etc. are available

Requirement: Need to identify and collect metrics for all WebACLs belonging to a single Shoot when multiple Shoots share the same AWS account.

Question: Should visibilityConfig.metricName be required or optional? If optional, what default value should be used?

Option 1: Customer-Defined Only (Required)

visibilityConfig:
  metricName: my-custom-name  # REQUIRED - customer must provide
  • Pro: Gives customers full control over metric organization
  • Pro: No ambiguity about metric names
  • Con: Burdens customers with understanding CloudWatch internals
  • Con: Complicates bulk metric collection (must query each WebACL individually)
  • Con: Cannot identify which WebACLs belong to which Shoot

Option 2: Customer-Defined with Default (Optional)

visibilityConfig:
  metricName: my-custom-name  # Optional - defaults if omitted

Customer can override, otherwise defaults to one of:

  • WebACL resource name - Each WebACL gets unique metric name automatically
    • Pro: Simple, unique per resource
    • Con: Complicates bulk collection, breaks on renames
    • Con: Cannot group by Shoot
  • Shoot name - Groups all WebACLs in a Shoot
    • Pro: Natural organizational boundary, efficient collection per Shoot
    • Pro: Solves multi-Shoot, multi-WebACL per Shoot identification
    • Con: Customer override breaks Shoot identification - if customer provides custom name, we lose Shoot association
  • Hard-coded default ("kyma-webacl") - All Kyma WebACLs share one MetricName
    • Pro: Works with shared AWS accounts, single query gets all Kyma WebACLs
    • Pro: Simple implementation, no need to discover names
    • Con: Less granular than per-Shoot (but WebACL dimension still distinguishes individual WebACLs)
    • Con: Cannot identify which WebACLs belong to which Shoot

Option 3: Hard-Coded Value Only (No Override) - Recommended for Multi-Shoot Scenarios

# metricName field not exposed - automatically set by reconciler
visibilityConfig:
  cloudWatchMetricsEnabled: true
  sampledRequestsEnabled: true

Two variants:

3a) Hard-coded to Shoot Name (Recommended for multiple Shoots per AWS account)

  • Reconciler automatically sets metricName = shoot-name
  • Pro: Solves Shoot identification - query by MetricName returns all WebACLs for that Shoot
  • Pro: Efficient bulk collection per Shoot
  • Pro: Works correctly when multiple Shoots share AWS account (each Shoot gets unique MetricName)
  • Con: No customer control - but necessary to maintain Shoot-to-metric association

3b) Hard-coded to static value (Only for single Shoot per AWS account)

  • Reconciler automatically sets metricName = "kyma-webacl"
  • Pro: Simplest implementation, all WebACLs grouped
  • Con: Does NOT solve multi-Shoot identification - all Shoots share same MetricName

Recommendation:

  • For multi-Shoot environments (multiple Shoots per AWS account): Use Option 3a - Hard-code to Shoot name with no override
  • For single-Shoot environments (one Shoot per AWS account): Either Option 3a or 3b works

Reason: Since AWS tags don't appear in CloudWatch dimensions, MetricName is the ONLY dimension available to group WebACLs by Shoot. Any customer override would break this association, making it impossible to identify which WebACLs belong to which Shoot.

And/Or/Not Statements: Not Implemented

Proposal: Logical operators (And, Or, Not) for combining statements will not be supported in the initial implementation.

Reason:

  • Kubernetes API limitation: Circular nested structures cannot be expressed in OpenAPI v3 schema without workarounds
  • CRD generation complexity: controller-gen cannot generate proper validation for recursive types
  • Limited use cases: Most WAF rules can be expressed using single statements or managed rule groups

Workaround: Customers requiring complex logic can:

  1. Use managed rule groups with AWS-defined logic
  2. Create multiple separate rules with label-based matching (rule chains)
  3. Deploy external AWS WAF rule groups and reference them (future enhancement)

Future consideration: May be implemented using union types or external rule group references if demand justifies the complexity.

IPSet: Separate Resource

Proposal: IP-based filtering will be provided through a separate AwsIPSet resource that can be referenced from WebACL rules using Kubernetes object references.

Reason:

  • Resource lifecycle: IPSets are reusable resources referenced by multiple WebACLs
  • Management complexity: Inline IP lists would couple IPSet lifecycle to WebACL lifecycle
  • AWS best practice: IPSets are managed as independent resources in AWS WAF
  • Kubernetes pattern: Cross-resource references use standard object reference format

API Design:

---
# IPSet resource (separate CRD, cluster-scoped)
apiVersion: cloud-resources.kyma-project.io/v1beta1
kind: AwsIPSet
metadata:
  name: corporate-ips
spec:
  description: Corporate office IP addresses
  ipAddressVersion: IPV4  # or IPV6
  addresses:
    - 203.0.113.0/24
    - 198.51.100.10/32
    - 192.0.2.44/32

---
# WebACL rule referencing the IPSet
apiVersion: cloud-resources.kyma-project.io/v1beta1
kind: AwsWebAcl
metadata:
  name: my-webacl
spec:
  defaultAction:
    allow: {}
  
  rules:
    - name: allow-corporate-ips
      priority: 0
      action:
        allow: {}
      statement:
        ipSetReference:
          # Kubernetes object reference (cluster-scoped)
          name: corporate-ips
      visibilityConfig:
        cloudWatchMetricsEnabled: true
        sampledRequestsEnabled: true

Reference Format: Uses standard Kubernetes object reference pattern with only name field (no namespace since resources are cluster-scoped), similar to how klog.ObjectRef structures references.

Implementation Notes:

  • Cloud Manager reconciler will:
    1. Watch for AwsIPSet resources and create/update AWS IPSet resources
    2. Resolve ipSetReference to AWS IPSet ARN during WebACL reconciliation
    3. Validate that referenced IPSet exists before creating/updating WebACL
    4. Handle IPSet updates (WebACL automatically uses latest IPSet version)
    5. Prevent IPSet deletion if referenced by any WebACL (use finalizers)

RegexPatternSet: Omitted

Proposal: Regex pattern set references will not be supported. The regexMatch statement will be used for inline patterns instead.

Reason:

  • Inline alternative exists: regexMatch statement covers basic regex use cases (max 512 chars)
  • Limited value-add: Pattern sets primarily benefit when sharing patterns across rules, which can be achieved through rule duplication or managed rule groups
  • External resource complexity: Would require separate resource management similar to IPSet

Coverage: regexMatch statement supports patterns up to 512 characters, sufficient for common use cases like:

  • Path pattern matching: ^/(admin|root|config)/.*$
  • Email validation: ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
  • API versioning: ^/api/v[0-9]+/.*$

AssociationConfig: Omitted

Proposal: The associationConfig field for custom request body inspection limits will not be supported.

Reason:

  • ALB-only usage: Cloud Manager WebACLs are exclusively used with Application Load Balancer (ALB)
  • Fixed configuration: ALB has a hardcoded 8KB request body inspection limit that cannot be customized
  • No effect: AssociationConfig only applies to CloudFront, API Gateway, Cognito User Pool, App Runner, and Verified Access Instance
  • Misleading API: Including the field would suggest functionality that doesn't work with ALB

Future consideration: May be implemented if demand justifies support for CloudFront or API Gateway integration, which would require additional resource association logic beyond current ALB-only scope.

Example Usage

Below is the complete sample demonstrating all available features:

# Complete AwsWebAcl Example with All Features
# ============================================
# This example demonstrates every available field in the AwsWebAcl CRD including:
# - All statement types (GeoMatch, RateBased, ByteMatch, ManagedRuleGroup, etc.)
# - All action types (Allow, Block, Count, Captcha, Challenge)
# - Custom request handling and custom responses
# - Rule labels for advanced filtering
# - Per-rule immunity time overrides
# - Association config for request body inspection limits
# - Token domains for CAPTCHA/Challenge
# - Custom response bodies
#
# NOTE: Logical operators (And/Or/Not) are NOT supported due to Kubernetes API limitations
# NOTE: Use ipSetReference statement to reference separate AwsIPSet resources for IP filtering

apiVersion: cloud-resources.kyma-project.io/v1beta1
kind: AwsWebAcl
metadata:
  name: comprehensive-webacl-example
  namespace: default
  labels:
    app: my-application
    environment: production
    team: security

spec:
  # ====================
  # DEFAULT ACTION (Required)
  # ====================
  # What to do when no rules match - exactly one of allow or block must be set
  defaultAction:
    allow: {}
    # Alternatively use block with custom response:
    # block:
    #   customResponse:
    #     responseCode: 403
    #     customResponseBodyKey: "block-page"
    #     responseHeaders:
    #       - name: X-Custom-Header
    #         value: blocked-by-waf

  # ====================
  # DESCRIPTION (Optional)
  # ====================
  description: "Production WebACL with comprehensive protection: bot control, geo-blocking, IP filtering, rate limiting, and custom pattern matching"

  # ====================
  # VISIBILITY CONFIG (Required)
  # ====================
  visibilityConfig:
    cloudWatchMetricsEnabled: true
    metricName: ComprehensiveWebAclMetrics
    sampledRequestsEnabled: true

  # ====================
  # TOKEN DOMAINS (Optional, max 10)
  # ====================
  # Domains where CAPTCHA/Challenge tokens are valid (cross-domain support)
  tokenDomains:
    - example.com
    - api.example.com
    - www.example.com

  # ====================
  # CUSTOM RESPONSE BODIES (Optional)
  # ====================
  # Custom HTML/JSON content for block actions
  customResponseBodies:
    block-page:
      contentType: TEXT_HTML
      content: |
        <html>
          <head><title>Access Denied</title></head>
          <body><h1>Access Denied</h1><p>Your request was blocked by our Web Application Firewall.</p></body>
        </html>
    json-errOrStatement:
      contentType: APPLICATION_JSON
      content: '{"error": "forbidden", "message": "Request blocked by WAF"}'

  # ====================
  # GLOBAL CAPTCHA CONFIG (Optional)
  # ====================
  # Default immunity time after passing CAPTCHA (60-259200 seconds)
  captchaConfig:
    immunityTime: 3600  # 1 hour

  # ====================
  # GLOBAL CHALLENGE CONFIG (Optional)
  # ====================
  # Default immunity time after passing Challenge (60-259200 seconds)
  challengeConfig:
    immunityTime: 7200  # 2 hours

  # ====================
  # RULES (Optional, max 100)
  # ====================
  rules:
    # ============================================================
    # RULE 1: BYTE MATCH with Rule Labels
    # ============================================================
    - name: allow-api-path
      priority: 0
      action:
        allow:
          customRequestHandling:
            insertHeaders:
              - name: X-API-Access
                value: "true"
      statement:
        byteMatch:
          searchString: "/api"
          positionalConstraint: STARTS_WITH
          fieldToMatch:
            uriPath: true
          textTransformations:
            - priority: 0
              type: LOWERCASE
      # Apply labels to matching requests for use in subsequent rules
      ruleLabels:
        - name: "trusted:corporate"
        - name: "source:internal"
      visibilityConfig:
        cloudWatchMetricsEnabled: true
        metricName: allow-corporate-network
        sampledRequestsEnabled: true

    # ============================================================
    # RULE 2: GEO MATCH - Block with custom response
    # ============================================================
    - name: block-high-risk-countries
      priority: 1
      action:
        block:
          customResponse:
            responseCode: 403
            customResponseBodyKey: "block-page"
            responseHeaders:
              - name: X-Block-Reason
                value: geo-restricted
      statement:
        geoMatch:
          countryCodes:
            - "CN"
            - "RU"
            - "KP"
      ruleLabels:
        - name: "block:geo-restricted"
        - name: "severity:high"
      visibilityConfig:
        cloudWatchMetricsEnabled: true
        metricName: block-high-risk-countries
        sampledRequestsEnabled: true

    # ============================================================
    # RULE 3: RATE BASED - Rate limiting
    # ============================================================
    - name: rate-limit-per-ip
      priority: 2
      action:
        block:
          customResponse:
            responseCode: 429
            customResponseBodyKey: "json-error"
      statement:
        rateBased:
          limit: 2000
      ruleLabels:
        - name: "protection:rate-limit"
      visibilityConfig:
        cloudWatchMetricsEnabled: true
        metricName: rate-limit-per-ip
        sampledRequestsEnabled: true

    # ============================================================
    # RULE 4: BYTE MATCH - Pattern in query string
    # ============================================================
    - name: block-path-traversal
      priority: 3
      action:
        block: {}
      statement:
        byteMatch:
          searchString: "../"
          positionalConstraint: CONTAINS
          fieldToMatch:
            queryString: true
          textTransformations:
            - priority: 0
              type: URL_DECODE
            - priority: 1
              type: LOWERCASE
      visibilityConfig:
        cloudWatchMetricsEnabled: true
        metricName: block-path-traversal
        sampledRequestsEnabled: true

    # ============================================================
    # RULE 5: BYTE MATCH - Header inspection
    # ============================================================
    - name: block-malicious-user-agents
      priority: 4
      action:
        block: {}
      statement:
        byteMatch:
          searchString: "sqlmap"
          positionalConstraint: CONTAINS
          fieldToMatch:
            singleHeader: "user-agent"
          textTransformations:
            - priority: 0
              type: LOWERCASE
      visibilityConfig:
        cloudWatchMetricsEnabled: true
        metricName: block-malicious-user-agents
        sampledRequestsEnabled: true

    # ============================================================
    # RULE 6: CAPTCHA with per-rule immunity override
    # ============================================================
    - name: captcha-challenge-suspicious-ips
      priority: 5
      action:
        captcha:
          customRequestHandling:
            insertHeaders:
              - name: X-Captcha-Passed
                value: "true"
      statement:
        geoMatch:
          countryCodes:
            - "US"
      # Override global CAPTCHA immunity time for this specific rule
      captchaConfig:
        immunityTime: 300  # 5 minutes (shorter for suspicious IPs)
      ruleLabels:
        - name: "challenge:captcha"
        - name: "risk:medium"
      visibilityConfig:
        cloudWatchMetricsEnabled: true
        metricName: captcha-challenge-suspicious-ips
        sampledRequestsEnabled: true

    # ============================================================
    # RULE 7: CHALLENGE action (silent bot verification)
    # ============================================================
    - name: challenge-bots
      priority: 6
      action:
        challenge:
          customRequestHandling:
            insertHeaders:
              - name: X-Challenge-Passed
                value: "true"
      statement:
        rateBased:
          limit: 500
      # Override global Challenge immunity time for this rule
      challengeConfig:
        immunityTime: 600  # 10 minutes
      ruleLabels:
        - name: "challenge:silent-verification"
        - name: "protection:bot-control"
      visibilityConfig:
        cloudWatchMetricsEnabled: true
        metricName: challenge-bots
        sampledRequestsEnabled: true

    # ============================================================
    # RULE 8: COUNT action (monitoring mode)
    # ============================================================
    - name: monitor-admin-access
      priority: 7
      action:
        count:
          customRequestHandling:
            insertHeaders:
              - name: X-Admin-Access-Logged
                value: "true"
      statement:
        byteMatch:
          searchString: "/admin"
          positionalConstraint: STARTS_WITH
          fieldToMatch:
            uriPath: true
          textTransformations:
            - priority: 0
              type: LOWERCASE
      ruleLabels:
        - name: "monitOrStatement:admin-path"
        - name: "action:count"
      visibilityConfig:
        cloudWatchMetricsEnabled: true
        metricName: monitor-admin-access
        sampledRequestsEnabled: true

    # ============================================================
    # RULE 9: MANAGED RULE GROUP with OverrideAction
    # ============================================================
    - name: AWS-AWSManagedRulesBotControlRuleSet
      priority: 10
      # For managed rule groups, use OverrideAction instead of Action
      overrideAction:
        count: {}  # Override all rules in the group to Count (monitoring mode)
        # Alternatively use none to keep the group's default actions:
        # none: {}
      statement:
        managedRuleGroup:
          vendorName: AWS
          name: AWSManagedRulesBotControlRuleSet
          version: ""  # Empty means latest version
          excludedRules: []
      visibilityConfig:
        cloudWatchMetricsEnabled: true
        metricName: AWS-AWSManagedRulesBotControlRuleSet
        sampledRequestsEnabled: true

    # ============================================================
    # RULE 10: MANAGED RULE GROUP - OWASP Top 10
    # ============================================================
    - name: AWS-AWSManagedRulesCommonRuleSet
      priority: 11
      overrideAction:
        none: {}  # Use the rule group's default actions
      statement:
        managedRuleGroup:
          vendorName: AWS
          name: AWSManagedRulesCommonRuleSet
          version: ""
          excludedRules: []
      visibilityConfig:
        cloudWatchMetricsEnabled: true
        metricName: AWS-AWSManagedRulesCommonRuleSet
        sampledRequestsEnabled: true

    # ============================================================
    # RULE 11: MANAGED RULE GROUP - SQL Injection with exclusions
    # ============================================================
    - name: AWS-AWSManagedRulesSQLiRuleSet
      priority: 12
      overrideAction:
        none: {}
      statement:
        managedRuleGroup:
          vendorName: AWS
          name: AWSManagedRulesSQLiRuleSet
          version: ""
          # Exclude specific rules that cause false positives
          excludedRules:
            - name: SQLi_QUERYARGUMENTS
      visibilityConfig:
        cloudWatchMetricsEnabled: true
        metricName: AWS-AWSManagedRulesSQLiRuleSet
        sampledRequestsEnabled: true

    # RULE 16: LABEL MATCH - Block based on labels from previous rules
    # ============================================================
    # This rule demonstrates label-based filtering, matching requests
    # that have been labeled by previous rules. Useful for creating
    # rule chains where earlier rules classify traffic and later rules
    # take action based on those classifications.
    - name: block-labeled-threats
      priority: 17
      action:
        block:
          customResponse:
            responseCode: 403
            customResponseBodyKey: json-error
      # Block if request has been labeled as "block:high-risk-combined" by Rule 12
      statement:
        labelMatch:
          key: "block:high-risk-combined"
          scope: "LABEL"  # LABEL = exact match, NAMESPACE = prefix match
      visibilityConfig:
        cloudWatchMetricsEnabled: true
        metricName: block-labeled-threats
        sampledRequestsEnabled: true

    # ============================================================
    # RULE 17: SIZE CONSTRAINT - Block oversized requests (DDoS protection)
    # ============================================================
    # This rule blocks requests with query strings larger than 8KB,
    # commonly used in DDoS attacks or attempts to exploit parsing vulnerabilities.
    - name: block-oversized-query
      priority: 18
      action:
        block:
          customResponse:
            responseCode: 413  # Request Entity Too Large
            customResponseBodyKey: json-error
      # Block if query string exceeds 8192 bytes after URL decoding
      statement:
        sizeConstraint:
          comparisonOperator: "GT"  # Greater than
          size: 8192  # 8 KB
          fieldToMatch:
            queryString: true
          textTransformations:
            - priority: 0
              type: "URL_DECODE"
      ruleLabels:
        - name: "protection:size-limit"
        - name: "block:oversized-request"
      visibilityConfig:
        cloudWatchMetricsEnabled: true
        metricName: block-oversized-query
        sampledRequestsEnabled: true

    # ============================================================
    # RULE 18: SQL INJECTION DETECTION - Block SQL injection attacks
    # ============================================================
    # This rule detects SQL injection attempts in query parameters and body.
    # HIGH sensitivity level catches more attacks but may have more false positives.
    - name: block-sql-injection
      priority: 19
      action:
        block:
          customResponse:
            responseCode: 403
            customResponseBodyKey: json-error
      # Detect SQL injection patterns in query strings
      statement:
        sqliMatch:
          fieldToMatch:
            queryString: true
          textTransformations:
            - priority: 0
              type: "URL_DECODE"
            - priority: 1
              type: "HTML_ENTITY_DECODE"
          sensitivityLevel: "HIGH"  # More detections, may have false positives
      ruleLabels:
        - name: "security:sqli-protection"
        - name: "block:sql-injection"
      visibilityConfig:
        cloudWatchMetricsEnabled: true
        metricName: block-sql-injection
        sampledRequestsEnabled: true

    # ============================================================
    # RULE 19: CROSS-SITE SCRIPTING DETECTION - Block XSS attacks
    # ============================================================
    # This rule detects cross-site scripting (XSS) attempts in request fields.
    # XSS attacks inject malicious scripts that execute in users' browsers.
    - name: block-xss-attacks
      priority: 20
      action:
        block:
          customResponse:
            responseCode: 403
            customResponseBodyKey: json-error
      # Detect XSS patterns in query strings and body
      statement:
        xssMatch:
          fieldToMatch:
            queryString: true
          textTransformations:
            - priority: 0
              type: "URL_DECODE"
            - priority: 1
              type: "HTML_ENTITY_DECODE"
            - priority: 2
              type: "JS_DECODE"
      ruleLabels:
        - name: "security:xss-protection"
        - name: "block:xss-attack"
      visibilityConfig:
        cloudWatchMetricsEnabled: true
        metricName: block-xss-attacks
        sampledRequestsEnabled: true

    # ============================================================
    # RULE 20: REGEX PATTERN MATCHING - Match complex patterns
    # ============================================================
    # This rule uses regular expressions to match sophisticated attack patterns.
    # Regex matching is more flexible than ByteMatch for complex validation.
    - name: block-suspicious-patterns
      priority: 21
      action:
        block:
          customResponse:
            responseCode: 403
            customResponseBodyKey: json-error
      # Block requests with suspicious patterns in URI path (e.g., admin/root/config paths)
      statement:
        regexMatch:
          regexString: "^/(admin|root|config|backup|wp-admin)/.*$"
          fieldToMatch:
            uriPath: true
          textTransformations:
            - priority: 0
              type: "LOWERCASE"
            - priority: 1
              type: "URL_DECODE"
      ruleLabels:
        - name: "security:regex-protection"
        - name: "block:suspicious-path"
      visibilityConfig:
        cloudWatchMetricsEnabled: true
        metricName: block-suspicious-patterns
        sampledRequestsEnabled: true

    # ============================================================
    # RULE 21: ASN MATCHING - Match by Autonomous System Number
    # ============================================================
    # This rule blocks traffic from specific ASNs (network providers).
    # Useful for blocking known malicious hosting providers or data centers.
    - name: block-suspicious-asns
      priority: 22
      action:
        block:
          customResponse:
            responseCode: 403
            customResponseBodyKey: json-error
      # Block requests from specific ASNs (e.g., suspicious hosting providers)
      statement:
        asnMatch:
          autonomousSystemNumbers:
            - 64496  # Reserved for documentation/examples
            - 64497  # Reserved for documentation/examples
      ruleLabels:
        - name: "security:asn-block"
        - name: "block:suspicious-network"
      visibilityConfig:
        cloudWatchMetricsEnabled: true
        metricName: block-suspicious-asns
        sampledRequestsEnabled: true


---
# ====================
# API FIELD REFERENCE
# ====================
#
# REQUIRED FIELDS:
# - spec.defaultAction (exactly one: allow or block)
# - spec.visibilityConfig.cloudWatchMetricsEnabled (bool)
# - spec.visibilityConfig.metricName (string, 1-128 chars)
# - spec.visibilityConfig.sampledRequestsEnabled (bool)
#
# OPTIONAL TOP-LEVEL FIELDS:
# - spec.description (string, max 256 chars)
# - spec.rules[] (array, max 100)
# - spec.tokenDomains[] (array, max 10)
# - spec.customResponseBodies (map[string]AwsWebAclCustomResponseBody)
# - spec.captchaConfig.immunityTime (int64, 60-259200 seconds)
# - spec.challengeConfig.immunityTime (int64, 60-259200 seconds)
#
# RULE REQUIRED FIELDS:
# - name (string, 1-128 chars, pattern: ^[0-9A-Za-z_-]+$)
# - priority (int32, min 0, must be unique)
# - action OR overrideAction (exactly one, mutually exclusive)
# - statement (exactly one statement type)
#
# RULE OPTIONAL FIELDS:
# - ruleLabels[] (array, max 100, name: 1-1024 chars)
# - captchaConfig.immunityTime (overrides global)
# - challengeConfig.immunityTime (overrides global)
# - visibilityConfig (overrides WebACL-level)
#
# ACTION TYPES (for regular rules):
# - allow: { customRequestHandling: { insertHeaders: [...] } }
# - block: { customResponse: { responseCode, customResponseBodyKey, responseHeaders } }
# - count: { customRequestHandling: { insertHeaders: [...] } }
# - captcha: { customRequestHandling: { insertHeaders: [...] } }
# - challenge: { customRequestHandling: { insertHeaders: [...] } }
#
# OVERRIDE ACTION TYPES (for managed rule groups):
# - none: {}  # Don't override, use group's actions
# - count: { customRequestHandling: { insertHeaders: [...] } }  # Override all to count
#
# STATEMENT TYPES (exactly one per rule):
# - geoMatch: { countryCodes: [...], forwardedIPConfig: {...} }
# - rateBased: { limit: int64, forwardedIPConfig: {...} }
# - byteMatch: { searchString, positionalConstraint, fieldToMatch, textTransformations }
# - managedRuleGroup: { vendorName, name, version, excludedRules, managedRuleGroupConfigs, ruleActionOverrides }
# - labelMatch: { key: "label:name", scope: "LABEL" | "NAMESPACE" }  # Match based on labels from previous rules
# - sizeConstraint: { comparisonOperator: "EQ|NE|LE|LT|GE|GT", size, fieldToMatch, textTransformations }  # Size-based filtering
# - sqliMatch: { fieldToMatch, textTransformations, sensitivityLevel: "LOW" | "HIGH" }  # SQL injection detection
# - xssMatch: { fieldToMatch, textTransformations }  # Cross-site scripting detection
# - regexMatch: { regexString, fieldToMatch, textTransformations }  # Regular expression pattern matching
# - asnMatch: { autonomousSystemNumbers: [int64...], forwardedIPConfig: {...} }  # Match by ASN
#
# NOTE: Logical operators (and/or/not) are NOT supported due to Kubernetes API limitations
# NOTE: Use ipSetReference statement to reference separate AwsIPSet resources for IP filtering

Implementation Plan

This proposal covers the initial implementation of AWS WebACL support in Cloud Manager:

Scope:

  • 11 statement types for rule definitions (including ipSetReference)
  • Separate AwsIPSet CRD for IP address management
  • WebACL-level and rule-level visibility configuration
  • Custom response bodies and request handling
  • CAPTCHA/Challenge configuration
  • Managed rule group integration
  • CloudWatch metrics integration (via VisibilityConfig)

Out of Scope (Future Enhancements):

  • Logical operators (And/Or/Not) - requires workaround for Kubernetes API circular reference limitations
  • RegexPatternSet references - covered by inline regexMatch
  • AssociationConfig - not applicable for ALB-only usage

Target Use Case:

  • Application Load Balancer (ALB) integration
  • Request body inspection fixed at 8KB (ALB limitation)

See example YAML above for comprehensive API usage covering all supported features.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions