Understanding TargetRef policies

What is a policy?

A policy is a set of configuration that will be used to generate the proxy configuration. Kuma combines policies with dataplane configuration to generate the Envoy configuration of a proxy.

What do targetRef policies look like?

There are two parts to a policy:

  1. The metadata
  2. The spec

Metadata

Metadata identifies the policies by its name, type and what mesh it’s part of:

  1. type: ExamplePolicy
  2. name: my-policy-name
  3. mesh: default
  4. spec: ... # spec data specific to the policy kind

In Kubernetes all our policies are implemented as custom resource definitions (CRD) in the group kuma.io/v1alpha1.

  1. apiVersion: kuma.io/v1alpha1
  2. kind: ExamplePolicy
  3. metadata:
  4. name: my-policy-name
  5. namespace: kuma-system
  6. spec: ... # spec data specific to the policy kind

By default the policy is created in the default mesh. You can specify the mesh by using the kuma.io/mesh label.

For example:

  1. apiVersion: kuma.io/v1alpha1
  2. kind: ExamplePolicy
  3. metadata:
  4. name: my-policy-name
  5. namespace: kuma-system
  6. labels:
  7. kuma.io/mesh: "my-mesh"
  8. spec: ... # spec data specific to the policy kind

Policies are namespaced scope and currently the namespace must be the one the control-plane is running in kuma-system by default.

Spec

The spec field contains the actual configuration of the policy.

All specs have a top level targetRef which identifies which proxies this policy applies to. In particular, it defines which proxies have their Envoy configuration modified.

Some policies also support further narrowing.

The spec.to[].targetRef field defines rules that applies to outgoing traffic of proxies selected by spec.targetRef. The spec.from[].targetRef field defines rules that applies to incoming traffic of proxies selected by spec.targetRef.

The actual configuration is defined in a default map.

For example:

  1. type: ExamplePolicy
  2. name: my-example
  3. mesh: default
  4. spec:
  5. targetRef:
  6. kind: Mesh
  7. to:
  8. - targetRef:
  9. kind: Mesh
  10. default: # Configuration that applies to outgoing traffic
  11. key: value
  12. from:
  13. - targetRef:
  14. kind: Mesh
  15. default: # Configuration that applies to incoming traffic
  16. key: value

Some policies are not directional and will not have to and from. For example

  1. type: NonDirectionalPolicy
  2. name: my-example
  3. mesh: default
  4. spec:
  5. targetRef:
  6. kind: Mesh
  7. default:
  8. key: value

One of the benefits of targetRef policies is that the spec is always the same between Kubernetes and Universal.

This means that converting policies between Universal and Kubernetes only means rewriting the metadata.

Writing a targetRef

targetRef is a concept borrowed from Kubernetes Gateway API its usage is fully defined in MADR 005. Its goal is to select subsets of proxies with maximum flexibility.

It looks like:

  1. targetRef:
  2. kind: Mesh | MeshSubset | MeshService | MeshServiceSubset | MeshGateway
  3. name: "my-name" # For kinds MeshService, MeshServiceSubset and MeshGateway a name has to be defined
  4. tags:
  5. key: value # For kinds MeshServiceSubset, MeshSubset and MeshGateway a list of matching tags can be used
  6. proxyTypes: ["Sidecar", "Gateway"] # For kinds Mesh and MeshSubset a list of matching Dataplanes types can be used

Here’s an explanation of each kinds and their scope:

  • Mesh: applies to all proxies running in the mesh
  • MeshSubset: same as Mesh but filters only proxies who have matching targetRef.tags
  • MeshService: all proxies with a tag kuma.io/service equal to targetRef.name
  • MeshServiceSubset: same as MeshService but further refine to proxies that have matching targetRef.tags
  • MeshGateway: targets proxies matched by the named MeshGateway
    • Note that it’s very strongly recommended to target MeshGateway proxies using this kind, as opposed to MeshService/MeshServiceSubset.

In Kuma 2.6.x, the targetRef field gained the ability to select a specific subset of data plane proxies. To further refine policy enforcement, a new field named proxyTypes has been introduced. It allows you to target policies to specific types of data plane proxies:

  • Sidecar: Targets data plane proxies acting as sidecars to applications.
  • Gateway: Applies to data plane proxies operating in Gateway mode.
  • Empty list: Defaults to targeting all data plane proxies.

Consider the example below:

  1. apiVersion: kuma.io/v1alpha1
  2. kind: MeshAccessLog
  3. metadata:
  4. name: example
  5. namespace: kuma-system
  6. labels:
  7. kuma.io/mesh: default
  8. spec:
  9. targetRef: # top level targetRef
  10. kind: MeshService
  11. name: web-frontend
  12. to:
  13. - targetRef: # to level targetRef
  14. kind: MeshService
  15. name: web-backend
  16. default:
  17. backends:
  18. - file:
  19. format:
  20. plain: '{"start_time": "%START_TIME%"}'
  21. path: "/tmp/logs.txt"
  22. from:
  23. - targetRef: # from level targetRef
  24. kind: Mesh
  25. default:
  26. backends:
  27. - file:
  28. format:
  29. plain: '{"start_time": "%START_TIME%"}'
  30. path: "/tmp/logs.txt"

Using spec.targetRef, this policy targets all proxies that implement the service web-frontend. It defines the scope of this policy as applying to traffic either from or to web-frontend services.

The spec.to.targetRef section enables logging for any traffic going to web-backend. The spec.from.targetRef section enables logging for any traffic coming from any service in the Mesh.

Target resources

Not every policy supports to and from levels. Additionally, not every resource can appear at every supported level. The specified top level resource can also affect which resources can appear in to or from.

To help users, each policy documentation includes tables indicating which targetRef kinds is supported at each level. For each type of proxy, sidecar or builtin gateway, the table indicates for each targetRef level, which kinds are supported.

Example tables

These are just examples, remember to check the docs specific to your policy!

targetRefAllowed kinds
targetRef.kindMesh, MeshSubset, MeshService, MeshServiceSubset
to[].targetRef.kindMesh, MeshService
from[].targetRef.kindMesh
targetRefAllowed kinds
targetRef.kindMesh, MeshGateway, MeshGateway with tags
to[].targetRef.kindMesh

Sidecar

We see that we can select sidecar proxies via any of the kinds that select sidecars and we can set both to and from.

We can apply policy to:

  • all traffic originating at the sidecar to anywhere (to[].targetRef.kind: Mesh)
  • traffic to a specific kuma.io/service (to[].targetRef.kind: MeshService)

We can also apply policy to:

  • traffic terminating at the sidecar from anywhere in the mesh (from[].targetRef.kind: Mesh)

Builtin gateways

We see that we can select gateway proxies via any of the kinds that select gateways as well as specific gateway listeners and we can set only to.

We can only apply policy to:

  • all traffic originating at the gateway to anywhere (to[].targetRef.kind: Mesh)

Merging configuration

It is necessary to define a policy for merging configuration, because a proxy can be targeted by multiple targetRef’s.

We define a total order of policy priority:

  • MeshServiceSubset > MeshService > MeshSubset > Mesh (the more a targetRef is focused the higher priority it has)
  • If levels are equal the lexicographic order of policy names is used

For to and from policies we concatenate the array for each matching policies. We then build configuration by merging each level using JSON patch merge.

For example if I have 2 default ordered this way:

  1. default:
  2. conf: 1
  3. sub:
  4. array: [1, 2, 3]
  5. other: 50
  6. other-array: [3, 4, 5]
  7. ---
  8. default:
  9. sub:
  10. array: []
  11. other: null
  12. other-array: [5, 6]
  13. extra: 2

The merge result is:

  1. default:
  2. conf: 1
  3. sub:
  4. array: []
  5. other-array: [5, 6]
  6. extra: 2

Examples

Applying a global default

  1. type: ExamplePolicy
  2. name: example
  3. mesh: default
  4. spec:
  5. targetRef:
  6. kind: Mesh
  7. to:
  8. - targetRef:
  9. kind: Mesh
  10. default:
  11. key: value

All traffic from any proxy (top level targetRef) going to any proxy (to targetRef) will have this policy applied with value key=value.

Recommending to users

  1. type: ExamplePolicy
  2. name: example
  3. mesh: default
  4. spec:
  5. targetRef:
  6. kind: Mesh
  7. to:
  8. - targetRef:
  9. kind: MeshService
  10. name: my-service
  11. default:
  12. key: value

All traffic from any proxy (top level targetRef) going to the service “my-service” (to targetRef) will have this policy applied with value key=value.

This is useful when a service owner wants to suggest a set of configurations to its clients.

Configuring all proxies of a team

  1. type: ExamplePolicy
  2. name: example
  3. mesh: default
  4. spec:
  5. targetRef:
  6. kind: MeshSubset
  7. tags:
  8. team: "my-team"
  9. from:
  10. - targetRef:
  11. kind: Mesh
  12. default:
  13. key: value

All traffic from any proxies (from targetRef) going to any proxy that has the tag team=my-team (top level targetRef) will have this policy applied with value key=value.

This is a useful way to define coarse grain rules for example.

Configuring all proxies in a zone

  1. type: ExamplePolicy
  2. name: example
  3. mesh: default
  4. spec:
  5. targetRef:
  6. kind: MeshSubset
  7. tags:
  8. kuma.io/zone: "east"
  9. default:
  10. key: value

All proxies in zone east (top level targetRef) will have this policy configured with key=value.

This can be very useful when observability stores are different for each zone for example.

Configuring all gateways in a Mesh

  1. type: ExamplePolicy
  2. name: example
  3. mesh: default
  4. spec:
  5. targetRef:
  6. kind: Mesh
  7. proxyTypes: ["Gateway"]
  8. default:
  9. key: value

All gateway proxies in mesh default will have this policy configured with key=value.

This can be very useful when timeout configurations for gateways need to differ from those of other proxies.