When you need fine-grained access control for services in your mesh, Open Policy Agent (OPA) lets you define custom Rego policies that evaluate each request. By default, Service Mesh (ASM) deploys OPA as a sidecar inside each application pod, but you can also run OPA as a standalone service (centralized mode) to reduce resource overhead and avoid pod restarts. This guide walks through deploying a centralized OPA engine and connecting it to an ASM security policy.
Sidecar mode vs. centralized mode
In sidecar mode, ASM injects an OPA container into each application pod. The Istio proxy communicates with OPA within the same pod, keeping latency low. This makes sidecar mode a good fit for latency-sensitive services. However, it has trade-offs:
Higher resource usage: Every pod runs its own OPA container.
Pod restarts required: Injecting OPA requires restarting the application pod.
Less flexible routing: All requests to the pod are subject to OPA evaluation.
Centralized mode addresses these limitations by deploying OPA as a standalone service:
Lower resource footprint: A single OPA instance serves multiple workloads.
No pod restarts: Deploy or update OPA without disrupting application pods.
Selective enforcement: Apply OPA policies to specific request paths only.

Prerequisites
Before you begin, make sure that you have:
An ASM instance (v1.15.3.25 or later) with a managed Kubernetes cluster added. For more information, see Add a cluster to an ASM instance and Update an ASM instance
The httpbin application deployed and accessible. For more information, see Deploy the httpbin application
Automatic sidecar proxy injection enabled for the
defaultnamespace. For more information, see Enable automatic sidecar proxy injection
Step 1: Deploy OPA
Create a file named
asm-opa.yamlwith the following content. This manifest creates three resources:NoteThe username is extracted from the
Authorizationheader in the formatAuthorization: Basic <Base64-encoded username:password>.NoteReplace the region ID
cn-hangzhouin the image path with the region where your cluster is deployed.Resource Description Service Exposes OPA on port 9191 (gRPC) and port 8181 (HTTP). Deployment Runs the OPA container with the Envoy external authorization gRPC plugin enabled. Decision logging is turned on by default ( --set=decision_logs.console=true) to help with debugging.Secret Stores a Rego policy that allows requests matching any of these conditions: the path is health, the HTTP method isHEAD, or the username isalice.Connect to your Container Service for Kubernetes (ACK) cluster and deploy OPA:
kubectl apply -f asm-opa.yaml
Step 2: Associate an ASM security policy with OPA
Log on to the ASM console. In the left-side navigation pane, choose Service Mesh > Mesh Management.
On the Mesh Management page, click the name of your ASM instance. In the left-side navigation pane, choose Mesh Security Center > ASMSecurityPolicy.
Click Create. In the Create ASMSecurityPolicy dialog box, select the Custom Authorization Service card and click OK.
Configure the custom authorization service: After the security policy is created, the console displays the policy details:
On the CreateCustom Authorization Service page, enter the OPA service details from Step 1, then click Next.

In the Workload and Match Rules step, click Add Workload Group. In the New Workload Group dialog box, set a Workload Group Name and click Add Workload.
In the Add Workload dialog box, select Workload Scope, set Namespace to default and Workload Type to Service. Select httpbin in the Select workloads section, click the
icon, and click OK. 
In the Match Rule List section of the New Workload Group dialog box, set Match Mode to The selected request must be authenticated and Matching Rules to Custom Matching Rules. Turn on the Path switch, enter
/status/*, and click OK.
In the Workload and Match Rules step, click submit.

Step 3: Verify access control
Replace <IP-address-of-ASM-gateway> in the following commands with the actual IP address of your ASM ingress gateway.
Send a request to the root path
/(no authentication required): Expected output: The200 OKstatus confirms that the root path does not require authentication because the OPA policy match rule targets only/status/*.curl <IP-address-of-ASM-gateway>/ -I -X GETHTTP/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: 2Send a request to
/status/201without credentials: Expected output: The403 Forbiddenstatus confirms that unauthenticated requests to/status/*are rejected.curl <IP-address-of-ASM-gateway>/status/201 -I -X GETHTTP/1.1 403 Forbidden date: Tue, 25 Jul 2023 08:31:18 GMT server: istio-envoy content-length: 0 x-envoy-upstream-service-time: 1Send a request to
/status/201with valid credentials: Expected output: The201 Createdstatus confirms that the request from useraliceis allowed by the OPA policy.curl <IP-address-of-ASM-gateway>/status/201 -I -X GET --user alice:testpasswordHTTP/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
Step 4: Update the OPA policy at runtime
Use the OPA HTTP API to update policies without redeploying. The following example changes the allowed user from alice to bob.
Push the updated policy through the OPA REST API:
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), ":") }'Verify that user
bobis now allowed: Expected output:curl <IP-address-of-ASM-gateway>/status/201 -I -X GET --user bob:testpasswordHTTP/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: 3Verify that user
aliceis now denied: Expected output: The403 Forbiddenstatus confirms that useraliceis no longer authorized after the policy update.curl <IP-address-of-ASM-gateway>/status/201 -I -X GET --user alice:testpasswordHTTP/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