All Products
Search
Document Center

Alibaba Cloud Service Mesh:Integrate an OPA engine with an ingress gateway

Last Updated:Mar 11, 2026

When an ingress gateway receives external traffic, fine-grained access control often requires more than simple allow/deny rules. Open Policy Agent (OPA) lets you write custom authorization policies in Rego and enforce them at the gateway level -- without modifying application code.

This topic covers deploying an OPA engine alongside a Service Mesh (ASM) ingress gateway, connecting them through Envoy's external authorization mechanism, and testing the setup with the HTTPBin application.

How it works

The ASM ingress gateway uses Envoy as its data plane proxy. Envoy supports an external authorization filter (ext_authz) that delegates authorization decisions to an external service.

When OPA is integrated as the external authorization service:

  1. A request arrives at the ingress gateway.

  2. Envoy sends the request metadata (method, path, headers) to the OPA engine over gRPC.

  3. OPA evaluates the request against the loaded Rego policy.

  4. OPA returns an allow or deny decision. Envoy either forwards the request upstream or returns 403 Forbidden.

Because OPA runs as a separate pod within the cluster, policy evaluation is local -- no external network hop is required. Policies can be updated at runtime through the OPA HTTP API without redeploying any workloads.

Prerequisites

Before you begin, make sure that you have:

Step 1: Deploy an OPA engine

Create a file named asm-opa.yaml with the following content. This manifest defines three Kubernetes resources:

ResourcePurpose
ServiceExposes the OPA engine on port 9191 (gRPC) and port 8181 (HTTP)
DeploymentRuns the OPA container with the Envoy external authorization gRPC plugin enabled
SecretStores the default Rego policy that OPA loads at startup
Note

Replace the region ID cn-hangzhou in the container image path with the region where your cluster is deployed. For example, if your cluster is in the China (Shanghai) region, use registry-vpc.cn-shanghai.aliyuncs.com/acs/opa:0.46.1-istio-3-static.

Show the content of asm-opa.yaml

apiVersion: v1
kind: Service
metadata:
  name: asm-opa
  labels:
    app: opa
spec:
  ports:
    - name: grpc
      port: 9191
      targetPort: 9191
      protocol: TCP
    - name: http
      port: 8181
      targetPort: 8181
      protocol: TCP
  selector:
    app: opa
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: opa
  labels:
    app: opa
spec:
  replicas: 1
  selector:
    matchLabels:
      app: opa
  template:
    metadata:
      labels:
        app: opa
      annotations:
        sidecar.istio.io/inject: "false"
    spec:
      containers:
        - name: opa
          image: registry-vpc.cn-hangzhou.aliyuncs.com/acs/opa:0.46.1-istio-3-static
          securityContext:
            runAsUser: 1111
          volumeMounts:
            - readOnly: true
              mountPath: /policy
              name: opa-policy
          args:
            - "run"
            - "--server"
            - "--addr=0.0.0.0:8181"
            - "--diagnostic-addr=0.0.0.0:8282"
            - "--set=plugins.envoy_ext_authz_grpc.addr=:9191"
            - "--set=plugins.envoy_ext_authz_grpc.path=asm/authz/allow"
            - "--set=decision_logs.console=true"
            - "--ignore=.*"
            - "/policy/policy.rego"
          ports:
            - containerPort: 9191
              protocol: TCP
          resources:
            limits:
              cpu: "0"
              memory: "0"
      volumes:
        - name: opa-policy
          secret:
            secretName: opa-policy
---
apiVersion: v1
kind: Secret
metadata:
  name: opa-policy
type: Opaque
stringData:
  policy.rego: |
    package asm.authz

    import future.keywords

    import input.attributes.request.http as http_request
    import input.parsed_path

    default allow := false

    allow if {
      parsed_path[0] == "health"
    }

    allow if {
      http_request.method == "HEAD"
    }

    allow if {
      user_name == "alice"
    }

    user_name := parsed if {
      [_, encoded] := split(http_request.headers.authorization, " ")
      [parsed, _] := split(base64url.decode(encoded), ":")
    }

Key configuration fields

The Deployment args configure how the OPA engine operates:

FieldValueDescription
--addr0.0.0.0:8181HTTP API listening address for policy management and health checks.
--diagnostic-addr0.0.0.0:8282Diagnostic endpoint for readiness and liveness probes.
plugins.envoy_ext_authz_grpc.addr:9191gRPC listening address for the Envoy external authorization plugin. Must match the Service port.
plugins.envoy_ext_authz_grpc.pathasm/authz/allowThe Rego policy decision path. OPA evaluates data.asm.authz.allow for each request.
decision_logs.consoletruePrints decision logs to stdout for debugging.

Default policy rules

The Rego policy in the Secret defines three authorization rules. A request is allowed if any rule evaluates to true:

RuleConditionExample
Health check bypassRequest path starts with healthGET /health
HEAD method bypassHTTP method is HEADHEAD /any-path
User authorizationUsername in the Authorization header is aliceAuthorization: Basic YWxpY2U6dGVzdHBhc3N3b3Jk (alice:testpassword, Base64-encoded)

All other requests are denied (default allow := false).

Deploy

Run the following command to deploy OPA to the Container Service for Kubernetes (ACK) cluster:

kubectl apply -f asm-opa.yaml

Step 2: Configure the ingress gateway to use OPA

Connect the OPA engine to the ingress gateway as a custom authorization service through the ASM console.

  1. Log on to the ASM console. In the left-side navigation pane, choose Service Mesh > Mesh Management.

  2. On the Mesh Management page, click the name of the target ASM instance. In the left-side navigation pane, choose ASM Gateways > Ingress Gateway.

  3. On the Ingress Gateway page, click Gateway security next to the ingress gateway that you want to configure.

  4. In the left-side navigation pane, choose Gateway Security > Custom Authorization Service.

  5. In the Custom Authorization Service Configuration step, configure the OPA engine as the custom authorization service for the ingress gateway, and then click Next.

    Custom Authorization Service Configuration

  6. In the Matching Rules step, specify which requests require OPA authorization, and then click Submit.

    Matching Rules

After the configuration is saved, the custom authorization service appears on the page:

Custom Authorization Service Created

Step 3: Verify OPA authorization

Test the three authorization rules by sending requests to the HTTPBin application through the ingress gateway.

Replace <gateway-ip> in the following commands with the IP address of your ASM ingress gateway.

Test 1: Access a path not covered by matching rules

Send a GET request to the root path (/). If the matching rules configured in Step 2 do not cover this path, the request bypasses OPA and reaches the upstream service directly.

curl <gateway-ip>/ -I -X GET

Expected output:

HTTP/1.1 200 OK
server: istio-envoy
date: Tue, 25 Jul 2023 08:30:58 GMT
content-type: text/html; charset=utf-8
content-length: 9593
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 2

A 200 OK response confirms that requests to this path are not intercepted by OPA.

Test 2: Access a protected path without credentials

Send a GET request to /status/201 without an Authorization header:

curl <gateway-ip>/status/201 -I -X GET

Expected output:

HTTP/1.1 403 Forbidden
date: Tue, 25 Jul 2023 08:31:18 GMT
server: istio-envoy
content-length: 0
x-envoy-upstream-service-time: 1

A 403 Forbidden response confirms that OPA denies requests that do not match any allow rule.

Test 3: Access a protected path with valid credentials

Send a GET request to /status/201 with the authorized username alice:

curl <gateway-ip>/status/201 -I -X GET --user alice:testpassword

Expected output:

HTTP/1.1 201 Created
server: istio-envoy
date: Tue, 25 Jul 2023 08:31:38 GMT
content-type: text/html; charset=utf-8
access-control-allow-origin: *
access-control-allow-credentials: true
content-length: 0
x-envoy-upstream-service-time: 3

A 201 Created response confirms that OPA authorizes requests from the user alice.

Step 4: Update the OPA policy at runtime

OPA exposes an HTTP API for policy management. Use this API to update policies without redeploying the OPA pod.

The following example changes the authorized user from alice to bob:

kubectl exec deployment/httpbin -c istio-proxy -- curl asm-opa:8181/v1/policies/policy/policy.rego -XPUT --data-binary 'package asm.authz

import future.keywords
import input.attributes.request.http as http_request
import input.parsed_path

default allow := false

allow if {
  parsed_path[0] == "health"
}

allow if {
  http_request.method == "HEAD"
}

allow if {
  user_name == "bob"
}

user_name := parsed if {
  [_, encoded] := split(http_request.headers.authorization, " ")
  [parsed, _] := split(base64url.decode(encoded), ":")
}'
Note

This command runs curl from the istio-proxy container of the httpbin pod because the OPA service (asm-opa:8181) is accessible only within the cluster. The HTTP PUT method replaces the existing policy at the specified path.

Verify the updated policy

  1. Send a request as bob. Expect a 201 Created response: Expected output:

       curl <gateway-ip>/status/201 -I -X GET --user bob:testpassword
       HTTP/1.1 201 Created
       server: istio-envoy
       date: Tue, 25 Jul 2023 08:32:16 GMT
       content-type: text/html; charset=utf-8
       access-control-allow-origin: *
       access-control-allow-credentials: true
       content-length: 0
       x-envoy-upstream-service-time: 3
  2. Send a request as alice. Expect a 403 Forbidden response: Expected output:

       curl <gateway-ip>/status/201 -I -X GET --user alice:testpassword
       HTTP/1.1 403 Forbidden
       date: Tue, 25 Jul 2023 08:32:49 GMT
       server: istio-envoy
       content-length: 0
       x-envoy-upstream-service-time: 1

These results confirm that the updated policy takes effect immediately: bob is authorized while alice is now denied.