Skip to content

Envoy Gateway API

The Envoy Gateway is an open-source project that provides an implementation of the Gateway API using Envoyproxy as the data plane. The Gateway API is a set of APIs that allow users to configure API gateways using a declarative configuration model.

Installation

Run the helm command to install Envoy Gateway.

Run the Envoy Gateway deployment Script /opt/genestack/bin/install-envoy-gateway.sh
#!/bin/bash
# shellcheck disable=SC2124,SC2145,SC2294
#
# NOTE: This script uses OCI registry format instead of traditional helm repo
# because Envoy Gateway only publishes charts to OCI registries (docker.io/envoyproxy).
# Unlike other scripts that use 'helm repo add', OCI registries are accessed directly.
#
GLOBAL_OVERRIDES_DIR="/etc/genestack/helm-configs/global_overrides"
SERVICE_CONFIG_DIR="/etc/genestack/helm-configs/envoyproxy-gateway"
BASE_OVERRIDES="/opt/genestack/base-helm-configs/envoyproxy-gateway/envoy-gateway-helm-overrides.yaml"

# Read envoy version from helm-chart-versions.yaml
VERSION_FILE="/etc/genestack/helm-chart-versions.yaml"
if [ ! -f "$VERSION_FILE" ]; then
    echo "Error: helm-chart-versions.yaml not found at $VERSION_FILE"
    exit 1
fi

# Extract envoy version using grep and sed
ENVOY_VERSION=$(grep 'envoy:' "$VERSION_FILE" | sed 's/.*envoy: *//')

if [ -z "$ENVOY_VERSION" ]; then
    echo "Error: Could not extract envoy version from $VERSION_FILE"
    exit 1
fi

HELM_CMD="helm upgrade --install envoyproxy-gateway oci://docker.io/envoyproxy/gateway-helm \
                       --version ${ENVOY_VERSION} \
                       --namespace envoyproxy-gateway-system \
                       --create-namespace"

HELM_CMD+=" -f ${BASE_OVERRIDES}"

for dir in "$GLOBAL_OVERRIDES_DIR" "$SERVICE_CONFIG_DIR"; do
    if compgen -G "${dir}/*.yaml" > /dev/null; then
        for yaml_file in "${dir}"/*.yaml; do
            # Avoid re-adding the base override file if present in the service directory
            if [ "${yaml_file}" != "${BASE_OVERRIDES}" ]; then
                HELM_CMD+=" -f ${yaml_file}"
            fi
        done
    fi
done

HELM_CMD+=" $@"

echo "Executing Helm command:"
echo "${HELM_CMD}"
eval "${HELM_CMD}"

# Install egctl
if [ ! -f "/usr/local/bin/egctl" ]; then
    sudo mkdir -p /opt/egctl-install
    pushd /opt/egctl-install || exit 1
        sudo wget "https://github.com/envoyproxy/gateway/releases/download/${ENVOY_VERSION}/egctl_${ENVOY_VERSION}_linux_amd64.tar.gz" -O egctl.tar.gz
        sudo tar -xvf egctl.tar.gz
        sudo install -o root -g root -m 0755 bin/linux/amd64/egctl /usr/local/bin/egctl
        /usr/local/bin/egctl completion bash > /tmp/egctl.bash
        sudo mv /tmp/egctl.bash /etc/bash_completion.d/egctl
    popd || exit 1
fi

The install script will deploy Envoy Gateway to the envoy-gateway-system namespace via Helm.

Setup

Run the Envoy Gateway setup Script /opt/genestack/bin/setup-envoy-gateway.sh
#!/bin/bash
# shellcheck disable=SC2045,SC2124,SC2145,SC2164,SC2236,SC2294

# Function to display general usage
usage() {
    cat << EOF
Usage: $0 [OPTIONS]

OPTIONS:
    -e, --email EMAIL           Email address for ACME (required for ACME setup)
    -d, --domain DOMAIN         Gateway domain name (default: cluster.local)
    -c, --challenge METHOD      ACME challenge method: http01 or dns01 (default: http01)
    -p, --dns-plugin PLUGIN     DNS01 plugin (only used with dns01)
    -h, --help [PLUGIN]         Display this help message, or detailed help for a specific plugin

    # Generic credentials (usage depends on --dns-plugin):
    --api-key KEY               API Key (used by multiple providers)
    --api-secret SECRET         API Secret (used by multiple providers)
    --api-token TOKEN           API Token (used by multiple providers)
    --username USERNAME         Username (used by some providers)
    --password PASSWORD         Password (used by some providers)
    --project-id ID             Project ID (used by some providers)
    --tenant-id ID              Tenant ID (used by some providers)
    --subscription-id ID        Subscription ID (used by some providers)
    --resource-group NAME       Resource Group (used by some providers)
    --region REGION             Region (used by some providers)
    --hosted-zone-id ID         Hosted Zone ID (used by some providers)
    --service-account-file FILE Path to service account JSON file (used by some providers)
    --nameserver HOST           Nameserver (used by some providers)
    --tsig-key-name NAME        TSIG key name (used by some providers)
    --tsig-secret SECRET        TSIG Secret (used by some providers)
    --tsig-algorithm ALG        TSIG Algorithm (used by some providers)

SUPPORTED DNS PLUGINS:
    godaddy         GoDaddy DNS (requires webhook)
    rackspace       Rackspace Cloud DNS (requires webhook)
    cloudflare      Cloudflare DNS (built-in support)
    route53         AWS Route53 (built-in support)
    azuredns        Azure DNS (built-in support)
    google          Google Cloud DNS (built-in support)
    digitalocean    DigitalOcean DNS (built-in support)
    acmedns         ACME-DNS (built-in support)
    rfc2136         RFC2136 Dynamic DNS (built-in support)

For detailed help on a specific plugin, use: $0 --help PLUGIN
Example: $0 --help cloudflare

EXAMPLES:
    # Basic setup with HTTP01 challenge
    $0 --email user@example.com --domain example.com

    # Setup without ACME (no SSL certificates)
    $0 --domain example.com

    # Get detailed help for Cloudflare
    $0 --help cloudflare

    # Interactive mode
    $0
EOF
}

# Function to display detailed help for specific plugins
usage_plugin() {
    local plugin="$1"

    case "$plugin" in
        godaddy)
            cat << EOF
GoDaddy DNS Configuration
=========================

GoDaddy uses a webhook for DNS01 challenges and requires API credentials.

REQUIREMENTS:
    --api-key KEY               GoDaddy API Key
    --api-secret SECRET         GoDaddy API Secret

HOW TO GET CREDENTIALS:
    1. Log in to your GoDaddy account
    2. Go to https://developer.godaddy.com/keys
    3. Create a new API key (Production or Test)
    4. Save both the API Key and Secret

EXAMPLE:
    $0 --email user@example.com --domain example.com --challenge dns01 \\
       --dns-plugin godaddy --api-key YOUR_KEY --api-secret YOUR_SECRET

NOTES:
    - GoDaddy requires a webhook to be installed in your cluster
    - The script will automatically install the webhook from the Helm chart
    - Make sure cert-manager is already installed in your cluster
EOF
            ;;
        rackspace)
            cat << EOF
Rackspace Cloud DNS Configuration
==================================

Rackspace uses a webhook for DNS01 challenges and requires your Rackspace credentials.

REQUIREMENTS:
    --username USERNAME         Rackspace username
    --api-key KEY               Rackspace API Key

HOW TO GET CREDENTIALS:
    1. Log in to your Rackspace Cloud Control Panel
    2. Click on your username in the upper right corner
    3. Select "Account Settings"
    4. Click on "API Keys" or navigate to https://manage.rackspace.com/APIKeys
    5. View or generate your API key

EXAMPLE:
    $0 --email user@example.com --domain example.com --challenge dns01 \\
       --dns-plugin rackspace --username YOUR_USERNAME --api-key YOUR_API_KEY

NOTES:
    - Rackspace requires a webhook to be installed in your cluster
    - The script will clone and install the webhook from GitHub
    - The webhook will be installed in the cert-manager namespace
EOF
            ;;
        cloudflare)
            cat << EOF
Cloudflare DNS Configuration
=============================

Cloudflare has built-in support in cert-manager and offers two authentication methods.

OPTION 1 - API Token (RECOMMENDED):
    --api-token TOKEN           Cloudflare API Token

    How to create an API Token:
    1. Log in to your Cloudflare account
    2. Go to: User Profile > API Tokens > Create Token
    3. Use the "Edit zone DNS" template or create a custom token with:
       Permissions:
         - Zone > DNS > Edit
         - Zone > Zone > Read
       Zone Resources:
         - Include > All Zones (or specific zones)
    4. Copy the generated token

    Example:
    $0 --email user@example.com --domain example.com --challenge dns01 \\
       --dns-plugin cloudflare --api-token YOUR_TOKEN

OPTION 2 - API Key (Legacy):
    --api-key KEY               Cloudflare Global API Key
    --email EMAIL               Your Cloudflare account email

    How to get your API Key:
    1. Log in to your Cloudflare account
    2. Go to: User Profile > API Tokens > API Keys > Global API Key > View
    3. Enter your password to view the key

    Example:
    $0 --email user@example.com --domain example.com --challenge dns01 \\
       --dns-plugin cloudflare --api-key YOUR_KEY --email cf@example.com

NOTES:
    - API Tokens are more secure and recommended
    - API Tokens can be scoped to specific zones and permissions
    - API Keys have full account access
    - No webhook required - Cloudflare support is built into cert-manager
EOF
            ;;
        route53)
            cat << EOF
AWS Route53 DNS Configuration
==============================

Route53 has built-in support in cert-manager and offers multiple authentication methods.

OPTION 1 - IAM Role (RECOMMENDED for EKS):
    No credentials needed - uses pod IAM role

    Setup:
    1. Create an IAM policy with Route53 permissions:
       {
         "Version": "2012-10-17",
         "Statement": [
           {
             "Effect": "Allow",
             "Action": "route53:GetChange",
             "Resource": "arn:aws:route53:::change/*"
           },
           {
             "Effect": "Allow",
             "Action": [
               "route53:ChangeResourceRecordSets",
               "route53:ListResourceRecordSets"
             ],
             "Resource": "arn:aws:route53:::hostedzone/*"
           },
           {
             "Effect": "Allow",
             "Action": "route53:ListHostedZonesByName",
             "Resource": "*"
           }
         ]
       }
    2. Attach this policy to your cert-manager pod's IAM role
    3. Run the script without credentials

    Example:
    $0 --email user@example.com --domain example.com --challenge dns01 \\
       --dns-plugin route53

OPTION 2 - Access Key/Secret:
    --api-key KEY               AWS Access Key ID
    --api-secret SECRET         AWS Secret Access Key

    Optional:
    --region REGION             AWS Region (default: us-east-1)
    --hosted-zone-id ID         Specific Hosted Zone ID

    Example:
    $0 --email user@example.com --domain example.com --challenge dns01 \\
       --dns-plugin route53 --api-key YOUR_KEY --api-secret YOUR_SECRET \\
       --region us-west-2

NOTES:
    - IAM role method is more secure and recommended for EKS clusters
    - Hosted Zone ID is optional; cert-manager will auto-discover if not specified
    - No webhook required - Route53 support is built into cert-manager
EOF
            ;;
        azuredns)
            cat << EOF
Azure DNS Configuration
=======================

Azure DNS has built-in support in cert-manager and supports multiple authentication methods.

REQUIRED FOR ALL METHODS:
    --subscription-id ID        Azure Subscription ID
    --resource-group NAME       Azure Resource Group containing the DNS zone

OPTION 1 - Service Principal (Recommended for production):
    --tenant-id ID              Azure Tenant ID
    --api-key KEY               Azure Client ID (Application ID)
    --api-secret SECRET         Azure Client Secret

    How to create a Service Principal:
    1. Register an application in Azure AD:
       az ad sp create-for-rbac --name cert-manager-dns
    2. Note the appId (Client ID), password (Client Secret), and tenant
    3. Grant DNS Zone Contributor role:
       az role assignment create \\
         --assignee <appId> \\
         --role "DNS Zone Contributor" \\
         --scope /subscriptions/<subscription-id>/resourceGroups/<resource-group>

    Example:
    $0 --email user@example.com --domain example.com --challenge dns01 \\
       --dns-plugin azuredns --subscription-id SUB_ID --tenant-id TENANT_ID \\
       --resource-group RG_NAME --api-key CLIENT_ID --api-secret CLIENT_SECRET

OPTION 2 - Managed Identity (Recommended for AKS):
    --api-key KEY               Managed Identity Client ID (optional)

    Setup:
    1. Enable managed identity on your AKS cluster
    2. Grant the identity DNS Zone Contributor role
    3. Configure workload identity or pod identity

    Example:
    $0 --email user@example.com --domain example.com --challenge dns01 \\
       --dns-plugin azuredns --subscription-id SUB_ID --resource-group RG_NAME

HOW TO FIND YOUR IDS:
    Subscription ID: az account show --query id -o tsv
    Tenant ID:       az account show --query tenantId -o tsv
    Resource Group:  The name of the resource group containing your DNS zone

NOTES:
    - No webhook required - Azure DNS support is built into cert-manager
    - The DNS zone must exist in the specified resource group
    - Managed Identity is more secure for AKS clusters
EOF
            ;;
        google)
            cat << EOF
Google Cloud DNS Configuration
===============================

Google Cloud DNS has built-in support in cert-manager.

REQUIRED:
    --project-id ID             GCP Project ID

OPTION 1 - Workload Identity (RECOMMENDED for GKE):
    No additional credentials needed - uses pod Workload Identity

    Setup:
    1. Enable Workload Identity on your GKE cluster
    2. Create a Google Service Account with DNS Admin role:
       gcloud iam service-accounts create dns-admin
    3. Bind it to the Kubernetes service account:
       gcloud iam service-accounts add-iam-policy-binding \\
         dns-admin@PROJECT_ID.iam.gserviceaccount.com \\
         --role roles/iam.workloadIdentityUser \\
         --member "serviceAccount:PROJECT_ID.svc.id.goog[cert-manager/cert-manager]"
    4. Annotate the cert-manager service account:
       kubectl annotate serviceaccount cert-manager -n cert-manager \\
         iam.gke.io/gcp-service-account=dns-admin@PROJECT_ID.iam.gserviceaccount.com

    Example:
    $0 --email user@example.com --domain example.com --challenge dns01 \\
       --dns-plugin google --project-id YOUR_PROJECT_ID

OPTION 2 - Service Account Key File:
    --service-account-file FILE Path to service account JSON key file

    How to create a Service Account key:
    1. Go to IAM & Admin > Service Accounts in Google Cloud Console
    2. Create a service account with "DNS Administrator" role
    3. Create and download a JSON key file

    Example:
    $0 --email user@example.com --domain example.com --challenge dns01 \\
       --dns-plugin google --project-id YOUR_PROJECT_ID \\
       --service-account-file /path/to/key.json

HOW TO FIND YOUR PROJECT ID:
    gcloud config get-value project

NOTES:
    - Workload Identity is more secure and recommended for GKE
    - Service account needs "DNS Administrator" role on the Cloud DNS zone
    - No webhook required - Google Cloud DNS support is built into cert-manager
EOF
            ;;
        digitalocean)
            cat << EOF
DigitalOcean DNS Configuration
===============================

DigitalOcean has built-in support in cert-manager and uses API tokens.

REQUIREMENTS:
    --api-token TOKEN           DigitalOcean API Token

HOW TO GET AN API TOKEN:
    1. Log in to your DigitalOcean account
    2. Go to API section: https://cloud.digitalocean.com/account/api/tokens
    3. Click "Generate New Token"
    4. Give it a name (e.g., "cert-manager")
    5. Make sure "Write" scope is enabled
    6. Copy the generated token (shown only once)

EXAMPLE:
    $0 --email user@example.com --domain example.com --challenge dns01 \\
       --dns-plugin digitalocean --api-token YOUR_TOKEN

NOTES:
    - Your domain must be managed by DigitalOcean DNS
    - The token needs write access to manage DNS records
    - No webhook required - DigitalOcean support is built into cert-manager
    - Store your token securely; it won't be shown again
EOF
            ;;
        acmedns)
            cat << EOF
ACME-DNS Configuration
======================

ACME-DNS is a limited DNS server designed specifically for ACME DNS challenges.

REQUIREMENTS:
    --nameserver HOST           ACME-DNS server hostname (e.g., auth.acme-dns.io)
    --username USERNAME         ACME-DNS username
    --password PASSWORD         ACME-DNS password
    --api-key KEY               ACME-DNS subdomain (fulldomain)

WHAT IS ACME-DNS?
    ACME-DNS is a specialized DNS server that only handles TXT records for ACME
    challenges. It's useful when you can't give cert-manager access to your
    main DNS provider.

HOW TO SET UP:
    1. Register with an ACME-DNS server (or run your own):
       curl -X POST https://auth.acme-dns.io/register
    2. You'll receive:
       - subdomain (use as --api-key)
       - username (use as --username)
       - password (use as --password)
    3. Create a CNAME record in your main DNS:
       _acme-challenge.example.com. IN CNAME <subdomain>.auth.acme-dns.io.

EXAMPLE:
    $0 --email user@example.com --domain example.com --challenge dns01 \\
       --dns-plugin acmedns --nameserver auth.acme-dns.io \\
       --username YOUR_USERNAME --password YOUR_PASSWORD \\
       --api-key YOUR_SUBDOMAIN

NOTES:
    - ACME-DNS is a secure way to handle DNS challenges without full DNS access
    - You need to set up the CNAME delegation in your primary DNS
    - Public ACME-DNS servers: auth.acme-dns.io
    - You can also self-host ACME-DNS
EOF
            ;;
        rfc2136)
            cat << EOF
RFC2136 Dynamic DNS Configuration
==================================

RFC2136 is a protocol for dynamic DNS updates, commonly used with BIND and other
DNS servers that support TSIG authentication.

REQUIREMENTS:
    --nameserver HOST           DNS server hostname/IP (e.g., ns1.example.com:53)
    --tsig-key-name NAME        TSIG key name
    --tsig-secret SECRET        TSIG secret (base64 encoded)
    --tsig-algorithm ALG        TSIG algorithm (default: HMACSHA256)

SUPPORTED ALGORITHMS:
    - HMACMD5
    - HMACSHA1
    - HMACSHA256 (recommended)
    - HMACSHA512

HOW TO SET UP WITH BIND:
    1. Generate a TSIG key:
       tsig-keygen -a HMAC-SHA256 cert-manager > cert-manager.key

    2. Add the key to your BIND configuration (named.conf):
       include "/etc/bind/cert-manager.key";

    3. Allow updates in your zone configuration:
       zone "example.com" {
           type master;
           file "/etc/bind/zones/example.com";
           allow-update { key "cert-manager"; };
       };

    4. Reload BIND:
       rndc reload

EXAMPLE:
    $0 --email user@example.com --domain example.com --challenge dns01 \\
       --dns-plugin rfc2136 --nameserver ns1.example.com:53 \\
       --tsig-key-name cert-manager --tsig-secret BASE64_SECRET \\
       --tsig-algorithm HMACSHA256

NOTES:
    - RFC2136 works with any DNS server supporting dynamic updates
    - Most commonly used with BIND9
    - The DNS server must be configured to accept updates from the TSIG key
    - Ensure firewall allows access to DNS server from your cluster
    - No webhook required - RFC2136 support is built into cert-manager
EOF
            ;;
        *)
            echo "Unknown plugin: $plugin"
            echo ""
            echo "Supported plugins: godaddy, rackspace, cloudflare, route53, azuredns, google, digitalocean, acmedns, rfc2136"
            echo ""
            echo "Use: $0 --help PLUGIN for detailed information"
            exit 1
            ;;
    esac
}

# Initialize variables
ACME_EMAIL=""
GATEWAY_DOMAIN=""
CHALLENGE_METHOD="http01"
DNS_PLUGIN="godaddy"

# Generic credential variables
API_KEY=""
API_SECRET=""
API_TOKEN=""
USERNAME=""
PASSWORD=""
PROJECT_ID=""
TENANT_ID=""
SUBSCRIPTION_ID=""
RESOURCE_GROUP=""
REGION=""
HOSTED_ZONE_ID=""
SERVICE_ACCOUNT_FILE=""
NAMESERVER=""
TSIG_KEY_NAME=""
TSIG_SECRET=""
TSIG_ALGORITHM="HMACSHA256"

INTERACTIVE_MODE=true

# Parse command line arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        -e|--email)
            ACME_EMAIL="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        -d|--domain)
            GATEWAY_DOMAIN="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        -c|--challenge)
            CHALLENGE_METHOD="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        -p|--dns-plugin)
            DNS_PLUGIN="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        --api-key)
            API_KEY="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        --api-secret)
            API_SECRET="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        --api-token)
            API_TOKEN="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        --username)
            USERNAME="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        --password)
            PASSWORD="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        --project-id)
            PROJECT_ID="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        --tenant-id)
            TENANT_ID="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        --subscription-id)
            SUBSCRIPTION_ID="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        --resource-group)
            RESOURCE_GROUP="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        --region)
            REGION="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        --hosted-zone-id)
            HOSTED_ZONE_ID="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        --service-account-file)
            SERVICE_ACCOUNT_FILE="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        --nameserver)
            NAMESERVER="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        --tsig-key-name)
            TSIG_KEY_NAME="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        --tsig-secret)
            TSIG_SECRET="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        --tsig-algorithm)
            TSIG_ALGORITHM="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        -h|--help)
            if [[ -n "$2" && "$2" != -* ]]; then
                # Detailed help for specific plugin
                usage_plugin "$2"
                exit 0
            else
                # General help
                usage
                exit 0
            fi
            ;;
        *)
            echo "Unknown option: $1"
            usage
            exit 1
            ;;
    esac
done

# Validate challenge method
if [[ "$CHALLENGE_METHOD" != "http01" && "$CHALLENGE_METHOD" != "dns01" ]]; then
    echo "Error: Invalid challenge method. Must be 'http01' or 'dns01'"
    exit 1
fi

# Validate DNS plugin
VALID_PLUGINS="godaddy rackspace cloudflare route53 azuredns google digitalocean acmedns rfc2136"
if [[ "$CHALLENGE_METHOD" == "dns01" ]]; then
    if [[ ! " $VALID_PLUGINS " =~ " $DNS_PLUGIN " ]]; then
        echo "Error: Invalid DNS plugin. Must be one of: $VALID_PLUGINS"
        echo "Use --help PLUGIN for detailed information about a specific plugin"
        exit 1
    fi
fi

# Interactive prompts (fallback for missing parameters)
if [[ "$INTERACTIVE_MODE" == "true" ]]; then
    if [ -z "${ACME_EMAIL}" ]; then
        read -rp "Enter email address for ACME (press enter to skip ACME setup): " ACME_EMAIL
    fi

    if [ -z "${GATEWAY_DOMAIN}" ]; then
        echo "The domain name for the gateway is required, if you do not have a domain name press enter to use the default"
        read -rp "Enter the domain name for the gateway [cluster.local]: " GATEWAY_DOMAIN
        export GATEWAY_DOMAIN=${GATEWAY_DOMAIN:-cluster.local}
    fi
fi

# Set default domain if not provided
if [ -z "${GATEWAY_DOMAIN}" ]; then
    GATEWAY_DOMAIN="cluster.local"
fi

# Function to validate and prompt for credentials based on DNS plugin
validate_credentials() {
    local plugin="$1"

    case "$plugin" in
        godaddy)
            if [[ -z "$API_KEY" || -z "$API_SECRET" ]]; then
                if [[ "$INTERACTIVE_MODE" == "true" ]]; then
                    echo ""
                    echo "GoDaddy requires API credentials. Get them from: https://developer.godaddy.com/keys"
                    read -rp "Enter your GoDaddy API Key: " API_KEY
                    read -rsp "Enter your GoDaddy API Secret: " API_SECRET
                    echo
                else
                    echo "Error: GoDaddy requires --api-key and --api-secret"
                    echo "Use: $0 --help godaddy for more information"
                    exit 1
                fi
            fi
            ;;
        rackspace)
            if [[ -z "$USERNAME" || -z "$API_KEY" ]]; then
                if [[ "$INTERACTIVE_MODE" == "true" ]]; then
                    echo ""
                    echo "Rackspace requires your account credentials."
                    read -rp "Enter your Rackspace username: " USERNAME
                    read -rsp "Enter your Rackspace API Key: " API_KEY
                    echo
                else
                    echo "Error: Rackspace requires --username and --api-key"
                    echo "Use: $0 --help rackspace for more information"
                    exit 1
                fi
            fi
            ;;
        cloudflare)
            if [[ -z "$API_TOKEN" && ( -z "$API_KEY" || -z "$ACME_EMAIL" ) ]]; then
                if [[ "$INTERACTIVE_MODE" == "true" ]]; then
                    echo ""
                    echo "Cloudflare supports two authentication methods:"
                    echo "  1. API Token (recommended - more secure)"
                    echo "  2. API Key (requires email + key)"
                    echo ""
                    read -rp "Do you want to use API Token? (y/n) [y]: " USE_TOKEN
                    USE_TOKEN=${USE_TOKEN:-y}

                    if [[ "$USE_TOKEN" == "y" || "$USE_TOKEN" == "Y" ]]; then
                        echo "Create a token at: User Profile > API Tokens > Create Token"
                        read -rsp "Enter your Cloudflare API Token: " API_TOKEN
                        echo
                    else
                        if [ -z "${ACME_EMAIL}" ]; then
                            read -rp "Enter your Cloudflare account email: " ACME_EMAIL
                        fi
                        echo "Get your API key at: User Profile > API Tokens > Global API Key"
                        read -rsp "Enter your Cloudflare API Key: " API_KEY
                        echo
                    fi
                else
                    echo "Error: Cloudflare requires either:"
                    echo "  --api-token TOKEN (recommended), or"
                    echo "  --api-key KEY --email EMAIL"
                    echo "Use: $0 --help cloudflare for more information"
                    exit 1
                fi
            fi
            ;;
        route53)
            # Route53 can work with IAM roles (no creds needed) or explicit credentials
            if [[ -n "$API_KEY" && -z "$API_SECRET" ]] || [[ -z "$API_KEY" && -n "$API_SECRET" ]]; then
                echo "Error: Route53 requires both --api-key (Access Key ID) and --api-secret (Secret Access Key), or neither (for IAM role)"
                echo "Use: $0 --help route53 for more information"
                exit 1
            fi
            # Set default region if not provided
            REGION=${REGION:-us-east-1}
            ;;
        azuredns)
            if [[ -z "$SUBSCRIPTION_ID" || -z "$RESOURCE_GROUP" ]]; then
                if [[ "$INTERACTIVE_MODE" == "true" ]]; then
                    echo ""
                    echo "Azure DNS requires subscription and resource group information."
                    read -rp "Enter your Azure Subscription ID: " SUBSCRIPTION_ID
                    read -rp "Enter your Azure Resource Group: " RESOURCE_GROUP
                else
                    echo "Error: Azure DNS requires --subscription-id and --resource-group"
                    echo "Use: $0 --help azuredns for more information"
                    exit 1
                fi
            fi
            # Check if using Service Principal or Managed Identity
            if [[ -n "$API_KEY" || -n "$API_SECRET" || -n "$TENANT_ID" ]]; then
                # Service Principal mode - all three required
                if [[ -z "$TENANT_ID" || -z "$API_KEY" || -z "$API_SECRET" ]]; then
                    if [[ "$INTERACTIVE_MODE" == "true" ]]; then
                        echo ""
                        echo "Service Principal authentication requires tenant ID, client ID, and client secret."
                        read -rp "Enter your Azure Tenant ID: " TENANT_ID
                        read -rp "Enter your Azure Client ID: " API_KEY
                        read -rsp "Enter your Azure Client Secret: " API_SECRET
                        echo
                    else
                        echo "Error: Azure Service Principal requires --tenant-id, --api-key (Client ID), and --api-secret (Client Secret)"
                        echo "Use: $0 --help azuredns for more information"
                        exit 1
                    fi
                fi
            fi
            ;;
        google)
            if [[ -z "$PROJECT_ID" ]]; then
                if [[ "$INTERACTIVE_MODE" == "true" ]]; then
                    echo ""
                    echo "Google Cloud DNS requires your project ID."
                    read -rp "Enter your GCP Project ID: " PROJECT_ID
                else
                    echo "Error: Google Cloud DNS requires --project-id"
                    echo "Use: $0 --help google for more information"
                    exit 1
                fi
            fi
            # Service account file is optional (can use Workload Identity)
            if [[ -n "$SERVICE_ACCOUNT_FILE" && ! -f "$SERVICE_ACCOUNT_FILE" ]]; then
                echo "Error: Service account file not found: $SERVICE_ACCOUNT_FILE"
                exit 1
            fi
            ;;
        digitalocean)
            if [[ -z "$API_TOKEN" ]]; then
                if [[ "$INTERACTIVE_MODE" == "true" ]]; then
                    echo ""
                    echo "DigitalOcean requires an API token. Get one at: https://cloud.digitalocean.com/account/api/tokens"
                    read -rsp "Enter your DigitalOcean API Token: " API_TOKEN
                    echo
                else
                    echo "Error: DigitalOcean requires --api-token"
                    echo "Use: $0 --help digitalocean for more information"
                    exit 1
                fi
            fi
            ;;
        acmedns)
            if [[ -z "$NAMESERVER" || -z "$API_KEY" || -z "$USERNAME" || -z "$PASSWORD" ]]; then
                if [[ "$INTERACTIVE_MODE" == "true" ]]; then
                    echo ""
                    echo "ACME-DNS requires registration information from your ACME-DNS server."
                    read -rp "Enter ACME-DNS server hostname: " NAMESERVER
                    read -rp "Enter ACME-DNS username: " USERNAME
                    read -rsp "Enter ACME-DNS password: " PASSWORD
                    echo
                    read -rp "Enter ACME-DNS subdomain (fulldomain): " API_KEY
                else
                    echo "Error: ACME-DNS requires --nameserver, --username, --password, and --api-key"
                    echo "Use: $0 --help acmedns for more information"
                    exit 1
                fi
            fi
            ;;
        rfc2136)
            if [[ -z "$NAMESERVER" || -z "$TSIG_KEY_NAME" || -z "$TSIG_SECRET" ]]; then
                if [[ "$INTERACTIVE_MODE" == "true" ]]; then
                    echo ""
                    echo "RFC2136 requires DNS server and TSIG authentication information."
                    read -rp "Enter DNS server hostname:port (e.g., ns1.example.com:53): " NAMESERVER
                    read -rp "Enter TSIG key name: " TSIG_KEY_NAME
                    read -rsp "Enter TSIG secret (base64): " TSIG_SECRET
                    echo
                    read -rp "Enter TSIG algorithm [HMACSHA256]: " TSIG_ALG_INPUT
                    TSIG_ALGORITHM=${TSIG_ALG_INPUT:-HMACSHA256}
                else
                    echo "Error: RFC2136 requires --nameserver, --tsig-key-name, and --tsig-secret"
                    echo "Use: $0 --help rfc2136 for more information"
                    exit 1
                fi
            fi
            ;;
    esac
}

# Validate credentials if using DNS01
if [[ "$CHALLENGE_METHOD" == "dns01" ]]; then
    validate_credentials "$DNS_PLUGIN"
fi

# Display configuration
echo "Configuration:"
echo "  Email: ${ACME_EMAIL:-"(not provided - ACME setup will be skipped)"}"
echo "  Domain: ${GATEWAY_DOMAIN}"
echo "  Challenge Method: ${CHALLENGE_METHOD}"
if [[ "$CHALLENGE_METHOD" == "dns01" ]]; then
    echo "  DNS Plugin: ${DNS_PLUGIN}"
fi
echo

# Apply the gateway configuration
kubectl apply -k /etc/genestack/kustomize/envoyproxy-gateway/overlay
echo "Waiting for the gateway to be programmed"
kubectl -n envoy-gateway wait --timeout=5m gateways.gateway.networking.k8s.io flex-gateway --for=condition=Programmed

# Configure ACME if email is provided
if [ ! -z "${ACME_EMAIL}" ]; then
    if [ "${CHALLENGE_METHOD}" = "dns01" ]; then
        case "${DNS_PLUGIN}" in
            godaddy)
                echo "Setting up GoDaddy Webhook for DNS01 challenge..."

                # Install GoDaddy webhook
                echo "Installing GoDaddy webhook..."
                helm repo add godaddy-webhook https://snowdrop.github.io/godaddy-webhook
                helm repo update
                helm install godaddy-webhook godaddy-webhook/godaddy-webhook -n cert-manager --set groupName=acme.${GATEWAY_DOMAIN}

                # Create secret for GoDaddy API credentials
                echo "Creating GoDaddy API credentials secret..."
                kubectl create secret generic godaddy-api-key \
                    --namespace cert-manager \
                    --from-literal=token="${API_KEY}:${API_SECRET}" \
                    --dry-run=client -o yaml | kubectl apply -f -

                # Create ClusterIssuer for DNS01 with GoDaddy
                echo "Creating ClusterIssuer for DNS01 with GoDaddy webhook..."
                cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ${ACME_EMAIL}
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
    - dns01:
        webhook:
          groupName: acme.${GATEWAY_DOMAIN}
          solverName: godaddy
          config:
            apiKeySecretRef:
              name: godaddy-api-key
              key: token
            production: true
            ttl: 600
      selector:
        dnsZones:
        - ${GATEWAY_DOMAIN}
EOF
                ;;
            rackspace)
                echo "Setting up Rackspace Webhook for DNS01 challenge..."

                # Clone Rackspace webhook repository if not already present
                if [ ! -d "/opt/cert-manager-webhook-rackspace" ]; then
                    echo "Cloning Rackspace webhook repository..."
                    cd /opt
                    sudo git clone https://github.com/rackerlabs/cert-manager-webhook-rackspace.git
                else
                    echo "Rackspace webhook repository already exists, updating..."
                    cd /opt/cert-manager-webhook-rackspace
                    sudo git pull
                fi

                # Install Rackspace webhook from local chart
                echo "Installing Rackspace webhook from local chart..."
                helm install cert-manager-webhook-rackspace \
                    /opt/cert-manager-webhook-rackspace/charts/cert-manager-webhook-rackspace \
                    -n cert-manager \
                    --set groupName=acme.${GATEWAY_DOMAIN}

                # Create secret for Rackspace API credentials
                echo "Creating Rackspace API credentials secret..."
                kubectl create secret generic cert-manager-webhook-rackspace-creds \
                    --namespace cert-manager \
                    --from-literal=username="${USERNAME}" \
                    --from-literal=api-key="${API_KEY}" \
                    --dry-run=client -o yaml | kubectl apply -f -

                # Create ClusterIssuer for DNS01 with Rackspace
                echo "Creating ClusterIssuer for DNS01 with Rackspace webhook..."
                cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ${ACME_EMAIL}
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
    - dns01:
        webhook:
          groupName: acme.${GATEWAY_DOMAIN}
          solverName: rackspace
          config:
            authSecretRef: cert-manager-webhook-rackspace-creds
            domainName: ${GATEWAY_DOMAIN}
      selector:
        dnsZones:
        - ${GATEWAY_DOMAIN}
EOF
                ;;
            cloudflare)
                echo "Setting up Cloudflare for DNS01 challenge..."

                # Determine which auth method to use
                if [ -n "${API_TOKEN}" ]; then
                    # API Token method (recommended)
                    echo "Using Cloudflare API Token authentication..."

                    # Create secret for Cloudflare API token
                    echo "Creating Cloudflare API Token secret..."
                    kubectl create secret generic cloudflare-api-token-secret \
                        --namespace cert-manager \
                        --from-literal=api-token="${API_TOKEN}" \
                        --dry-run=client -o yaml | kubectl apply -f -

                    # Create ClusterIssuer for DNS01 with Cloudflare API Token
                    echo "Creating ClusterIssuer for DNS01 with Cloudflare API Token..."
                    cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ${ACME_EMAIL}
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
    - dns01:
        cloudflare:
          apiTokenSecretRef:
            name: cloudflare-api-token-secret
            key: api-token
      selector:
        dnsZones:
        - ${GATEWAY_DOMAIN}
EOF
                else
                    # API Key method
                    echo "Using Cloudflare API Key authentication..."

                    # Create secret for Cloudflare API key
                    echo "Creating Cloudflare API Key secret..."
                    kubectl create secret generic cloudflare-api-key-secret \
                        --namespace cert-manager \
                        --from-literal=api-key="${API_KEY}" \
                        --dry-run=client -o yaml | kubectl apply -f -

                    # Create ClusterIssuer for DNS01 with Cloudflare API Key
                    echo "Creating ClusterIssuer for DNS01 with Cloudflare API Key..."
                    cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ${ACME_EMAIL}
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
    - dns01:
        cloudflare:
          email: ${ACME_EMAIL}
          apiKeySecretRef:
            name: cloudflare-api-key-secret
            key: api-key
      selector:
        dnsZones:
        - ${GATEWAY_DOMAIN}
EOF
                fi
                ;;
            route53)
                echo "Setting up Route53 for DNS01 challenge..."

                if [ -n "${API_KEY}" ]; then
                    # Using explicit credentials
                    echo "Using explicit AWS credentials..."

                    # Create secret for AWS credentials
                    echo "Creating AWS credentials secret..."
                    kubectl create secret generic route53-credentials \
                        --namespace cert-manager \
                        --from-literal=access-key-id="${API_KEY}" \
                        --from-literal=secret-access-key="${API_SECRET}" \
                        --dry-run=client -o yaml | kubectl apply -f -

                    # Create ClusterIssuer with explicit credentials
                    echo "Creating ClusterIssuer for DNS01 with Route53..."
                    cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ${ACME_EMAIL}
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
    - dns01:
        route53:
          region: ${REGION}
          accessKeyID: ${API_KEY}
          secretAccessKeySecretRef:
            name: route53-credentials
            key: secret-access-key
$([ -n "${HOSTED_ZONE_ID}" ] && echo "          hostedZoneID: ${HOSTED_ZONE_ID}")
      selector:
        dnsZones:
        - ${GATEWAY_DOMAIN}
EOF
                else
                    # Using IAM role (no credentials needed)
                    echo "Using IAM role for authentication..."

                    # Create ClusterIssuer without credentials (uses IAM role)
                    echo "Creating ClusterIssuer for DNS01 with Route53..."
                    cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ${ACME_EMAIL}
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
    - dns01:
        route53:
          region: ${REGION}
$([ -n "${HOSTED_ZONE_ID}" ] && echo "          hostedZoneID: ${HOSTED_ZONE_ID}")
      selector:
        dnsZones:
        - ${GATEWAY_DOMAIN}
EOF
                fi
                ;;
            azuredns)
                echo "Setting up Azure DNS for DNS01 challenge..."

                if [ -n "${TENANT_ID}" ]; then
                    # Using Service Principal
                    echo "Using Azure Service Principal authentication..."

                    # Create secret for Azure Service Principal
                    echo "Creating Azure Service Principal secret..."
                    kubectl create secret generic azuredns-credentials \
                        --namespace cert-manager \
                        --from-literal=client-secret="${API_SECRET}" \
                        --dry-run=client -o yaml | kubectl apply -f -

                    # Create ClusterIssuer with Service Principal
                    echo "Creating ClusterIssuer for DNS01 with Azure DNS..."
                    cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ${ACME_EMAIL}
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
    - dns01:
        azureDNS:
          subscriptionID: ${SUBSCRIPTION_ID}
          resourceGroupName: ${RESOURCE_GROUP}
          hostedZoneName: ${GATEWAY_DOMAIN}
          environment: AzurePublicCloud
          tenantID: ${TENANT_ID}
          clientID: ${API_KEY}
          clientSecretSecretRef:
            name: azuredns-credentials
            key: client-secret
      selector:
        dnsZones:
        - ${GATEWAY_DOMAIN}
EOF
                else
                    # Using Managed Identity
                    echo "Using Azure Managed Identity authentication..."

                    # Create ClusterIssuer with Managed Identity
                    echo "Creating ClusterIssuer for DNS01 with Azure DNS..."
                    cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ${ACME_EMAIL}
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
    - dns01:
        azureDNS:
          subscriptionID: ${SUBSCRIPTION_ID}
          resourceGroupName: ${RESOURCE_GROUP}
          hostedZoneName: ${GATEWAY_DOMAIN}
          environment: AzurePublicCloud
$([ -n "${API_KEY}" ] && echo "          managedIdentity:")
$([ -n "${API_KEY}" ] && echo "            clientID: ${API_KEY}")
      selector:
        dnsZones:
        - ${GATEWAY_DOMAIN}
EOF
                fi
                ;;
            google)
                echo "Setting up Google Cloud DNS for DNS01 challenge..."

                if [ -n "${SERVICE_ACCOUNT_FILE}" ]; then
                    # Using Service Account file
                    echo "Using Google Service Account file authentication..."

                    # Create secret from service account file
                    echo "Creating Google Service Account secret..."
                    kubectl create secret generic clouddns-service-account \
                        --namespace cert-manager \
                        --from-file=key.json="${SERVICE_ACCOUNT_FILE}" \
                        --dry-run=client -o yaml | kubectl apply -f -

                    # Create ClusterIssuer with Service Account
                    echo "Creating ClusterIssuer for DNS01 with Google Cloud DNS..."
                    cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ${ACME_EMAIL}
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
    - dns01:
        cloudDNS:
          project: ${PROJECT_ID}
          serviceAccountSecretRef:
            name: clouddns-service-account
            key: key.json
      selector:
        dnsZones:
        - ${GATEWAY_DOMAIN}
EOF
                else
                    # Using Workload Identity
                    echo "Using Google Workload Identity authentication..."

                    # Create ClusterIssuer without credentials (uses Workload Identity)
                    echo "Creating ClusterIssuer for DNS01 with Google Cloud DNS..."
                    cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ${ACME_EMAIL}
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
    - dns01:
        cloudDNS:
          project: ${PROJECT_ID}
      selector:
        dnsZones:
        - ${GATEWAY_DOMAIN}
EOF
                fi
                ;;
            digitalocean)
                echo "Setting up DigitalOcean for DNS01 challenge..."

                # Create secret for DigitalOcean API token
                echo "Creating DigitalOcean API Token secret..."
                kubectl create secret generic digitalocean-dns \
                    --namespace cert-manager \
                    --from-literal=access-token="${API_TOKEN}" \
                    --dry-run=client -o yaml | kubectl apply -f -

                # Create ClusterIssuer for DNS01 with DigitalOcean
                echo "Creating ClusterIssuer for DNS01 with DigitalOcean..."
                cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ${ACME_EMAIL}
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
    - dns01:
        digitalocean:
          tokenSecretRef:
            name: digitalocean-dns
            key: access-token
      selector:
        dnsZones:
        - ${GATEWAY_DOMAIN}
EOF
                ;;
            acmedns)
                echo "Setting up ACME-DNS for DNS01 challenge..."

                # Create secret for ACME-DNS credentials
                echo "Creating ACME-DNS credentials secret..."
                kubectl create secret generic acmedns-credentials \
                    --namespace cert-manager \
                    --from-literal=acmedns.json="{\"${GATEWAY_DOMAIN}\":{\"username\":\"${USERNAME}\",\"password\":\"${PASSWORD}\",\"fulldomain\":\"${API_KEY}\",\"subdomain\":\"${API_KEY}\",\"allowfrom\":[]}}" \
                    --dry-run=client -o yaml | kubectl apply -f -

                # Create ClusterIssuer for DNS01 with ACME-DNS
                echo "Creating ClusterIssuer for DNS01 with ACME-DNS..."
                cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ${ACME_EMAIL}
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
    - dns01:
        acmeDNS:
          host: https://${NAMESERVER}
          accountSecretRef:
            name: acmedns-credentials
            key: acmedns.json
      selector:
        dnsZones:
        - ${GATEWAY_DOMAIN}
EOF
                ;;
            rfc2136)
                echo "Setting up RFC2136 for DNS01 challenge..."

                # Create secret for TSIG key
                echo "Creating TSIG secret..."
                kubectl create secret generic rfc2136-credentials \
                    --namespace cert-manager \
                    --from-literal=tsig-secret="${TSIG_SECRET}" \
                    --dry-run=client -o yaml | kubectl apply -f -

                # Create ClusterIssuer for DNS01 with RFC2136
                echo "Creating ClusterIssuer for DNS01 with RFC2136..."
                cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ${ACME_EMAIL}
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
    - dns01:
        rfc2136:
          nameserver: ${NAMESERVER}
          tsigKeyName: ${TSIG_KEY_NAME}
          tsigAlgorithm: ${TSIG_ALGORITHM}
          tsigSecretSecretRef:
            name: rfc2136-credentials
            key: tsig-secret
      selector:
        dnsZones:
        - ${GATEWAY_DOMAIN}
EOF
                ;;
            *)
                echo "Error: Unsupported DNS plugin: ${DNS_PLUGIN}"
                exit 1
                ;;
        esac
    else
        # HTTP01 challenge (original behavior)
        echo "Setting up HTTP01 challenge..."
        cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ${ACME_EMAIL}
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
      - http01:
          gatewayHTTPRoute:
            parentRefs:
            - group: gateway.networking.k8s.io
              kind: Gateway
              name: flex-gateway
              namespace: envoy-gateway
EOF
    fi

    # Annotate the gateway with the cluster issuer
    kubectl -n envoy-gateway annotate --overwrite gateway/flex-gateway cert-manager.io/cluster-issuer=letsencrypt-prod
else
    echo "Skipping ACME configuration (no email provided)"
fi

# Process routes
sudo mkdir -p /etc/genestack/gateway-api/routes
for route in $(ls -1 /opt/genestack/etc/gateway-api/routes); do
    sed "s/your.domain.tld/${GATEWAY_DOMAIN}/g" "/opt/genestack/etc/gateway-api/routes/${route}" > "/tmp/${route}"
    sudo mv -v "/tmp/${route}" "/etc/genestack/gateway-api/routes/${route}"
done
kubectl apply -f /etc/genestack/gateway-api/routes

# Process listeners
sudo mkdir -p /etc/genestack/gateway-api/listeners
for listener in $(ls -1 /opt/genestack/etc/gateway-api/listeners); do
    sed "s/your.domain.tld/${GATEWAY_DOMAIN}/g" "/opt/genestack/etc/gateway-api/listeners/${listener}" > "/tmp/${listener}"
    sudo mv -v "/tmp/${listener}" "/etc/genestack/gateway-api/listeners/${listener}"
done
kubectl patch -n envoy-gateway gateway flex-gateway \
              --type='json' \
              --patch="$(jq -s 'flatten | .' /etc/genestack/gateway-api/listeners/*)"

echo "Setup Complete"

The setup script will ask the following questions:

  • Enter a valid email address for use with ACME, press enter to skip"
  • Enter the domain name for the gateway"

These values will be used to generate a certificate for the gateway and set the routes used within the flex-gateway, typically for OpenStack. This script can also be fully automated by providing the required values as arguments.

Run the Envoy Gateway setup Script with arguments

ACME_EMAIL="username@your.domain.tld" GATEWAY_DOMAIN="your.domain.tld" /opt/genestack/bin/setup-envoy-gateway.sh

Validation

At this stage, Envoy Gateway should be operational. To validate the configuration, run the following command.

kubectl -n openstack get httproute
kubectl -n envoy-gateway get gateways.gateway.networking.k8s.io flex-gateway

Troubleshooting

If you encounter any issues, check the logs of the envoy-gateway deployment.

kubectl logs -n envoyproxy-gateway-system deployment/envoy-gateway