days to Istio 1.5

Introducing the Istio v1beta1 Authorization Policy

Istio 1.4 introduces the v1beta1 authorization policy, which is a major update to the previous v1alpha1 role-based access control (RBAC) policy. The new policy provides these improvements:

  • Aligns with Istio configuration model.
  • Improves the user experience by simplifying the API.
  • Supports more use cases (e.g. Ingress/Egress gateway support) without added complexity.

The v1beta1 policy is not backward compatible and requires a one time conversion. A tool is provided to automate this process. The previous configuration resources ClusterRbacConfig, ServiceRole, and ServiceRoleBinding will not be supported from Istio 1.6 onwards.

This post describes the new v1beta1 authorization policy model, its design goals and the migration from v1alpha1 RBAC policies. See the authorization concept page for a detailed in-depth explanation of the v1beta1 authorization policy.

We welcome your feedback about the v1beta1 authorization policy at discuss.istio.io.

Background

To date, Istio provided RBAC policies to enforce access control on services using three configuration resources: ClusterRbacConfig, ServiceRole and ServiceRoleBinding. With this API, users have been able to enforce control access at mesh-level, namespace-level and service-level. Like other RBAC policies, Istio RBAC uses the same concept of role and binding for granting permissions to identities.

Although Istio RBAC has been working reliably, we've found that many improvements were possible.

For example, users have mistakenly assumed that access control enforcement happens at service-level because ServiceRole uses service to specify where to apply the policy, however, the policy is actually applied on workloads, the service is only used to find the corresponding workload. This nuance is significant when multiple services are referring to the same workload. A ServiceRole for service A will also affect service B if the two services are referring to the same workload, which can cause confusion and incorrect configuration.

An other example is that it's proven difficult for users to maintain and manage the Istio RBAC configurations because of the need to deeply understand three related resources.

Design goals

The new v1beta1 authorization policy had several design goals:

  • Align with Istio Configuration Model for better clarity on the policy target. The configuration model provides a unified configuration hierarchy, resolution and target selection.

  • Improve the user experience by simplifying the API. It's easier to manage one custom resource definition (CRD) that includes all access control specifications, instead of multiple CRDs.

  • Support more use cases without added complexity. For example, allow the policy to be applied on Ingress/Egress gateway to enforce access control for traffic entering/exiting the mesh.

AuthorizationPolicy

An AuthorizationPolicy custom resource enables access control on workloads. This section gives an overview of the changes in the v1beta1 authorization policy.

An AuthorizationPolicy includes a selector and a list of rule. The selector specifies on which workload to apply the policy and the list of rule specifies the detailed access control rule for the workload.

The rule is additive, which means a request is allowed if any rule allows the request. Each rule includes a list of from, to and when, which specifies who is allowed to do what under which conditions.

The selector replaces the functionality provided by ClusterRbacConfig and the services field in ServiceRole. The rule replaces the other fields in the ServiceRole and ServiceRoleBinding.

Example

The following authorization policy applies to workloads with app: httpbin and version: v1 label in the foo namespace:

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
 name: httpbin
 namespace: foo
spec:
 selector:
   matchLabels:
     app: httpbin
     version: v1
 rules:
 - from:
   - source:
       principals: ["cluster.local/ns/default/sa/sleep"]
   to:
   - operation:
       methods: ["GET"]
   when:
   - key: request.headers[version]
     values: ["v1", "v2"]

The policy allows principal cluster.local/ns/default/sa/sleep to access the workload using the GET method when the request includes a version header of value v1 or v2. Any requests not matched with the policy will be denied by default.

Assuming the httpbin service is defined as:

apiVersion: v1
kind: Service
metadata:
  name: httpbin
  namespace: foo
spec:
  selector:
    app: httpbin
    version: v1
  ports:
    # omitted

You would need to configure three resources to achieve the same result in v1alpha1:

apiVersion: "rbac.istio.io/v1alpha1"
kind: ClusterRbacConfig
metadata:
  name: default
spec:
  mode: 'ON_WITH_INCLUSION'
  inclusion:
    services: ["httpbin.foo.svc.cluster.local"]
---
apiVersion: "rbac.istio.io/v1alpha1"
kind: ServiceRole
metadata:
  name: httpbin
  namespace: foo
spec:
  rules:
  - services: ["httpbin.foo.svc.cluster.local"]
    methods: ["GET"]
    constraints:
    - key: request.headers[version]
      values: ["v1", "v2"]
---
apiVersion: "rbac.istio.io/v1alpha1"
kind: ServiceRoleBinding
metadata:
  name: httpbin
  namespace: foo
spec:
  subjects:
  - user: "cluster.local/ns/default/sa/sleep"
  roleRef:
    kind: ServiceRole
    name: "httpbin"

Workload selector

A major change in the v1beta1 authorization policy is that it now uses workload selector to specify where to apply the policy. This is the same workload selector used in the Gateway, Sidecar and EnvoyFilter configurations.

The workload selector makes it clear that the policy is applied and enforced on workloads instead of services. If a policy applies to a workload that is used by multiple different services, the same policy will affect the traffic to all the different services.

You can simply leave the selector empty to apply the policy to all workloads in a namespace. The following policy applies to all workloads in the namespace bar:

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
 name: policy
 namespace: bar
spec:
 rules:
 # omitted

Root namespace

A policy in the root namespace applies to all workloads in the mesh in every namespaces. The root namespace is configurable in the MeshConfig and has the default value of istio-system.

For example, you installed Istio in istio-system namespace and deployed workloads in default and bookinfo namespace. The root namespace is changed to istio-config from the default value. The following policy will apply to workloads in every namespace including default, bookinfo and the istio-system:

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
 name: policy
 namespace: istio-config
spec:
 rules:
 # omitted

Ingress/Egress Gateway support

The v1beta1 authorization policy can also be applied on ingress/egress gateway to enforce access control on traffic entering/leaving the mesh, you only need to change the selector to make select the ingress/egress workload.

The following policy applies to workloads with the app: istio-ingressgateway label:

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
 name: ingress
 namespace: istio-system
spec:
 selector:
   matchLabels:
     app: istio-ingressgateway
 rules:
 # omitted

Remember the authorization policy only applies to workloads in the same namespace as the policy, unless the policy is applied in the root namespace:

  • If you don't change the default root namespace value (i.e. istio-system), the above policy will apply to workloads with the app: istio-ingressgateway label in every namespace.

  • If you have changed the root namespace to a different value, the above policy will only apply to workloads with the app: istio-ingressgateway label only in the istio-system namespace.

Comparison

The following table highlights the key differences between the old v1alpha1 RBAC policies and the new v1beta1 authorization policy.

Feature

Featurev1alpha1 RBAC policyv1beta1 Authorization Policy
API stabilityalpha: No backward compatiblebeta: backward compatible guaranteed
Number of CRDsThree: ClusterRbacConfig, ServiceRole and ServiceRoleBindingOnly One: AuthorizationPolicy
Policy targetserviceworkload
Deny-by-default behaviorEnabled explicitly by configuring ClusterRbacConfigEnabled implicitly with AuthorizationPolicy
Ingress/Egress gateway supportNot supportedSupported
The "*" value in policyMatch all contents (empty and non-empty)Match non-empty contents only

The following tables show the relationship between the v1alpha1 and v1beta1 API.

ClusterRbacConfig

ClusterRbacConfig.ModeAuthorizationPolicy
OFFNo policy applied
ONA deny-all policy applied in root namespace
ON_WITH_INCLUSIONpolicies should be applied to namespaces or workloads included by ClusterRbacConfig
ON_WITH_EXCLUSIONpolicies should be applied to namespaces or workloads excluded by ClusterRbacConfig

ServiceRole

ServiceRoleAuthorizationPolicy
servicesselector
pathspaths in to
methodsmethods in to
destination.ip in constraintNot supported
destination.port in constraintports in to
destination.labels in constraintselector
destination.namespace in constraintReplaced by the namespace of the policy, i.e. the namespace in metadata
destination.user in constraintNot supported
experimental.envoy.filters in constraintexperimental.envoy.filters in when
request.headers in constraintrequest.headers in when

ServiceRoleBinding

ServiceRoleBindingAuthorizationPolicy
userprincipals in from
grouprequest.auth.claims[group] in when
source.ip in propertyipBlocks in from
source.namespace in propertynamespaces in from
source.principal in propertyprincipals in from
request.headers in propertyrequest.headers in when
request.auth.principal in propertyrequestPrincipals in from or request.auth.principal in when
request.auth.audiences in propertyrequest.auth.audiences in when
request.auth.presenter in propertyrequest.auth.presenter in when
request.auth.claims in propertyrequest.auth.claims in when

Beyond all the differences, the v1beta1 policy is enforced by the same engine in Envoy and supports the same authenticated identity (mutual TLS or JWT), condition and other primitives (e.g. IP, port and etc.) as the v1alpha1 policy.

Future of the v1alpha1 policy

The v1alpha1 RBAC policy (ClusterRbacConfig, ServiceRole, and ServiceRoleBinding) is deprecated by the v1beta1 authorization policy.

Istio 1.4 continues to support the v1alpha1 RBAC policy to give you enough time to move away from the alpha policies.

Migration from the v1alpha1 policy

Istio only supports one of the two versions for a given workload:

  • If there is only v1beta1 policy for a workload, the v1beta1 policy will be used.
  • If there is only v1alpha1 policy for a workload, the v1alpha1 policy will be used.
  • If there are both v1beta1 and v1alpha1 policies for a workload, only the v1beta1 policy will be used and the the v1alpha1 policy will be ignored.

General Guideline

The typical flow of migrating to v1beta1 policy is to start by checking the ClusterRbacConfig to decide which namespace or service is enabled with RBAC.

For each service enabled with RBAC:

  1. Get the workload selector from the service definition.
  2. Create a v1beta1 policy with the workload selector.
  3. Update the v1beta1 policy for each ServiceRole and ServiceRoleBinding applied to the service.
  4. Apply the v1beta1 policy and monitor the traffic to make sure the policy is working as expected.
  5. Repeat the process for the next service enabled with RBAC.

For each namespace enabled with RBAC:

  1. Apply a v1beta1 policy that denies all traffic to the given namespace.

Migration Example

Assume you have the following v1alpha1 policies for the httpbin service in the foo namespace:

apiVersion: "rbac.istio.io/v1alpha1"
kind: ClusterRbacConfig
metadata:
  name: default
spec:
  mode: 'ON_WITH_INCLUSION'
  inclusion:
    namespaces: ["foo"]
---
apiVersion: "rbac.istio.io/v1alpha1"
kind: ServiceRole
metadata:
  name: httpbin
  namespace: foo
spec:
  rules:
  - services: ["httpbin.foo.svc.cluster.local"]
    methods: ["GET"]
---
apiVersion: "rbac.istio.io/v1alpha1"
kind: ServiceRoleBinding
metadata:
  name: httpbin
  namespace: foo
spec:
  subjects:
  - user: "cluster.local/ns/default/sa/sleep"
  roleRef:
    kind: ServiceRole
    name: "httpbin"

Migrate the above policies to v1beta1 in the following ways:

  1. Assume the httpbin service has the following workload selector:

    selector:
      app: httpbin
      version: v1
    
  2. Create a v1beta1 policy with the workload selector:

    apiVersion: security.istio.io/v1beta1
    kind: AuthorizationPolicy
    metadata:
     name: httpbin
     namespace: foo
    spec:
     selector:
       matchLabels:
         app: httpbin
         version: v1
    
  3. Update the v1beta1 policy with each ServiceRole and ServiceRoleBinding applied to the service:

    apiVersion: security.istio.io/v1beta1
    kind: AuthorizationPolicy
    metadata:
     name: httpbin
     namespace: foo
    spec:
     selector:
       matchLabels:
         app: httpbin
         version: v1
     rules:
     - from:
       - source:
           principals: ["cluster.local/ns/default/sa/sleep"]
       to:
       - operation:
           methods: ["GET"]
    
  4. Apply the v1beta1 policy and monitor the traffic to make sure it works as expected.

  5. Apply the following v1beta1 policy that denies all traffic to the foo namespace because the foo namespace is enabled with RBAC:

    apiVersion: security.istio.io/v1beta1
    kind: AuthorizationPolicy
    metadata:
     name: deny-all
     namespace: foo
    spec:
     {}
    

Make sure the v1beta1 policy is working as expected and then you can delete the v1alpha1 policies from the cluster.

Automation of the Migration

To help ease the migration, the istioctl experimental authz convert command is provided to automatically convert the v1alpha1 policies to the v1beta1 policy.

You can evaluate the command but it is experimental in Istio 1.4 and doesn't support the full v1alpha1 semantics as of the date of this blog post.

The command to support the full v1alpha1 semantics is expected in a patch release following Istio 1.4.

Was this information useful?
Do you have any suggestions for improvement?

Thanks for your feedback!