When dynamic access control is required, you can integrate an Open Policy Agent (OPA) engine in an ingress gateway to customize authorization policies based on user identities or request content and control communication between services in real time. This effectively prevents unauthorized access, reduces the risks of data breach, and enhances the security of applications in a Service Mesh (ASM) instance. This topic describes how to use an OPA engine to authenticate and authorize requests that are received by an ingress gateway. In this example, requests flow through the ingress gateway to access the HTTPBin application.
Prerequisites
-
A managed Kubernetes cluster is added to an ASM instance that runs version 1.15.3.25 or later. For instructions, see Add a cluster to an ASM instance and Upgrade an ASM instance.
-
The httpbin application deployed and accessible through an ASM ingress gateway. The test commands in this tutorial send requests to
${ASM_GATEWAY_IP}, which is the IP address of that gateway. For instructions, see Deploy the httpbin application. -
Automatic sidecar injection enabled for the
defaultnamespace. For instructions, see Configure a sidecar injection policy.
Step 1: Deploy OPA
Deploy a centralized OPA authorization service in the cluster that you added to your ASM instance.
-
Create the
asm-opa.yamlfile with the following content. The YAML file deploys an OPA Service, an OPA Deployment, and a Secret.Replace
cn-hangzhouin the imageregistry-vpc.cn-hangzhou.aliyuncs.com/acs/opa:0.46.1-istio-3-staticwith the region of your cluster before you deploy the file.The OPA engine in the Deployment enables logging by default (
--set=decision_logs.console=true) to simplify debugging.The Secret defines the OPA policy, which allows a request in any of the following cases:
-
The request path is
health. -
The request method is
HEAD. -
The username is
alice.NoteThe username comes from the
Authorizationheader of the request, in the formatAuthorization: Basic ${Base64 encoding of the username:password string}.
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), ":") }NoteThe
resources.limitsvalues in this example are placeholders. Setcpuandmemoryto values that suit your environment. -
-
Use the kubeconfig file of the Container Service for Kubernetes (ACK) cluster to run the following command to deploy OPA.
kubectl apply -f asm-opa.yaml -
Confirm that the OPA authorization service is available before you continue. The
opaDeployment must be ready, and theasm-opaService must expose port9191for gRPC and port8181for HTTP.
Step 2: Use the external authorization feature of the ingress gateway to integrate the OPA engine with the ingress gateway
-
Log on to the ASM console. In the left-side navigation pane, choose .
-
On the Mesh Management page, click the name of the ASM instance. In the left-side navigation pane, choose .
On the Ingress Gateway page, find the gateway that you want to integrate with OPA and click Gateway Security.
In the left-side navigation pane of the gateway, choose .
Configure a custom authorization service.
In the Custom Authorization Service Configuration wizard, configure OPA as the custom authorization service for the gateway, and then click Next.
Turn on the Enable Gateway Custom Authorization Service switch, select the Custom authorization service based on envoy.ext_authz (HTTP or gRPC) tab, set Protocol to GRPC, service address to
asm-opa.default.svc.cluster.local, service port to 9191, and timeout to 10 seconds.In the Matching Rules wizard, configure matching rules to specify which requests require OPA authorization, and then click Submit.
Set Matching Mode to Selected requests must be authorized, select Custom matching rules, turn on HTTP Path (Path), and enter
/status/*.A message appears, confirming that the Gateway Custom Authorization Service was created successfully and listing the created Istio native resources: an ASMExtensionProvider (resource name:
grpcextauth-asmsecuritypolicy-ingressgateway-extauthz) and an AuthorizationPolicy (resource name:ingressgateway-extauthz-ap-wg-gateway-istio-system-gateway-ingressgateway). You can click View YAML to view the resource configuration or click Edit to modify the authorization service.
Step 3: Test httpbin access
In the following commands, ${ASM_GATEWAY_IP} is the IP address of the ASM ingress gateway that exposes httpbin. Each command uses -I to return only the response headers and -X GET to force the GET method, because the OPA policy allows every HEAD request. These tests exercise the username rule of the OPA policy.
-
Run the following command to access the
/path.curl ${ASM_GATEWAY_IP}/ -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: 2200 OKresponse shows that the request is allowed. The/path does not match the/status/*matching rule that you configured in Step 2, so the request is not sent to OPA for authorization. -
Run the following command to access the
/status/201path without valid credentials.curl ${ASM_GATEWAY_IP}/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: 1403 Forbiddenresponse indicates that the request is denied because it carries no valid credentials. -
Run the following command to access the
/status/201path with valid credentials.curl ${ASM_GATEWAY_IP}/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: 3201 Createdresponse confirms that the request is allowed because it carries valid credentials for thealiceuser. If this request also returns403 Forbidden, review the OPA deployment in Step 1 and the ASM security policy configuration in Step 2.
Step 4: Update the OPA policy and retest access
Call the HTTP API of the OPA engine to update the OPA policy at runtime. The opa-policy Secret that you created in Step 1 is not modified and still contains the original policy.
-
Run the following command to update the policy so that only the
bobuser is allowed to access the application and the previousaliceuser is denied.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), ":") }' -
Run the following command to access httpbin as the
bobuser.curl ${ASM_GATEWAY_IP}/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: 3201 Createdresponse shows that thebobuser accesses the application successfully under the updated policy. -
Run the following command to access httpbin as the
aliceuser.curl ${ASM_GATEWAY_IP}/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: 1403 Forbiddenresponse shows that access from thealiceuser is denied.