Skip to content
You are viewing the documentation for Solo Enterprise for Istio, formerly known as Gloo Mesh (OSS APIs).

CEL and workload claims authorization for L4 traffic

EnterpriseAlpha
Page as Markdown

Use Common Expression Language (CEL) expressions in ztunnel authorization policies to enforce fine-grained L4 access control based on workload identity claims.

This feature requires your mesh to be installed with the Solo distribution of Istio and an Enterprise-level license for Solo Enterprise for Istio. Contact your account representative to obtain a valid license.

This feature is considered alpha in the Solo distribution of Istio 1.30. Alpha features are likely to change, are not fully tested, and are not supported for production. For more information, see Solo feature maturity.

About CEL and workload claims authorization

Solo Enterprise for Istio extends the Istio AuthorizationPolicy resource to support Common Expression Language (CEL) expressions in ztunnel, the ambient mesh Layer 4 (L4) proxy. CEL expressions give you access to workload identity claims embedded in mTLS certificates, going beyond the fixed set of attribute keys that standard when conditions support. Ztunnel enforces CEL-based authorization at L4 on every proxied connection. No waypoint is required. To enforce workload claims at Layer 7 using an agentgateway waypoint, see Workload identity tokens (WIMSE).

When you enable workload claims in ztunnel, istiod embeds pod-level identity claims directly in each workload’s mTLS certificate. Ztunnel reads those claims at connection setup time and exposes them in a source.claims map keyed by fully-qualified dotted names. You can then write authorization rules that reference workload identity attributes and custom security annotations.

Use cases

CEL expressions with workload claims can be useful in the following example cases:

  • Enforce traffic segmentation between security zones. For example, allow only workloads annotated as PCI-DSS to reach a payment service.
  • Write precise policies for workloads that share a Kubernetes service account, using workload-level claims to distinguish individual workload identities.
  • Enforce consistent access policies across hybrid deployments and AI agent architectures, where workloads may operate beyond Kubernetes cluster boundaries and a SPIFFE identity alone cannot provide sufficient or stable identity signals.

Available source.claims keys

The following keys are available in the source.claims map. The map is populated from identity claims that istiod embeds in each workload’s mTLS certificate when you enabled workload claims. All keys use fully-qualified, dotted names so that the same CEL expression is portable between ztunnel and an agentgateway waypoint.

KeyTypeDescription
source.claims['istio.io.trust_domain']stringWorkload trust domain.
source.claims['istio.io.workload.name']stringWorkload name.
source.claims['istio.io.workload.namespace']stringWorkload namespace.
source.claims['istio.io.workload.pod']stringWorkload pod name. Absent from the map if empty.
source.claims['solo.io.security-claims.<key>']stringUser-defined security claim sourced from a pod annotation prefixed solo.io.security-claims/. The forward slash in the annotation key is converted to a dot to form the map key. For example, the annotation solo.io.security-claims/zone: PCI-DSS is accessible as source.claims['solo.io.security-claims.zone'].

Before you begin

  1. Install an ambient mesh with the Solo distribution of Istio version 1.30 or later.

  2. Enroll workloads in the ambient mesh by labeling their namespaces.

    kubectl label namespace <namespace> istio.io/dataplane-mode=ambient

Enable workload claims

To use the source.claims map, enable workload claims in ztunnel.

Standard Istio mTLS certificates identify workloads using a SPIFFE URI. This identity is scoped to the service account level and to workloads that the mesh CA can issue certificates for. Two pods that share a service account are indistinguishable by SPIFFE identity alone, and workloads outside the mesh boundary, such as AI agents or services in hybrid deployments, cannot present a SPIFFE identity at all. When you enable workload claims, ztunnel requests per-workload certificates and istiod embeds pod-level identity claims in each certificate, including individual workload name, namespace, and pod name, as well as custom claims from pod annotations. Ztunnel reads these claims from the peer’s certificate at connection setup time and exposes all identity claims in a source.claims map keyed by fully-qualified dotted names, giving you a richer, more portable identity model for writing granular access policies.

  1. Get the current values for your ztunnel Helm release.

    helm get values ztunnel -n istio-system -o yaml > ztunnel.yaml
    open ztunnel.yaml
  2. Add the ENABLE_WORKLOAD_CLAIMS environment variable under the env key. If the env key does not exist, add it.

    env:
      ENABLE_WORKLOAD_CLAIMS: "true"
  3. Upgrade the ztunnel Helm release with the updated values.

    helm upgrade ztunnel oci://us-docker.pkg.dev/soloio-img/istio-helm/ztunnel \
      --version 1.30.3-solo \
      --namespace istio-system \
      -f ztunnel.yaml

Optional: Annotate workloads with custom security claims

The source.claims['istio.io.*'] entries are populated automatically from the workload’s SPIFFE identity. If your authorization policies reference source.claims['solo.io.security-claims.*'] keys, you must annotate workloads.

Istiod reads any pod annotation prefixed with solo.io.security-claims/ at certificate issuance time and includes it as a custom claim in the workload certificate. You define the claim keys by choosing annotation suffixes that match your organization’s security classification scheme. The following example uses zone and jurisdiction, but any suffix is valid.

apiVersion: v1
kind: Pod
metadata:
  name: payment-service
  namespace: shop
  annotations:
    solo.io.security-claims/zone: PCI-DSS       # accessible in CEL as source.claims['solo.io.security-claims.zone']
    solo.io.security-claims/jurisdiction: US    # accessible in CEL as source.claims['solo.io.security-claims.jurisdiction']
spec:
  ...
Annotation changes do not invalidate already-issued certificates. If you update a solo.io.security-claims/ annotation, the new claim value takes effect only when the workload’s certificate is next renewed by istiod.

Istiod enforces the following limits on security annotation content. Annotations that exceed these limits or contain non-printable characters are silently excluded from the workload certificate.

  • Annotation key suffix: maximum 255 characters
  • Annotation value: maximum 8192 characters

Write workload claims-based authorization policies

Use source.claims map keys as the when.key expression in your authorization policies. The values and notValues fields contain the strings to match against the claim’s value.

Missing key behavior

When a source workload has no solo.io.security-claims/ annotations, the source.claims map contains no solo.io.security-claims.* keys. Accessing a key that does not exist raises a CEL runtime error. Ztunnel treats CEL evaluation errors as fail-closed: for ALLOW policies the rule does not match, and for DENY policies the rule fires. Either way, an evaluation error cannot let traffic through, and traffic is denied regardless of the policy action.

For DENY policies with values, unannotated workloads are also denied. They do not match the intended condition, but the missing-key error triggers fail-closed, which fires the DENY. Apply DENY policies with claim conditions only when all source workloads in scope carry the annotation. When that is not the case, prefer an ALLOW policy that explicitly grants access to annotated workloads, so that unannotated workloads are denied by the absence of a matching ALLOW rule rather than by a CEL error.

Example: Allow traffic only from PCI-DSS workloads

The following policy allows requests to the payment-api workloads only from source workloads annotated with solo.io.security-claims/zone: PCI-DSS.

apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: allow-pci-zone-only
  namespace: shop
spec:
  selector:
    matchLabels:
      app: payment-api
  action: ALLOW
  rules:
  - when:
    - key: "source.claims['solo.io.security-claims.zone']"
      values:
      - "PCI-DSS"

Example: Allow traffic only from a specific workload namespace

The following policy allows traffic to workloads in the cart namespace only from source workloads whose embedded claims identify their namespace as shop. Use source.claims['istio.io.workload.namespace'] when multiple workloads share a Kubernetes service account and source.namespace alone is not specific enough to distinguish them.

apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: allow-shop-workloads
  namespace: cart
spec:
  action: ALLOW
  rules:
  - when:
    - key: "source.claims['istio.io.workload.namespace']"
      values:
      - "shop"

Example: Deny traffic based on a negated claim

The following policy denies traffic from source workloads that do not have the solo.io.security-claims/env: prod annotation, which populates source.claims['solo.io.security-claims.env'] with the value prod. Because unannotated workloads trigger fail-closed behavior on the DENY rule, this pattern works correctly only when every source workload in scope carries the solo.io.security-claims/env annotation.

apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: deny-non-prod
  namespace: shop
spec:
  action: DENY
  rules:
  - when:
    - key: "source.claims['solo.io.security-claims.env']"
      notValues:
      - "prod"

Considerations

Keep in mind the following considerations when deploying workload claims authorization policies in your environment.

Envoy-based proxies not supported: CEL workload claims authorization is enforced by ztunnel only. Sidecars, community waypoints (Envoy-based), and Envoy-based ingress/egress gateways do not support source.claims evaluation.

Multicluster deployments: Avoid IP-based when conditions (source.ip, destination.ip) in a multicluster ambient mesh. Source and destination IP addresses are not stable across cluster boundaries.

Workload claims require ztunnel restart: ENABLE_WORKLOAD_CLAIMS is a ztunnel configuration flag. If you set it outside of a Helm upgrade, such as by patching the environment variable directly, you must restart ztunnel manually for the change to take effect. A Helm upgrade handles the restart automatically.

kubectl rollout restart daemonset/ztunnel -n istio-system

Claims are per hop: Ztunnel evaluates claims against the immediate mTLS peer, not the original source workload. If a waypoint is in the traffic path, the destination ztunnel sees the waypoint’s claims, not the source workload’s claims. Policy that references source.claims applies based on the identity of the proximate peer at each hop.

Annotation changes take effect at certificate renewal: Workload claims reflect the annotations present at the time istiod issued the certificate. Updating a solo.io.security-claims/ annotation does not immediately affect policy evaluation. The new value takes effect when the certificate expires and istiod issues a new one. To apply annotation changes sooner, restart ztunnel. This causes ztunnel to fetch a new certificate and establish new connections, propagating the updated claims immediately.

kubectl rollout restart daemonset/ztunnel -n istio-system

Empty workload claims: When a source workload’s certificate carries no embedded claims, for example because the peer’s ztunnel was not yet upgraded with ENABLE_WORKLOAD_CLAIMS: "true", the source.claims map is empty. Write expressions defensively to handle this case.

CEL evaluation errors are logged at debug level: When a CEL expression raises a runtime error, such as a missing key in source.claims, ztunnel logs it at debug rather than warn. In a mixed cluster where some workloads carry claims and others do not, missing-key errors occur on every connection to an unannotated workload. Logging at debug prevents those per-connection entries from masking genuine issues.