Envoy Gateway

Prerequisites

  • Envoy Gateway v1.6.0 installed
    • DataDome Envoy Gateway integration uses EnvoyPatchPolicy that might evolve in the future

Important: Two Namespaces

This integration uses two different namespaces:

  1. Envoy Gateway Namespace (typically envoy-gateway-system):

    • Where Envoy Gateway control plane is installed. ConfigMap and EnvoyProxy resources go here.
  2. Gateway Namespace (e.g., production, default):

    • Where the Gateway resource lives. EnvoyPatchPolicy goes here.

Pre-flight Checks

Find your GatewayClass and Gateway names before starting:

# Find your GatewayClass name
kubectl get gatewayclass # Example output: NAME: eg, CONTROLLER: gateway.envoyproxy.io/gatewayclass-controller

# Find your Gateway name and namespace
kubectl get gateway -A # Example output: NAMESPACE: production, NAME: api-gateway, CLASS: eg

# Find listener name (usually 'http' or 'https')
kubectl get gateway <gateway-name> -n <gateway-namespace> -o yaml | grep -A 2 "listeners:" # Example output: - name: http

Set these as environment variables:

export GATEWAYCLASS_NAME="eg"        # From first command
export GATEWAY_NAME="api-gateway"    # From second command
export GATEWAY_NAMESPACE="production" # From second command

Step 1: Enable EnvoyPatchPolicy

Make sure you activate enableEnvoyPatchPolicy: true on the Envoy Gateway configuration. If you already have an envoy-gateway-config ConfigMap, merge this setting with your existing configuration.)

kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: envoy-gateway-config
  namespace: envoy-gateway-system
data:
  envoy-gateway.yaml: |
    apiVersion: gateway.envoyproxy.io/v1alpha1
    kind: EnvoyGateway
    extensionApis:
      enableEnvoyPatchPolicy: true
EOF

Restart:

kubectl rollout restart deployment/envoy-gateway -n envoy-gateway-system
kubectl rollout status deployment/envoy-gateway -n envoy-gateway-system

Verify:

kubectl get configmap envoy-gateway-config -n envoy-gateway-system

Step 2: Create ConfigMap to store DataDome code

Store the DataDome Lua filter code in Kubernetes.

kubectl create configmap datadome-lua \
  --from-file=datadome.lua=./datadome.lua \
  -n envoy-gateway-system \
  --dry-run=client -o yaml | kubectl apply -f -

Verify:

kubectl get configmap datadome-lua -n envoy-gateway-system

Step 3: Create EnvoyProxy Resource

Mount the ConfigMap into Envoy proxy pods at /etc/envoy-lua-filters/.

kubectl apply -f - <<EOF
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: datadome-proxy-config
  namespace: envoy-gateway-system
spec:
  provider:
    type: Kubernetes
    kubernetes:
      envoyDeployment:
        pod:
          volumes:
          - name: datadome-lua-filter
            configMap:
              name: datadome-lua
        container:
          volumeMounts:
          - name: datadome-lua-filter
            mountPath: /etc/envoy-lua-filters
            readOnly: true
EOF

Verify:

kubectl get envoyproxy datadome-proxy-config -n envoy-gateway-system

Step 4: Update GatewayClass

Link the GatewayClass to the EnvoyProxy resource to activate the mount.

kubectl patch gatewayclass ${GATEWAYCLASS_NAME} --type=merge -p '{
  "spec": {
    "parametersRef": {
      "group": "gateway.envoyproxy.io",
      "kind": "EnvoyProxy",
      "name": "datadome-proxy-config",
      "namespace": "envoy-gateway-system"
    }
  }
}'

Note: This triggers a rolling restart of Envoy proxy pods.

Verify:

kubectl get gatewayclass ${GATEWAYCLASS_NAME} -o yaml | grep -A 4 parametersRef

Step 5: Apply EnvoyPatchPolicy

Inject the Lua filter into the HTTP filter chain and add DataDome API cluster.

Set your API key and find listener name:

export DATADOME_API_KEY="your-api-key-here"

# Find your listener name (usually 'http' or 'https')
export LISTENER_NAME=$(kubectl get gateway ${GATEWAY_NAME} -n ${GATEWAY_NAMESPACE} -o jsonpath='{.spec.listeners[0].name}')
echo "Listener name: ${LISTENER_NAME}"

Apply the policy:

kubectl apply -f - <<EOF
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyPatchPolicy
metadata:
  name: datadome-filter-patch
  namespace: ${GATEWAY_NAMESPACE}
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: Gateway
    name: ${GATEWAY_NAME}
  type: JSONPatch
  jsonPatches:
  - type: "type.googleapis.com/envoy.config.listener.v3.Listener"
    name: "${GATEWAY_NAMESPACE}/${GATEWAY_NAME}/${LISTENER_NAME}"
    operation:
      op: add
      path: "/default_filter_chain/filters/0/typed_config/http_filters/0"
      value:
        name: envoy.filters.http.lua
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
          default_source_code:
            inline_string: |
              assert(loadfile("/etc/envoy-lua-filters/datadome.lua"))({
                ['API_KEY'] = '${DATADOME_API_KEY}',
                ['API_TIMEOUT'] = 150,
                ['DATADOME_ENDPOINT'] = 'api.datadome.co',
              })
  - type: "type.googleapis.com/envoy.config.cluster.v3.Cluster"
    name: "datadome"
    operation:
      op: add
      path: ""
      value:
        name: datadome
        type: STRICT_DNS
        load_assignment:
          cluster_name: datadome
          endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: api.datadome.co
                    port_value: 443
        transport_socket:
          name: envoy.transport_sockets.tls
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
            sni: api.datadome.co
EOF

Verify:

kubectl get envoypatchpolicy datadome-filter-patch -n ${GATEWAY_NAMESPACE}