Overview
Implement a production-grade API Gateway using Envoy Proxy and gRPC-Gateway to provide unified REST/gRPC access, authentication, rate limiting, and observability for the cloud IDE platform.
🎯 Architecture Goals
- Unified API: Single entry point for REST and gRPC clients
- Authentication: JWT validation and user context injection
- Rate Limiting: Per-user and global rate limiting
- Observability: Request tracing, metrics, and logging
- Security: TLS termination and request validation
🏗️ Gateway Architecture
Internet → Envoy Proxy → gRPC-Gateway → Go gRPC Services
↓
Auth, Rate Limiting,
Metrics, Logging
📋 Implementation Tasks
Envoy Proxy Configuration
gRPC-Gateway Setup
Configuration Files
Envoy Configuration
# gateway/envoy.yaml
static_resources:
listeners:
- name: listener_0
address:
socket_address:
protocol: TCP
address: 0.0.0.0
port_value: 8080
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
access_log:
- name: envoy.access_loggers.stdout
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
log_format:
json_format:
timestamp: "%START_TIME%"
method: "%REQ(:METHOD)%"
path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"
status: "%RESPONSE_CODE%"
duration: "%DURATION%"
user_id: "%REQ(X-USER-ID)%"
request_id: "%REQ(X-REQUEST-ID)%"
http_filters:
# Authentication filter
- name: envoy.filters.http.lua
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
inline_code: |
function envoy_on_request(request_handle)
local auth_header = request_handle:headers():get("authorization")
if auth_header == nil then
request_handle:respond({":status"}, "401", {["content-type"] = "application/json"}, '{"error":"missing authorization header"}')
return
end
-- Extract JWT token and validate
local token = string.gsub(auth_header, "Bearer ", "")
-- Add JWT validation logic here
-- Set user context for downstream services
request_handle:headers():add("x-user-id", user_id)
request_handle:headers():add("x-user-email", user_email)
end
# Rate limiting filter
- name: envoy.filters.http.local_ratelimit
typed_config:
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
type_url: type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
value:
stat_prefix: http_local_rate_limiter
token_bucket:
max_tokens: 100
tokens_per_fill: 100
fill_interval: 60s
filter_enabled:
runtime_key: test_enabled
default_value:
numerator: 100
denominator: HUNDRED
filter_enforced:
runtime_key: test_enforced
default_value:
numerator: 100
denominator: HUNDRED
# Router filter
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
# Health check endpoint
- match:
path: "/health"
direct_response:
status: 200
body:
inline_string: "OK"
# Swagger UI
- match:
prefix: "/docs"
route:
cluster: swagger_ui
# API v1 routes to gRPC-Gateway
- match:
prefix: "/api/v1"
route:
cluster: grpc_gateway
timeout: 30s
# gRPC routes (direct)
- match:
prefix: "/"
headers:
- name: content-type
string_match:
prefix: "application/grpc"
route:
cluster: grpc_backend
timeout: 30s
clusters:
- name: grpc_gateway
connect_timeout: 5s
type: LOGICAL_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: grpc_gateway
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: grpc-gateway
port_value: 8081
- name: grpc_backend
connect_timeout: 5s
type: LOGICAL_DNS
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
load_assignment:
cluster_name: grpc_backend
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: grpc-server
port_value: 8080
- name: swagger_ui
connect_timeout: 5s
type: LOGICAL_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: swagger_ui
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: swagger-ui
port_value: 8080
admin:
address:
socket_address:
protocol: TCP
address: 0.0.0.0
port_value: 9901
gRPC-Gateway Implementation
// gateway/grpc-gateway/main.go
package main
import (
"context"
"flag"
"fmt"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
environmentv1 "github.com/VAIBHAVSING/Dev8.dev/api/grpc/go/environment/v1"
azurev1 "github.com/VAIBHAVSING/Dev8.dev/api/grpc/go/azure/v1"
)
func main() {
var (
grpcServerEndpoint = flag.String("grpc-server-endpoint", "localhost:8080", "gRPC server endpoint")
httpPort = flag.String("http-port", "8081", "HTTP port for REST API")
)
flag.Parse()
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// Register gRPC server endpoint
mux := runtime.NewServeMux(
runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{
MarshalOptions: protojson.MarshalOptions{
UseProtoNames: true,
EmitUnpopulated: true,
},
UnmarshalOptions: protojson.UnmarshalOptions{
DiscardUnknown: true,
},
}),
runtime.WithErrorHandler(customErrorHandler),
runtime.WithMetadata(annotator),
)
opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
// Register Environment Service
err := environmentv1.RegisterEnvironmentServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)
if err != nil {
panic(err)
}
// Register Azure Service
err = azurev1.RegisterAzureProvisionerServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)
if err != nil {
panic(err)
}
// CORS middleware
corsHandler := func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
h.ServeHTTP(w, r)
})
}
// Start HTTP server
fmt.Printf("Starting gRPC-Gateway on port %s\n", *httpPort)
if err := http.ListenAndServe(":"+*httpPort, corsHandler(mux)); err != nil {
panic(err)
}
}
func customErrorHandler(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, r *http.Request, err error) {
// Custom error response formatting
w.Header().Set("Content-Type", "application/json")
status := runtime.HTTPStatusFromCode(grpc.Code(err))
w.WriteHeader(status)
errorResponse := map[string]interface{}{
"error": err.Error(),
"code": grpc.Code(err).String(),
}
json.NewEncoder(w).Encode(errorResponse)
}
func annotator(ctx context.Context, req *http.Request) metadata.MD {
// Extract headers and add to gRPC metadata
md := metadata.New(nil)
if userID := req.Header.Get("X-User-ID"); userID != "" {
md.Append("user-id", userID)
}
if requestID := req.Header.Get("X-Request-ID"); requestID != "" {
md.Append("request-id", requestID)
}
return md
}
Docker Compose Setup
# gateway/docker-compose.yaml
version: '3.8'
services:
envoy:
image: envoyproxy/envoy:v1.28.0
ports:
- "8080:8080"
- "9901:9901"
volumes:
- ./envoy.yaml:/etc/envoy/envoy.yaml:ro
depends_on:
- grpc-gateway
- grpc-server
grpc-gateway:
build: ./grpc-gateway
ports:
- "8081:8081"
environment:
- GRPC_SERVER_ENDPOINT=grpc-server:8080
depends_on:
- grpc-server
grpc-server:
build: ../../apps/agent
ports:
- "8082:8080"
environment:
- DATABASE_URL=postgres://user:pass@postgres:5432/dev8
- AZURE_SUBSCRIPTION_ID=
- AZURE_CLIENT_ID=
- AZURE_CLIENT_SECRET=
- AZURE_TENANT_ID=
swagger-ui:
image: swaggerapi/swagger-ui:latest
ports:
- "8083:8080"
environment:
- SWAGGER_JSON=/openapi/environments.yaml
volumes:
- ../openapi:/openapi:ro
redis:
image: redis:7-alpine
ports:
- "6379:6379"
command: redis-server --appendonly yes
Authentication Integration
// gateway/auth/jwt.go
package auth
import (
"crypto/rsa"
"fmt"
"github.com/golang-jwt/jwt/v5"
)
type JWTValidator struct {
publicKey *rsa.PublicKey
}
func NewJWTValidator(publicKeyPath string) (*JWTValidator, error) {
// Load NextAuth.js public key for JWT validation
keyData, err := os.ReadFile(publicKeyPath)
if err != nil {
return nil, err
}
publicKey, err := jwt.ParseRSAPublicKeyFromPEM(keyData)
if err != nil {
return nil, err
}
return &JWTValidator{publicKey: publicKey}, nil
}
func (v *JWTValidator) ValidateToken(tokenString string) (*jwt.Token, error) {
return jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return v.publicKey, nil
})
}
🔧 Monitoring & Observability
Prometheus Metrics
Distributed Tracing
Logging
🔗 Integration Points
⏱️ Time Estimate
6-8 hours - Gateway setup with authentication and observability
🎯 Success Criteria
🚀 Priority
HIGH - Essential for API access and security
📝 Notes
- Use Envoy's built-in filters for maximum performance
- Implement proper error handling and user feedback
- Configure rate limiting to prevent abuse
- Add comprehensive monitoring for production readiness
- Design for horizontal scaling with load balancing
Overview
Implement a production-grade API Gateway using Envoy Proxy and gRPC-Gateway to provide unified REST/gRPC access, authentication, rate limiting, and observability for the cloud IDE platform.
🎯 Architecture Goals
🏗️ Gateway Architecture
📋 Implementation Tasks
Envoy Proxy Configuration
Core Envoy Setup (
gateway/envoy.yaml)Authentication Filter (
gateway/filters/auth.lua)Rate Limiting Configuration
Observability Filters
gRPC-Gateway Setup
Gateway Server (
gateway/grpc-gateway/main.go)OpenAPI Serving (
gateway/swagger/)Configuration Files
Envoy Configuration
gRPC-Gateway Implementation
Docker Compose Setup
Authentication Integration
🔧 Monitoring & Observability
Prometheus Metrics
Distributed Tracing
Logging
🔗 Integration Points
⏱️ Time Estimate
6-8 hours - Gateway setup with authentication and observability
🎯 Success Criteria
🚀 Priority
HIGH - Essential for API access and security
📝 Notes