Skip to main content

Envoy/Istio Integration

FieldValue
Document IDASCEND-SDK-005
Version2026.04
Last UpdatedApril 2026
AuthorAscend Engineering Team
PublisherOW-KAI Technologies Inc.
ClassificationEnterprise Client Documentation
ComplianceSOC 2 CC6.1/CC6.2, PCI-DSS 7.1/8.3, HIPAA 164.312, NIST 800-53 AC-2/SI-4

Reading Time: 15 minutes | Skill Level: Advanced

Overview

The ASCEND Envoy ext_authz service provides AI governance for Envoy proxy and Istio service mesh deployments. All service-to-service traffic can be governed without code changes.

Fail-Secure Behavior

When ASCEND is unreachable, the ext_authz filter denies all requests by default. Ensure ASCEND availability and configure appropriate timeouts before enabling governance in production.

Architecture

┌─────────────────────────────────────────────────────────────────────┐
│ ENVOY/ISTIO ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ AI │ │ Envoy │ │ Backend │ │
│ │ Agent │───────▶│ Proxy │───────▶│ Service │ │
│ │ │ │ (Sidecar) │ │ │ │
│ └─────────┘ └──────┬──────┘ └─────────────┘ │
│ │ │
│ │ gRPC ext_authz │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ ASCEND │ │
│ │ ext_authz │ │
│ │ Service │ │
│ │ │ │
│ │ Go gRPC │ │
│ │ Server │ │
│ └──────┬──────┘ │
│ │ │
│ │ HTTP/HTTPS │
│ ▼ │
│ ┌─────────────┐ │
│ │ ASCEND │ │
│ │ Platform │ │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘

Prerequisites

  • Kubernetes 1.21+
  • Envoy 1.20+ or Istio 1.12+
  • ASCEND API key

Deployment

Kubernetes Deployment

# Source: ascend-envoy-authz/deploy/kubernetes/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ascend-authz
namespace: ascend-system
spec:
replicas: 3
selector:
matchLabels:
app: ascend-authz
template:
metadata:
labels:
app: ascend-authz
spec:
containers:
- name: ascend-authz
image: owkai/ascend-envoy-authz:latest
ports:
- containerPort: 9001
name: grpc
- containerPort: 8080
name: health
env:
- name: ASCEND_API_KEY
valueFrom:
secretKeyRef:
name: ascend-secrets
key: api-key
- name: ASCEND_API_URL
value: "https://pilot.owkai.app"
- name: ENVIRONMENT
value: "production"
- name: FAIL_OPEN
value: "false"
- name: CACHE_TTL
value: "60s"
- name: LOG_LEVEL
value: "info"
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: health
initialDelaySeconds: 5
readinessProbe:
httpGet:
path: /ready
port: health
initialDelaySeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: ascend-authz
namespace: ascend-system
spec:
selector:
app: ascend-authz
ports:
- port: 9001
name: grpc
targetPort: grpc
- port: 8080
name: health
targetPort: health

Secrets

apiVersion: v1
kind: Secret
metadata:
name: ascend-secrets
namespace: ascend-system
type: Opaque
stringData:
api-key: "owkai_your_key_here"

Helm Chart

# Install from ECR Public OCI Registry
helm install ascend-authz oci://public.ecr.aws/w2q8a6d2/ascend-envoy-authz \
--version 1.0.1 \
--namespace istio-system \
--set ascend.apiUrl=https://pilot.owkai.app \
--set ascend.environment=production

Package Info: ascend-envoy-authz on ECR Public | Version: 1.0.1

Envoy Configuration

ext_authz Filter

# envoy.yaml
static_resources:
listeners:
- name: listener_0
address:
socket_address:
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
http_filters:
# ASCEND ext_authz filter
- name: envoy.filters.http.ext_authz
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
grpc_service:
envoy_grpc:
cluster_name: ascend-authz
timeout: 5s
transport_api_version: V3
failure_mode_allow: false
with_request_body:
max_request_bytes: 8192
allow_partial_message: true
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

clusters:
- name: ascend-authz
type: STRICT_DNS
lb_policy: ROUND_ROBIN
typed_extension_protocol_options:
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
"@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
explicit_http_config:
http2_protocol_options: {}
load_assignment:
cluster_name: ascend-authz
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: ascend-authz.ascend-system.svc.cluster.local
port_value: 9001

Istio Configuration

AuthorizationPolicy

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: ascend-ext-authz
namespace: istio-system
spec:
selector:
matchLabels:
app: istio-ingressgateway
action: CUSTOM
provider:
name: ascend-authz
rules:
- to:
- operation:
notPaths:
- "/health"
- "/metrics"

MeshConfig Extension Provider

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
meshConfig:
extensionProviders:
- name: ascend-authz
envoyExtAuthzGrpc:
service: ascend-authz.ascend-system.svc.cluster.local
port: 9001
timeout: 5s
failOpen: false

Configuration Options

OptionDefaultDescription
ASCEND_API_KEYRequiredASCEND API key
ASCEND_API_URLhttps://pilot.owkai.appAPI endpoint
ENVIRONMENTproductionEnvironment name
FAIL_OPENfalseAllow on error
CACHE_TTL60sCache duration
AGENT_ID_HEADERx-ascend-agent-idHeader for agent ID
DEFAULT_AGENT_ID-Default agent if missing
REQUIRE_AGENT_IDtrueRequire agent header
LOG_LEVELinfoLogging level
LOG_DECISIONStrueLog all decisions
BLOCK_ON_PENDINGtrueBlock pending approvals
EXCLUDED_PATHS/health,/metricsPaths to skip
DATA_SENSITIVITYinternalDefault data class

Handler Implementation

// Source: ascend-envoy-authz/internal/authz/handler.go:70
func (h *Handler) Check(ctx context.Context, req *authv3.CheckRequest) (*authv3.CheckResponse, error) {
// Extract HTTP request attributes
httpReq := req.GetAttributes().GetRequest().GetHttp()

// Check path exclusions
if mapper.IsPathExcluded(path, h.config.ExcludedPaths) {
return h.allowResponse(nil), nil
}

// Extract agent ID from headers
agentID := h.extractAgentID(httpReq.GetHeaders())
if agentID == "" && h.config.RequireAgentID {
return h.denyResponse("Missing agent ID", codes.Unauthenticated, nil), nil
}

// Map request to ASCEND action
action := mapper.MapCheckRequest(req, agentID, h.config.Environment)

// Check cache
if cached := h.getFromCache(cacheKey); cached != nil {
return h.buildResponse(cached), nil
}

// Call ASCEND API
decision, err := h.client.EvaluateAction(ctx, action)
if err != nil {
// FAIL SECURE
if h.config.FailOpen {
return h.allowResponse(map[string]string{"x-ascend-status": "error-allowed"}), nil
}
return h.denyResponse("Governance unavailable", codes.Unavailable, nil), nil
}

// Cache approved decisions
if decision.IsApproved() {
h.setCache(cacheKey, decision)
}

return h.buildResponse(decision), nil
}

Response Headers

The service adds these headers to allowed requests:

// Source: ascend-envoy-authz/internal/authz/handler.go:197
func (h *Handler) allowResponse(headers map[string]string) *authv3.CheckResponse {
headers["x-ascend-status"] = "approved"
headers["x-ascend-action-id"] = fmt.Sprintf("%d", decision.ActionID)
headers["x-ascend-risk-score"] = fmt.Sprintf("%.1f", decision.RiskScore)
headers["x-ascend-risk-level"] = decision.RiskLevel
// ...
}

Fail Secure Design

// Source: ascend-envoy-authz/internal/authz/handler.go:130
// FAIL SECURE: Deny on error unless fail_open is configured
if h.config.FailOpen {
h.logger.Warn("Fail open enabled, allowing request despite error")
return h.allowResponse(map[string]string{
"x-ascend-status": "error-allowed",
"x-ascend-error": err.Error(),
}), nil
}

return h.denyResponse(
"Governance service unavailable",
codes.Unavailable,
map[string]string{"x-ascend-error": "service_unavailable"},
), nil

Caching

The service maintains an in-memory cache for approved decisions:

// Source: ascend-envoy-authz/internal/authz/handler.go:296
type cacheEntry struct {
decision *ascend.Decision
expiresAt time.Time
}

func (h *Handler) getFromCache(key string) *ascend.Decision {
h.cacheMu.RLock()
defer h.cacheMu.RUnlock()

entry, ok := h.cache[key]
if !ok || time.Now().After(entry.expiresAt) {
return nil
}
return entry.decision
}

Health Checks

// Health endpoint: GET /health
// Ready endpoint: GET /ready

Health check integration:

livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5

Monitoring

Prometheus Metrics

The service exposes Prometheus metrics:

MetricTypeDescription
ascend_authz_requests_totalCounterTotal requests
ascend_authz_decisionsCounterDecisions by status
ascend_authz_latency_secondsHistogramRequest latency
ascend_authz_cache_hitsCounterCache hit count
ascend_authz_api_errorsCounterAPI errors

ServiceMonitor

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: ascend-authz
spec:
selector:
matchLabels:
app: ascend-authz
endpoints:
- port: health
path: /metrics

Logging

Structured JSON logging:

{
"level": "info",
"component": "authz_handler",
"agent_id": "my-agent-001",
"action_type": "api.read",
"status": "approved",
"risk_score": 3.5,
"risk_level": "low",
"action_id": 12345,
"latency_ms": 45,
"cached": false
}

Testing

Local Testing

# Build and run
go build -o ascend-authz ./cmd/server
./ascend-authz

# Test with grpcurl
grpcurl -plaintext \
-d '{"attributes":{"request":{"http":{"method":"GET","path":"/api/data","headers":{"x-ascend-agent-id":"test"}}}}}' \
localhost:9001 envoy.service.auth.v3.Authorization/Check

Integration Testing

# Deploy to Kubernetes
kubectl apply -f deploy/kubernetes/

# Test through Envoy
curl -H "X-Ascend-Agent-ID: test-agent" \
http://your-envoy-endpoint/api/data

Troubleshooting

ext_authz filter not intercepting requests

Cause: The EnvoyFilter resource is not applied to the correct namespace or workload selector. Istio sidecar injection may not be enabled. Solution: Verify the EnvoyFilter is in the correct namespace: kubectl get envoyfilter -A. Confirm Istio sidecar injection is enabled: kubectl get namespace -L istio-injection. Check the workload selector matches your deployment labels.

gRPC connection refused to ext_authz service

Cause: The ASCEND ext_authz service is not running or the Kubernetes Service is misconfigured. The default port is 9001. Solution: Check the service is running: kubectl get pods -l app=ascend-authz. Verify the Service port mapping: kubectl get svc ascend-authz -o yaml. Check logs: kubectl logs -l app=ascend-authz.

Helm chart deployment fails with image pull error

Cause: The ECR public registry requires authentication, or your cluster cannot access public.ecr.aws. Solution: For EKS clusters, ECR public access should work by default. For other clusters, pull the image manually: docker pull public.ecr.aws/w2q8a6d2/ascend-envoy-authz:latest and push to your private registry. Update the Helm values to use your private registry.

Best Practices

  1. Use 3+ replicas for high availability
  2. Set resource limits to prevent noisy neighbors
  3. Enable caching for production workloads
  4. Monitor error rates with Prometheus
  5. Use PodDisruptionBudget for updates

Next Steps


Document Version: 2026.04 | Last Updated: April 2026