Related Documentation
Minimum Version
Kong Mesh - 2.6

MeshOPA policy plugin

Kong Mesh integrates the Open Policy Agent (OPA) to provide access control for your services.

The agent is included in the data plane proxy sidecar, instead of the more common deployment as a separate sidecar.

When the MeshOPA policy is applied, the control plane configures the following:

  • The embedded policy agent, with the specified policy
  • Envoy, to use External Authorization that points to the embedded policy agent

TargetRef support matrix

Configuration

To apply a policy with MeshOPA, you must do the following:

  • Specify the group of data plane proxies to apply the policy to with the targetRef property.
  • Provide a policy with the appendPolicies property. Policies are defined in the Rego language.
  • Optionally provide custom configuration for the policy agent.

Inline

With secrets

Encoding the policy in a Secret provides some security for policies that contain sensitive data.

Configuration

Kong Mesh defines a default configuration for OPA, but you can adjust the configuration to meet your environment’s requirements.

The following environment variables are available:

Variable Type What it configures Default value {:width=25%:}
KMESH_OPA_ADDR string Address OPA API server listens on localhost:8181
KMESH_OPA_CONFIG_PATH string Path to file of initial config N/A
KMESH_OPA_DIAGNOSTIC_ADDR string Address of OPA diagnostics server 0.0.0.0:8282
KMESH_OPA_ENABLED bool Whether kuma-dp starts embedded OPA true
KMESH_OPA_EXT_AUTHZ_ADDR string Address of Envoy External AuthZ service localhost:9191
KMESH_OPA_CONFIG_OVERRIDES strings Overrides for OPA configuration, in addition to config file(*) nil

Configuring the authorization filter

You can configure the external authorization filter by adjusting the authConfig section.

By default, the body will not be sent to the agent. To send it, set authConfig.requestBody.maxSize to the maximum size of your body. If the request body is larger than this parameter, it will be truncated and the header x-envoy-auth-partial-body will be set to true.

Support for external API management servers

The agentConfig field lets you define a custom configuration that points to an external management server:

Composing policies

In your organization, the mesh operator may want to set a policy for subset of proxies in the mesh. At the same time, service owners may want to exercise additional policies.

For example, the mesh operator may want to enable JWT token validation for all proxies in the mesh

apiVersion: kuma.io/v1alpha1
kind: MeshOPA
metadata:
  name: mopa-mesh-operator
  namespace: kong-mesh-system
  labels:
    kuma.io/mesh: default
spec:
  targetRef:
    kind: Mesh
  default:
    appendPolicies:
      - rego:
          inlineString: |
            package operator
            
            import input.attributes.request.http as http_request
            
            default allow = false
            
            token = {"valid": valid, "payload": payload} {
                [_, encoded] := split(http_request.headers.authorization, " ")
                [valid, _, payload] := io.jwt.decode_verify(encoded, {"secret": "secret"})
            }
            
            allow {
                is_token_valid
                action_allowed
            }
            
            is_token_valid {
              token.valid
              now := time.now_ns() / 1000000000
              token.payload.nbf <= now
              now < token.payload.exp
            }
            
            action_allowed {
              http_request.method == "GET"
              token.payload.role == "admin"
            }

Service owner wants to block all requests on path /blocked:

apiVersion: kuma.io/v1alpha1
kind: MeshOPA
metadata:
  name: mopa-service-owner
  namespace: kong-mesh-system
  labels:
    kuma.io/mesh: default
spec:
  targetRef:
    kind: MeshService
    name: test-server_kuma-demo_svc_80
  default:
    appendPolicies:
      - rego:
          inlineString: |
            package serviceowner
            
            default allow = true
            
            deny {
              input.parsed_path == ["blocked"]
            }

appendPolicies is a list you can append, therefore in the case of the data plane proxy test-server_kuma-demo_svc_80 service, both policies will be applied.

Kong Mesh will autogenerate an additional OPA decision policy:

package implicitkmesh
import data.operator
import data.serviceowner

allow {
  data.operator.allow
  not data.operator.deny
  data.serviceowner.allow
  not data.serviceowner.deny
}

It also configures the OPA agent decision path (plugins.envoy_ext_authz_grpc.path) to implicitkmesh/allow.

You can also add a rego policy which is not part of the decision. Set a appendPolicies[*].ignoreDecision to true so the rego policy won’t be added to autogenerated decision policy. This way, the mesh operator can expose utility functions to service owner.

Example

The following example shows how to deploy and test a sample MeshOPA policy on Kubernetes, using the kuma-demo application.

  1. Deploy the example application:

    kubectl apply -f https://bit.ly/demokuma
    
  2. Make a request from the frontend to the backend:

    kubectl exec -i -t $(kubectl get pod -l "app=kuma-demo-frontend" -o jsonpath='{.items[0].metadata.name}' -n kuma-demo) -n kuma-demo -c kuma-fe -- curl backend:3001 -v
    

    The output looks like:

    Defaulting container name to kuma-fe.
    Use 'kubectl describe pod/kuma-demo-app-6787b4f7f5-m428c -n kuma-demo' to see all of the containers in this pod.
    *   Trying 10.111.108.218:3001...
    * TCP_NODELAY set
    * Connected to backend (10.111.108.218) port 3001 (#0)
    > GET / HTTP/1.1
    > Host: backend:3001
    > User-Agent: curl/7.67.0
    > Accept: */*
    >
    * Mark bundle as not supporting multiuse
    < HTTP/1.1 200 OK
    < x-powered-by: Express
    < cache-control: no-store, no-cache, must-revalidate, private
    < access-control-allow-origin: *
    < access-control-allow-methods: PUT, GET, POST, DELETE, OPTIONS
    < access-control-allow-headers: *
    < host: backend:3001
    < user-agent: curl/7.67.0
    < accept: */*
    < x-forwarded-proto: http
    < x-request-id: 1717af9c-2587-43b9-897f-f8061bba5ad4
    < content-length: 90
    < content-type: text/html; charset=utf-8
    < date: Tue, 16 Mar 2021 15:33:18 GMT
    < x-envoy-upstream-service-time: 1521
    < server: envoy
    <
    * Connection #0 to host backend left intact
    Hello World! Marketplace with sales and reviews made with <3 by the OCTO team at Kong Inc.
    
  3. Apply a MeshOPA policy that requires a valid JWT token:

    echo "
    apiVersion: kuma.io/v1alpha1
    kind: MeshOPA
    metadata:
      namespace: kong-mesh-system
      name: mopa-1
      labels:
        kuma.io/mesh: default
    spec:
      targetRef:
        kind: Mesh
      default:
        appendPolicies:
          - rego:
              inlineString: |
                package envoy.authz
    
                import input.attributes.request.http as http_request
    
                default allow = false
    
                token = {\"valid\": valid, \"payload\": payload} {
                    [_, encoded] := split(http_request.headers.authorization, \" \")
                    [valid, _, payload] := io.jwt.decode_verify(encoded, {\"secret\": \"secret\"})
                }
    
                allow {
                    is_token_valid
                    action_allowed
                }
    
                is_token_valid {
                  token.valid
                  now := time.now_ns() / 1000000000
                  token.payload.nbf <= now
                  now < token.payload.exp
                }
    
                action_allowed {
                  http_request.method == \"GET\"
                  token.payload.role == \"admin\"
                }
    " | kubectl apply -f -
    
  4. Make an invalid request from the frontend to the backend:

    kubectl exec -i -t $(kubectl get pod -l "app=kuma-demo-frontend" -o jsonpath='{.items[0].metadata.name}' -n kuma-demo) -n kuma-demo -c kuma-fe -- curl backend:3001 -v
    

    The output looks like:

    Defaulting container name to kuma-fe.
    Use 'kubectl describe pod/kuma-demo-app-6787b4f7f5-bwvnb -n kuma-demo' to see all of the containers in this pod.
    *   Trying 10.105.146.164:3001...
    * TCP_NODELAY set
    * Connected to backend (10.105.146.164) port 3001 (#0)
    > GET / HTTP/1.1
    > Host: backend:3001
    > User-Agent: curl/7.67.0
    > Accept: */*
    >
    * Mark bundle as not supporting multiuse
    < HTTP/1.1 403 Forbidden
    < date: Tue, 09 Mar 2021 16:50:40 GMT
    < server: envoy
    < x-envoy-upstream-service-time: 2
    < content-length: 0
    <
    * Connection #0 to host backend left intact
    

    Note the HTTP/1.1 403 Forbidden message. The application doesn’t allow a request without a valid token.

    The policy can take up to 30 seconds to propagate, so if this request succeeds the first time, wait and then try again.

  5. Make a valid request from the frontend to the backend.

    Export the token into an environment variable:

    export ADMIN_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJzdWIiOiJZbTlpIiwibmJmIjoxNTE0ODUxMTM5LCJleHAiOjI1MjQ2MDgwMDB9.H0-42LYzoWyQ_4MXAcED30u6lA5JE087eECV2nxDfXo"
    

    Make the request:

    kubectl exec -i -t $(kubectl get pod -l "app=kuma-demo-frontend" -o jsonpath='{.items[0].metadata.name}' -n kuma-demo) -n kuma-demo -c kuma-fe -- curl -H "Authorization: Bearer $ADMIN_TOKEN" backend:3001
    

    The output looks like:

    Defaulting container name to kuma-fe.
    Use 'kubectl describe pod/kuma-demo-app-6787b4f7f5-m428c -n kuma-demo' to see all of the containers in this pod.
    *   Trying 10.111.108.218:3001...
    * TCP_NODELAY set
    * Connected to backend (10.111.108.218) port 3001 (#0)
    > GET / HTTP/1.1
    > Host: backend:3001
    > User-Agent: curl/7.67.0
    > Accept: */*
    >
    * Mark bundle as not supporting multiuse
    < HTTP/1.1 200 OK
    < x-powered-by: Express
    < cache-control: no-store, no-cache, must-revalidate, private
    < access-control-allow-origin: *
    < access-control-allow-methods: PUT, GET, POST, DELETE, OPTIONS
    < access-control-allow-headers: *
    < host: backend:3001
    < user-agent: curl/7.67.0
    < accept: */*
    < x-forwarded-proto: http
    < x-request-id: 8fd7b398-1ba2-4c2e-b229-5159d04d782e
    < content-length: 90
    < content-type: text/html; charset=utf-8
    < date: Tue, 16 Mar 2021 17:26:00 GMT
    < x-envoy-upstream-service-time: 261
    < server: envoy
    <
    * Connection #0 to host backend left intact
    Hello World! Marketplace with sales and reviews made with <3 by the OCTO team at Kong Inc.
    

    The request is valid again because the token is signed with the secret private key, its payload includes the admin role, and it is not expired.

Something wrong?

Help us make these docs great!

Kong Developer docs are open source. If you find these useful and want to make them better, contribute today!