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
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 usage
usage() {
    cat << EOF
Usage: $0 [OPTIONS]

OPTIONS:
    -e, --email EMAIL           Email address for ACME (optional)
    -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: godaddy (default: godaddy, only used with dns01)
    -k, --godaddy-key KEY       GoDaddy API Key (required for dns01 with godaddy)
    -s, --godaddy-secret SECRET GoDaddy API Secret (required for dns01 with godaddy)
    -h, --help                  Display this help message

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

    # Setup with DNS01 challenge using GoDaddy
    $0 --email user@example.com --domain example.com --challenge dns01 --godaddy-key KEY --godaddy-secret SECRET

    # Interactive mode (original behavior)
    $0
EOF
}

# Initialize variables
ACME_EMAIL=""
GATEWAY_DOMAIN=""
CHALLENGE_METHOD="http01"
DNS_PLUGIN="godaddy"
GODADDY_KEY=""
GODADDY_SECRET=""
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
            ;;
        -k|--godaddy-key)
            GODADDY_KEY="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        -s|--godaddy-secret)
            GODADDY_SECRET="$2"
            INTERACTIVE_MODE=false
            shift 2
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        *)
            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

# Interactive prompts (fallback for missing parameters)
if [[ "$INTERACTIVE_MODE" == "true" ]]; then
    if [ -z "${ACME_EMAIL}" ]; then
        read -rp "Enter a valid email address for use with ACME, press enter to skip: " 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

# Validate required parameters for DNS01 with GoDaddy
if [[ "$CHALLENGE_METHOD" == "dns01" && "$DNS_PLUGIN" == "godaddy" ]]; then
    if [[ -z "$GODADDY_KEY" || -z "$GODADDY_SECRET" ]]; then
        if [[ "$INTERACTIVE_MODE" == "true" ]]; then
            read -rp "Enter your GoDaddy API Key: " GODADDY_KEY
            read -rsp "Enter your GoDaddy API Secret: " GODADDY_SECRET
            echo
        else
            echo "Error: GoDaddy API Key and Secret are required for DNS01 challenge with GoDaddy plugin"
            echo "Use --godaddy-key and --godaddy-secret options"
            exit 1
        fi
    fi
fi

# Display configuration
echo "Configuration:"
echo "  Email: ${ACME_EMAIL:-"(not provided)"}"
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
        if [ "${DNS_PLUGIN}" = "godaddy" ]; then
            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="${GODADDY_KEY}:${GODADDY_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
        else
            echo "Error: Unsupported DNS plugin: ${DNS_PLUGIN}"
            exit 1
        fi
    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
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}"
    sed -i 's/namespace: nginx-gateway/namespace: envoy-gateway/g' "/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