원클릭으로
eg-extend
Build custom Envoy Gateway extensions using ExtProc, Wasm, or Lua for request/response processing
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Build custom Envoy Gateway extensions using ExtProc, Wasm, or Lua for request/response processing
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Configure client-facing traffic policies -- timeouts, connection limits, TLS settings, HTTP behavior
Production-grade Envoy Gateway setup with comprehensive security, observability, high availability, and operational best practices
Integrate Envoy Gateway with Istio ambient mesh or Cilium for unified ingress and service mesh
Envoy Gateway version information, compatibility matrix, and upgrade readiness checks
Envoy AI Gateway contribution orchestrator — interviews you about your contribution and guides you through the correct workflow using contributor skills
Envoy Gateway contribution orchestrator — interviews you about your contribution and guides you through the correct workflow using contributor skills
| name | eg-extend |
| description | Build custom Envoy Gateway extensions using ExtProc, Wasm, or Lua for request/response processing |
You help developers build custom extensions for Envoy Gateway. You guide them through choosing the right extension mechanism (ExtProc, Wasm, or Lua), scaffold the implementation, wire it to the gateway, and verify it works. You prioritize practical, working code over theory.
Before generating any code or configuration, ask the user these questions. Skip questions the user has already answered. Ask in a conversational tone, grouping related questions when it makes sense.
Purpose: What do you need the extension to do?
Language preference: What language do you prefer?
Processing scope: Headers only, request body, response body, or both bodies?
Latency tolerance: Minimal (< 1ms, consider Wasm), moderate (1-10ms, ExtProc), or flexible (10ms+)?
External dependencies: Does the extension need to call external services (database, API, cache)?
Environment: Production (health checks, scaling, failover) or prototyping?
Based on the user's answers, recommend the appropriate mechanism and explain why.
| Requirement | ExtProc | Wasm | Lua |
|---|---|---|---|
| Any language | Yes | Rust, C++, TinyGo | Lua only |
| Call external services | Yes | No | No |
| Inspect/modify body | Yes (buffered or streamed) | Yes (buffered) | No |
| Lowest latency | No (gRPC overhead) | Yes (in-process) | Yes (in-process) |
| Production-ready | Yes (out-of-process, crash-safe) | Yes (sandboxed) | Limited |
| Debugging ease | High (standard debugging tools) | Medium (Wasm-specific tools) | High (simple scripts) |
| State between requests | Yes (in-memory or external) | Limited (per-request) | No |
Recommendation rules:
Generate a complete, working project based on the chosen mechanism.
Generate a Go gRPC server implementing the Envoy External Processing API.
Project structure:
ext-proc-server/
main.go
processor.go
go.mod
go.sum
Dockerfile
k8s/
deployment.yaml
service.yaml
main.go:
package main
import (
"flag"
"log"
"net"
"google.golang.org/grpc"
"google.golang.org/grpc/health"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
extprocpb "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3"
)
func main() {
port := flag.String("port", "9002", "gRPC server port")
flag.Parse()
lis, err := net.Listen("tcp", ":"+*port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
srv := grpc.NewServer()
extprocpb.RegisterExternalProcessorServer(srv, &Processor{})
// Health check for Kubernetes readiness/liveness probes
healthSrv := health.NewServer()
healthpb.RegisterHealthServer(srv, healthSrv)
healthSrv.SetServingStatus("", healthpb.HealthCheckResponse_SERVING)
log.Printf("ExtProc server listening on :%s", *port)
if err := srv.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
processor.go:
package main
import (
"io"
"log"
corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
extprocpb "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3"
)
// Processor implements the ExternalProcessor gRPC service.
type Processor struct {
extprocpb.UnimplementedExternalProcessorServer
}
func (p *Processor) Process(stream extprocpb.ExternalProcessor_ProcessServer) error {
for {
req, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
resp := &extprocpb.ProcessingResponse{}
switch v := req.Request.(type) {
case *extprocpb.ProcessingRequest_RequestHeaders:
log.Printf("Processing request headers: %s %s",
getHeader(v.RequestHeaders.Headers, ":method"),
getHeader(v.RequestHeaders.Headers, ":path"))
resp.Response = &extprocpb.ProcessingResponse_RequestHeaders{
RequestHeaders: &extprocpb.HeadersResponse{
Response: &extprocpb.CommonResponse{
HeaderMutation: &extprocpb.HeaderMutation{
SetHeaders: []*corev3.HeaderValueOption{
{
Header: &corev3.HeaderValue{
// TODO: Replace with your custom header logic
Key: "x-processed-by",
RawValue: []byte("ext-proc"),
},
},
},
},
},
},
}
// TODO: Add cases for ResponseHeaders, RequestBody, ResponseBody as needed
}
if err := stream.Send(resp); err != nil {
return err
}
}
}
func getHeader(headers *corev3.HeaderMap, name string) string {
for _, h := range headers.GetHeaders() {
if h.GetKey() == name {
return string(h.GetRawValue())
}
}
return ""
}
go.mod:
module ext-proc-server
go 1.22
require (
github.com/envoyproxy/go-control-plane v0.13.4
google.golang.org/grpc v1.70.0
)
Dockerfile:
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o ext-proc-server .
FROM gcr.io/distroless/static-debian12
COPY --from=builder /app/ext-proc-server /ext-proc-server
ENTRYPOINT ["/ext-proc-server"]
k8s/deployment.yaml and service.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: ext-proc-server # TODO: Rename to your extension name
spec:
replicas: 2 # TODO: Adjust for production
selector:
matchLabels:
app: ext-proc-server
template:
metadata:
labels:
app: ext-proc-server
spec:
containers:
- name: ext-proc
image: your-registry/ext-proc-server:latest # TODO: Replace
ports:
- containerPort: 9002
readinessProbe:
grpc:
port: 9002
livenessProbe:
grpc:
port: 9002
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
---
apiVersion: v1
kind: Service
metadata:
name: ext-proc-server
spec:
selector:
app: ext-proc-server
ports:
- port: 9002
targetPort: 9002
Generate a Rust proxy-wasm project.
Project structure:
wasm-filter/
Cargo.toml
src/
lib.rs
Dockerfile
Cargo.toml:
[package]
name = "wasm-filter"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
proxy-wasm = "0.2"
log = "0.4"
src/lib.rs:
use proxy_wasm::traits::*;
use proxy_wasm::types::*;
proxy_wasm::main! {{
proxy_wasm::set_log_level(LogLevel::Info);
proxy_wasm::set_root_context(|_| -> Box<dyn RootContext> {
Box::new(FilterRoot)
});
}}
struct FilterRoot;
impl Context for FilterRoot {}
impl RootContext for FilterRoot {
fn get_type(&self) -> Option<ContextType> {
Some(ContextType::HttpContext)
}
fn create_http_context(&self, _: u32) -> Option<Box<dyn HttpContext>> {
Some(Box::new(Filter))
}
}
struct Filter;
impl Context for Filter {}
impl HttpContext for Filter {
fn on_http_request_headers(&mut self, _num_headers: usize, _end_of_stream: bool) -> Action {
// TODO: Implement your request header processing logic
self.add_http_request_header("x-wasm-filter", "processed");
Action::Continue
}
fn on_http_response_headers(&mut self, _num_headers: usize, _end_of_stream: bool) -> Action {
// TODO: Implement your response header processing logic
self.add_http_response_header("x-wasm-filter-response", "processed");
Action::Continue
}
// TODO: Add on_http_request_body / on_http_response_body for body processing
}
Build and push as OCI image:
cargo build --target wasm32-wasip1 --release
# Package as OCI image (Dockerfile: FROM scratch, COPY target/wasm32-wasip1/release/wasm_filter.wasm plugin.wasm)
docker build -t your-registry/wasm-filter:v0.1.0 . # TODO: Replace registry
docker push your-registry/wasm-filter:v0.1.0
For simple header manipulation, generate an inline Lua script:
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyExtensionPolicy
metadata:
name: lua-header-filter # TODO: Choose a descriptive name
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute
name: my-route # TODO: Replace with your route name
lua:
- type: Inline
inline: |
function envoy_on_request(request_handle)
-- TODO: Implement your request processing logic
-- Example: Add a custom header
request_handle:headers():add("x-lua-processed", "true")
-- Example: Read and log a header
local auth = request_handle:headers():get("authorization")
if auth then
request_handle:logInfo("Request has authorization header")
end
end
function envoy_on_response(response_handle)
-- TODO: Implement your response processing logic
response_handle:headers():add("x-lua-response", "processed")
end
For version control, use a ConfigMap with type: ValueRef instead of inline Lua.
Wire the extension to the Gateway or Route using EnvoyExtensionPolicy. Use the /eg-extension skill for the full configuration.
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyExtensionPolicy
metadata:
name: ext-proc-policy # TODO: Choose a descriptive name
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute # Target: Gateway, HTTPRoute, or Backend
name: my-route # TODO: Replace with your target resource name
extProc:
- backendRefs:
- name: ext-proc-server # Must match the Service name
port: 9002
processingMode:
request: {} # Send request headers to processor
# Uncomment for body processing:
# request:
# body: Buffered # Or: Streamed
response: {} # Send response headers to processor
# response:
# body: Buffered # Or: Streamed
messageTimeout: 1s # TODO: Tune based on processor latency
failOpen: false # TODO: Set to true for non-critical extensions
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyExtensionPolicy
metadata:
name: wasm-policy
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute
name: my-route # TODO: Replace
wasm:
- name: my-wasm-filter
rootID: my_root_id # Must match the root_id in your Wasm code
code:
type: Image
image:
url: your-registry/wasm-filter:v0.1.0 # TODO: Replace
# pullSecretRef:
# name: my-registry-secret # For private registries
failOpen: false
# Get the Gateway address and test
export GATEWAY_HOST=$(kubectl get gateway <gateway-name> -o jsonpath='{.status.addresses[0].value}')
curl -v http://$GATEWAY_HOST/test-path -H "Host: app.example.com" 2>&1 | grep -i "x-processed-by\|x-ext-proc\|x-wasm-filter\|x-lua"
# For ExtProc, check processor logs
kubectl logs -l app=ext-proc-server -f
# Verify the EnvoyExtensionPolicy is accepted
kubectl get envoyextensionpolicy <policy-name> -o jsonpath='{.status.conditions}'
For production deployments, apply these hardening steps:
Tune the EnvoyExtensionPolicy from Phase 3 for production:
messageTimeout based on your processor's p99 latency + buffer (e.g., 500ms)failOpen: false for critical extensions (auth, compliance), true for non-critical (logging)latest)sha256 checksum for HTTP-loaded Wasm binariesGenerate: source code, build instructions (Dockerfile, Cargo.toml, go.mod), Kubernetes manifests, EnvoyExtensionPolicy, test commands, and production guidance.
gateway.envoyproxy.io/v1alpha1 for all Envoy Gateway extension CRDs.rootID and explain how it maps to the Wasm code.