1.kubernetes
Migrating to Single Alb with Waf

This guide outlines the process of consolidating multiple AWS Application Load Balancers (ALBs) managed by Kubernetes Ingress resources into a single shared Ingress ALB. It also covers integrating AWS WAF for enhanced security on your subdomains and services, specifically focusing on how to translate Ingress-level CIDR restrictions into WAF rules.

Table of Contents

  1. Introduction

  2. Prerequisites

  3. Identifying Existing Ingress Resources and CIDRs

  4. Planning Your Migration

  5. Modifying Ingress Resources for a Shared ALB

  6. Integrating AWS WAF

  7. Verification and Testing

  8. Troubleshooting

1. Introduction

Managing numerous ALBs can lead to increased operational overhead and costs. This guide will walk you through fetching existing Ingress configurations, modifying them to use a shared ALB, configuring access logs, and attaching WAF to protect your applications, specifically handling domain-based IP restrictions.

2. Prerequisites

Before you begin, ensure you have the following:

  • Kubernetes Cluster: An EKS cluster or a Kubernetes cluster with the AWS Load Balancer Controller installed and configured.
  • kubectl: Configured to connect to your Kubernetes cluster.
  • jq: A lightweight and flexible command-line JSON processor.
  • AWS CLI: Configured with appropriate permissions to manage ALBs, S3, and WAF.
  • IAM Permissions: Sufficient IAM permissions to create/modify Ingress resources, S3 buckets/policies, and WAF Web ACLs.
  • S3 Bucket: An S3 bucket for storing ALB access logs.

3. Identifying Existing Ingress Resources and CIDRs

First, let's gather information about your current Ingress configurations and any specific inbound CIDR restrictions. This helps in understanding your current setup and planning the migration.

Fetch All Ingress Files

kubectl get ingress --all-namespaces -o yaml > all-ingress.yaml

Fetch All Inbound CIDRs Used by ALBs

kubectl get ingress --all-namespaces -o json | \
jq -r '.items[] | select(.metadata.annotations."alb.ingress.kubernetes.io/inbound-cidrs") |
        "\(.metadata.namespace)/\(.metadata.name): \(.metadata.annotations."alb.ingress.kubernetes.io/inbound-cidrs")"' \
> inbound-cidrs.txt

Review inbound-cidrs.txt to understand which services currently have IP restrictions. You'll use this information to create WAF IP sets and rules.

4. Planning Your Migration

Before making changes:

  • Shared ALB Name: e.g., shared-application-alb
  • Consolidation Strategy: Decide which ingress resources migrate.
  • DNS Updates: Prepare to repoint DNS to the new shared ALB CNAME.
  • WAF Rules: Derived from inbound-cidrs.txt.

5. Modifying Ingress Resources for a Shared ALB

Example Ingress Modification

Existing ingress file (SANITIZED)

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: portal-service
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/subnets: {{SUBNET_ID_1}},{{SUBNET_ID_2}}
    alb.ingress.kubernetes.io/certificate-arn: {{ACM_CERT_ARN}}
    alb.ingress.kubernetes.io/ssl-policy: ELBSecurityPolicy-TLS13-1-2-2021-06
spec:
  rules:
    - host: "{{DOMAIN_1}}"
      http:
        paths:
          - path: "/"
            pathType: Prefix
            backend:
              service:
                name: portal-service-backend
                port:
                  number: 443

New ingress file (SANITIZED)

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: new-portal-service
  namespace: default
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/group.name: shared-application-alb
    alb.ingress.kubernetes.io/backend-protocol: HTTP
    alb.ingress.kubernetes.io/certificate-arn: "{{ACM_CERT_ARN}}"
    alb.ingress.kubernetes.io/healthcheck-protocol: HTTP
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
    alb.ingress.kubernetes.io/ssl-redirect: '443'
    alb.ingress.kubernetes.io/subnets: {{SUBNET_ID_1}},{{SUBNET_ID_2}}
    alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=3000
    alb.ingress.kubernetes.io/ssl-policy: ELBSecurityPolicy-TLS13-1-2-2021-06
    alb.ingress.kubernetes.io/wafv2-acl-arn: {{WAF_ACL_ARN}}
spec:
  rules:
  - host: {{DOMAIN_1}}
    http:
      paths:
      - backend:
          service:
            name: portal-service-backend
            port:
              number: 443
        path: /
        pathType: Prefix

Explanation of Changes: (No confidential data → unchanged)

5. S3 Bucket Policy for Access Logs

(Sanitized: ELB account ID + AWS account ID)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::{{ELB_ACCOUNT_ID}}:root"
      },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::your-access-log-bucket-name/AWSLogs/{{ACCOUNT_ID}}/*",
      "Condition": {
        "StringEquals": {
          "s3:x-amz-acl": "bucket-owner-full-control"
        }
      }
    }
  ]
}

CFN and json builder

AWSTemplateFormatVersion: '2010-09-09'
Description: 'WAF WebACL for EKS ALBs with managed rule groups and IP set references'
 
Parameters:
  EnvironmentName:
    Type: String
    Default: UAT
    Description: Deployment environment (e.g. UAT, PROD)
  OfficeNetworkIPSetARN:
    Type: String
    Default: '{{WAF_IPSET_OFFICE_ARN}}'
    Description: ARN of the allowed office network IP Set
  PartnerNetworkIPSetARN:
    Type: String
    Default: '{{WAF_IPSET_PARTNER_ARN}}'
    Description: ARN of the partner network IP Set
 
Resources:
  OfficeNetworkIPSet:
    Type: AWS::WAFv2::IPSet
    Properties:
      Name: "office-network-ip-set"
      Description: "Allowed office network CIDRs for UAT ALBs"
      Scope: REGIONAL
      IPAddressVersion: IPV4
      Addresses:
        - "{{CIDR_OFFICE_1}}"   # Example CIDR
        - "{{CIDR_OFFICE_2}}"   # Example CIDR
        
  EKSWAFWebACL:
    Type: AWS::WAFv2::WebACL
    Properties:
      Name: !Sub 'EKS-WAF-ALB-${EnvironmentName}'
      Scope: REGIONAL
      Description: WAF for EKS ALBs
      DefaultAction:
        Block: {}
      VisibilityConfig:
        SampledRequestsEnabled: true
        CloudWatchMetricsEnabled: true
        MetricName: !Sub 'EKS-WAF-ALB-${EnvironmentName}'
      Rules:
 
        - Name: DomainSpecificAccessRules
          Priority: 0
          Statement:
            OrStatement:
              Statements:
 
                - AndStatement:
                    Statements:
                      - ByteMatchStatement:
                          SearchString: "{{DOMAIN_DASHBOARD}}"
                          FieldToMatch:
                            SingleHeader:
                              Name: "host"
                          TextTransformations:
                            - Priority: 0
                              Type: "LOWERCASE"
                          PositionalConstraint: "EXACTLY"
 
                      - IPSetReferenceStatement:
                          Arn: !GetAtt OfficeNetworkIPSet.Arn
                          IPSetForwardedIPConfig:
                            HeaderName: "X-Forwarded-For"
                            FallbackBehavior: "MATCH"
                            Position: "FIRST"
 
                - AndStatement:
                    Statements:
                      - ByteMatchStatement:
                          SearchString: "{{DOMAIN_CUSTOMER_PORTAL}}"
                          FieldToMatch:
                            SingleHeader:
                              Name: "host"
                          TextTransformations:
                            - Priority: 0
                              Type: "LOWERCASE"
                          PositionalConstraint: "EXACTLY"
 
                      - IPSetReferenceStatement:
                          Arn: !Ref PartnerNetworkIPSetARN
                          IPSetForwardedIPConfig:
                            HeaderName: "X-Forwarded-For"
                            FallbackBehavior: "MATCH"
                            Position: "FIRST"
 
                - ByteMatchStatement:
                    SearchString: "{{DOMAIN_API}}"
                    FieldToMatch:
                      SingleHeader:
                        Name: "host"
                    TextTransformations:
                      - Priority: 0
                        Type: "LOWERCASE"
                    PositionalConstraint: "EXACTLY"
          Action:
            Allow: {}
          VisibilityConfig:
            SampledRequestsEnabled: true
            CloudWatchMetricsEnabled: true
            MetricName: DomainSpecificAccessRule
 
        - Name: AWS-AWSManagedRulesAnonymousIpList
          Priority: 1
          Statement:
            ManagedRuleGroupStatement:
              VendorName: AWS
              Name: AWSManagedRulesAnonymousIpList
          OverrideAction:
            None: {}
          VisibilityConfig:
            SampledRequestsEnabled: true
            CloudWatchMetricsEnabled: true
            MetricName: AWS-AWSManagedRulesAnonymousIpList
 
        - Name: AWS-AWSManagedRulesAmazonIpReputationList
          Priority: 2
          Statement:
            ManagedRuleGroupStatement:
              VendorName: AWS
              Name: AWSManagedRulesAmazonIpReputationList
          OverrideAction:
            None: {}
          VisibilityConfig:
            SampledRequestsEnabled: true
            CloudWatchMetricsEnabled: true
            MetricName: AWS-AWSManagedRulesAmazonIpReputationList
 
        - Name: AWS-AWSManagedRulesKnownBadInputsRuleSet
          Priority: 3
          Statement:
            ManagedRuleGroupStatement:
              VendorName: AWS
              Name: AWSManagedRulesKnownBadInputsRuleSet
          OverrideAction:
            None: {}
          VisibilityConfig:
            SampledRequestsEnabled: true
            CloudWatchMetricsEnabled: true
            MetricName: AWS-AWSManagedRulesKnownBadInputsRuleSet
 
        - Name: AWS-AWSManagedRulesLinuxRuleSet
          Priority: 4
          Statement:
            ManagedRuleGroupStatement:
              VendorName: AWS
              Name: AWSManagedRulesLinuxRuleSet
          OverrideAction:
            None: {}
          VisibilityConfig:
            SampledRequestsEnabled: true
            CloudWatchMetricsEnabled: true
            MetricName: AWS-AWSManagedRulesLinuxRuleSet
 
        - Name: AWS-AWSManagedRulesPHPRuleSet
          Priority: 5
          Statement:
            ManagedRuleGroupStatement:
              VendorName: AWS
              Name: AWSManagedRulesPHPRuleSet
          OverrideAction:
            None: {}
          VisibilityConfig:
            SampledRequestsEnabled: true
            CloudWatchMetricsEnabled: true
            MetricName: AWS-AWSManagedRulesPHPRuleSet
 
        - Name: AWS-AWSManagedRulesUnixRuleSet
          Priority: 6
          Statement:
            ManagedRuleGroupStatement:
              VendorName: AWS
              Name: AWSManagedRulesUnixRuleSet
          OverrideAction:
            None: {}
          VisibilityConfig:
            SampledRequestsEnabled: true
            CloudWatchMetricsEnabled: true
            MetricName: AWS-AWSManagedRulesUnixRuleSet
 
        - Name: AWS-AWSManagedRulesSQLiRuleSet
          Priority: 7
          Statement:
            ManagedRuleGroupStatement:
              VendorName: AWS
              Name: AWSManagedRulesSQLiRuleSet
          OverrideAction:
            None: {}
          VisibilityConfig:
            SampledRequestsEnabled: true
            CloudWatchMetricsEnabled: true
            MetricName: AWS-AWSManagedRulesSQLiRuleSet
 
        - Name: AWS-AWSManagedRulesWindowsRuleSet
          Priority: 8
          Statement:
            ManagedRuleGroupStatement:
              VendorName: AWS
              Name: AWSManagedRulesWindowsRuleSet
          OverrideAction:
            None: {}
          VisibilityConfig:
            SampledRequestsEnabled: true
            CloudWatchMetricsEnabled: true
            MetricName: AWS-AWSManagedRulesWindowsRuleSet
            
Outputs:
  WebACLId:
    Description: The ID of the WAF WebACL
    Value: !GetAtt EKSWAFWebACL.Id
    Export:
      Name: !Sub '${AWS::StackName}-WebACLId'

JSON Rule Builder for all allowed

{
  "Name": "AllowPubliclyAccessibleServices",
  "Priority": 2,
  "Statement": {
    "OrStatement": {
      "Statements": [
        { "ByteMatchStatement": { "SearchString": "{{DOMAIN_PUBLIC_CKYC}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
        { "ByteMatchStatement": { "SearchString": "{{DOMAIN_PUBLIC_LANDING}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
        { "ByteMatchStatement": { "SearchString": "{{DOMAIN_GUEST_VIEW_DOCS}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
        { "ByteMatchStatement": { "SearchString": "{{DOMAIN_PUBLIC_API_GATEWAY}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
        { "ByteMatchStatement": { "SearchString": "{{DOMAIN_VENDOR_INFO}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
        { "ByteMatchStatement": { "SearchString": "{{DOMAIN_EXTERNAL_VENDOR_PORTAL}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
        { "ByteMatchStatement": { "SearchString": "{{DOMAIN_CIBIL}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
        { "ByteMatchStatement": { "SearchString": "{{DOMAIN_CONFIG_PORTAL_API}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
        { "ByteMatchStatement": { "SearchString": "{{DOMAIN_DATA_ANALYTICS}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
        { "ByteMatchStatement": { "SearchString": "{{DOMAIN_NEW_APP_RELEASE}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
        { "ByteMatchStatement": { "SearchString": "{{DOMAIN_UTIL_SERVICES}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
        { "ByteMatchStatement": { "SearchString": "{{DOMAIN_HR_VERIFICATION}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
        { "ByteMatchStatement": { "SearchString": "{{DOMAIN_CRON}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
        { "ByteMatchStatement": { "SearchString": "{{DOMAIN_PAYMENT_CLIENT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
        { "ByteMatchStatement": { "SearchString": "{{DOMAIN_SECURE_FORMS}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } }
      ]
    }
  },
  "Action": { "Allow": {} },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "AllowPubliclyAccessibleServices"
  }
}

✅ ** DomainAndIPSetRoutingRule for restricted service domains**

{
  "Name": "DomainAndIPSetRoutingRule",
  "Priority": 0,
  "Action": { "Allow": {} },
  "Statement": {
    "OrStatement": {
      "Statements": [
        {
          "AndStatement": {
            "Statements": [
              {
                "OrStatement": {
                  "Statements": [
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_CHATBOT_SERVER_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_CHATBOT_PORTAL_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_PAY_SERVER_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_AGENT_PAYMENT_COLLECTION_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_SYNORIQ_LMS_MW_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_SYNC_YUBI_API_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_WEBSITE_CRON_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_DIGITAL_PAYMENT_API_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_SSFB_API_PRE_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_IDFC_API_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_LOS_CO_LENDER_API_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_SCORECARD_API_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_NEW_BIKE_BAZAAR_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_APP_SUAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_GBL_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_GBL_BACKEND_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_GBL_PAYMENTS_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_TEST_SERVER_NET}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_NACH_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_NACH_SERVER_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_MUTHOOT_DISBURSAL_API_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_MUTHOOT_CUSTOMER_COLLECTION_API_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_MIDDLEWARE_UAT_EKS}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_PA_INSURANCE_API_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_YUBI_API_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_AUTH_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_MANDATE_INTEGRATIONS_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_MANDATE_CRON_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_SFDC_API_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_EKYC_ENCRYPTION_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_EKYC_API_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_PUSH_YUBI_API_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_SSFB_API_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } }
                  ]
                }
              },
              {
                "IPSetReferenceStatement": {
                  "ARN": "{{WAF_IPSET_OFFICE_UAT_ARN}}"
                }
              }
            ]
          }
        },
        {
          "AndStatement": {
            "Statements": [
              {
                "OrStatement": {
                  "Statements": [
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_BUSINESS_RULES_API_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
                    { "ByteMatchStatement": { "SearchString": "{{DOMAIN_SFDC_NEW_MW_API_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } }
                  ]
                }
              },
              {
                "IPSetReferenceStatement": {
                  "ARN": "{{WAF_IPSET_BUSINESS_RULES_UAT_ARN}}"
                }
              }
            ]
          }
        },
        {
          "AndStatement": {
            "Statements": [
              { "ByteMatchStatement": { "SearchString": "{{DOMAIN_DIGITAL_JOURNEY_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
              { "IPSetReferenceStatement": { "ARN": "{{WAF_IPSET_DIGITAL_JOURNEY_UAT_ARN}}" } }
            ]
          }
        },
        {
          "AndStatement": {
            "Statements": [
              { "ByteMatchStatement": { "SearchString": "{{DOMAIN_FINONE_LMS_API_UAT}}", "FieldToMatch": { "SingleHeader": { "Name": "host" } }, "TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }], "PositionalConstraint": "EXACTLY" } },
              { "IPSetReferenceStatement": { "ARN": "{{WAF_IPSET_FINONE_LMS_API_UAT_ARN}}" } }
            ]
          }
        }
      ]
    }
  },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "DomainAndIPSetRoutingRule"
  }
}

Troubleshoot WAF 403 Errors

The curl -vk https://{{DOMAIN_CUSTOMER_PORTAL}}/ command is used to check whether your AWS WAF, ALB, or Ingress routing is working properly.

Test WITHOUT DNS mapping

curl -vk https://{{ALB_HOSTNAME_SHARED}} \
     -H "Host: {{DOMAIN_APP_UAT}}"

Flag meanings:

  • -v → verbose output
  • -k → ignore SSL verification
  • URL → domain served behind WAF/ALB

Example 404 Error

curl -v {{DOMAIN_APP_UAT}}
* TLSv1.2 (IN), TLS header, Supplemental data (23):
< HTTP/2 404
< server: awselb/2.0
< date: Wed, 15 Oct 2025 08:19:04 GMT
< content-type: text/plain; charset=utf-8
< content-length: 0
<
* Connection #0 to host {{ALB_HOSTNAME_SHARED_UAT}}

A 404 here was due to unhealthy target group caused by Ingress misconfiguration.


7. Verification and Testing

After applying the changes, verify everything end-to-end.

✔ Check ALB Status

kubectl get ingress -n <your-namespace> new-portal-service \
  -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'

You should see:

{{ALB_HOSTNAME_SHARED}}

✔ Access Logs

Check your S3 bucket to confirm logs are delivered to:

s3://{{S3_ALB_LOG_BUCKET}}/AWSLogs/{{ACCOUNT_ID}}/

✔ WAF Association

In AWS WAF → Web ACLs → {{WAF_ACL_NAME}}, verify:

  • The shared ALB is listed under Associated AWS Resources

✔ Application Connectivity

Test all services now routed via shared ALB:

  • HTTP
  • HTTPS
  • All paths /api, /login, /health
  • All hosts (Host: headers)

Example test:

curl -vk https://{{ALB_HOSTNAME_SHARED}} \
   -H "Host: {{DOMAIN_CUSTOMER_PORTAL}}"

✔ WAF Rule Testing

Test your IP-based WAF logic:

  • From allowed IP → must load normally
  • From blocked IP → expect WAF 403

Examples:

# Allowed office network
curl -v https://{{DOMAIN_DASHBOARD}} --interface {{OFFICE_IP}}
 
# Blocked (home network)
curl -v https://{{DOMAIN_DASHBOARD}}

Test public endpoints like:

https://{{DOMAIN_PUBLIC_API}}

8. Troubleshooting

✔ Ingress Events

kubectl describe ingress <ingress-name>

Look for errors from AWS Load Balancer Controller.


✔ Controller Logs

kubectl logs -n kube-system deploy/aws-load-balancer-controller