---
title: "Configure RBAC for tiered policies"
description: "Set up Kubernetes RBAC to control which users can edit Calico Open Source policies and tiers in a multi-team cluster."
product: "Calico Open Source"
version: "3.32 (latest)"
section: "Network policy"
canonical_url: "https://docs.tigera.io/calico/latest/network-policy/policy-tiers/rbac-tiered-policies"
---

# Configure RBAC for tiered policies

## Big picture

Configure fine-grained user access controls for tiered policies.

## Value

Self-service is an important part of CI/CD processes for containerization and microservices. Calico provides fine-grained access control (RBAC) for:

- Calico policy and tiers
- Kubernetes network policy

## Concepts

### Standard Kubernetes RBAC

Calico implements the standard **Kubernetes RBAC Authorization APIs** with `Role` and `ClusterRole` types. In the default installation, the Calico API server integrates with Kubernetes RBAC Authorization APIs as an extension API server. When using [native v3 CRDs](https://docs.tigera.io/calico/latest/operations/native-v3-crds.md), tier RBAC is enforced via an admission webhook instead.

> **SECONDARY:** When using native `projectcalico.org/v3` CRDs, tier RBAC is enforced for **create, update, and delete** operations via the admission webhook. However, **GET, LIST, and WATCH** operations on tiered policies are not enforced because admission webhooks cannot intercept read requests. This is a known limitation.

### RBAC for policies and tiers

In Calico, global network policy and network policy resources are associated with a specific tier. Admins can configure access control for these Calico policies using standard Kubernetes `Role` and `ClusterRole` resource types. This makes it easy to manage RBAC for both Kubernetes network policies and Calico tiered network policies. RBAC permissions include managing resources using the Calico web console, and `kubectl`.

### RBAC definitions for Calico network policy

To specify per-tier RBAC for the Calico network policy and Calico global network policy, use pseudo resource kinds and names in the `Role` and `ClusterRole` definitions. For example,

```yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: tier-default-reader
rules:
  - apiGroups: ['projectcalico.org']
    resources: ['tiers']
    resourceNames: ['default']
    verbs: ['get']
  - apiGroups: ['projectcalico.org']
    resources: ['tier.networkpolicies']
    resourceNames: ['default.*']
    verbs: ['get', 'list']
```

Where:

- **resources**: `tier.globalnetworkpolicies` and `tier.networkpolicies`

- **resourceNames**:

  - Blank - any policy of the specified kind across all tiers.
  - `<tiername>.*` - any policy of the specified kind within the named tier.
  - `<tiername.policyname>` - the specific policy of the specified kind. Because the policy name is prefixed with the tier name, this also specifies the tier.

## Before you begin...

**Required**

A **cluster-admin** role with full permissions to create and modify resources.

**Recommended**

A rough idea of your tiered policy workflow, and who should access what. See [Configure tiered policies](https://docs.tigera.io/calico/latest/network-policy/policy-tiers/tiered-policy.md).

## How to

- [Create Admin users, full permissions](#create-admin-users-full-permissions)

> **SECONDARY:** `kubectl auth can-i` cannot be used to check RBAC for tiered policy.

### Create Admin users, full permissions

Create an Admin user with full access to the Calico web console (as well as everything else in the cluster) using the following command. See the Kubernetes documentation to identify users based on your chosen [authentication method](https://kubernetes.io/docs/reference/access-authn-authz/authentication/), and how to use the [RBAC resources](https://kubernetes.io/docs/reference/access-authn-authz/rbac/).

```bash
kubectl create clusterrolebinding permissive-binding \
    --clusterrole=cluster-admin \
    --user=<USER\>
```

## Tutorial

This tutorial shows how to use RBAC to control access to resources and CRUD actions for a non-Admin user, John, with the username **john**.

The RBAC examples shown will include:

- [User cannot read policies in any tier](#user-cannot-read-policies-in-any-tier)
- [User can view all policies, and modify policies in the default namespace and tier](#user-can-view-all-policies-and-modify-policies-in-the-default-namespace-and-tier)
- [User can read policies only in both the default tier and namespace](#user-can-read-policies-only-in-both-the-default-tier-and-namespace)
- [User can read policies only in both a specific tier and in the default namespace](#user-can-read-policies-only-in-both-a-specific-tier-and-in-the-default-namespace)
- [User can only view a specific tier](#user-can-only-view-a-specific-tier)
- [User can read all policies across all tiers and namespaces](#user-can-read-all-policies-across-all-tiers-and-namespaces)
- [User has full control over policies only in both a specific tier and in the default namespace](#user-has-full-control-over-policies-only-in-both-a-specific-tier-and-in-the-default-namespace)

### User cannot read policies in any tier

User 'john' is forbidden from reading policies in any tier (**default** tier, and **net-sec** tier).

When John issues the following command:

```bash
kubectl get networkpolicies.p
```

It returns:

```text
Error from server (Forbidden): networkpolicies.projectcalico.org is forbidden: User "john" cannot list networkpolicies.projectcalico.org in tier "default" and namespace "default" (user cannot get tier)
```

Similarly, when John issues this command:

```bash
kubectl get networkpolicies.p -l projectcalico.org/tier==net-sec
```

It returns:

```text
Error from server (Forbidden): networkpolicies.projectcalico.org is forbidden: User "john" cannot list networkpolicies.projectcalico.org in tier "net-sec" and namespace "default" (user cannot get tier)
```

> **SECONDARY:** The .p' extension (`networkpolicies.p`) is short for "networkpolicies.projectcalico.org" and used to differentiate it from the Kubernetes NetworkPolicy resource and the underlying CRDs (if using the Kubernetes Datastore Driver).

> **SECONDARY:** The label for selecting a tier is `projectcalico.org/tier`. When a label selector is not specified, the server defaults the selection to the `default` tier. Alternatively, a field selector (`spec.tier`) may be used to select a tier.
>
> ```bash
> kubectl get networkpolicies.p --field-selector spec.tier=net-sec
> ```

### User can view all policies, and modify policies in the default namespace and tier

1. Download the [`read-all-crud-default-rbac.yaml` manifest](https://docs.tigera.io/files/read-all-crud-default-rbac.yaml).

2. Run the following command to replace `<USER\>` with the `name or email` of the user you are providing permissions to:

   ```bash
   sed -i -e 's/<USER\>/<name or email>/g' read-all-crud-default-rbac.yaml
   ```

3. Use the following command to install the bindings:

   ```bash
   kubectl apply -f read-all-crud-default-rbac.yaml
   ```

The roles and bindings in this file provide the permissions to read all policies across all tiers and to fully manage policies in the **default** tier and **default** namespace. This file includes the minimum required `ClusterRole` and `ClusterRoleBinding` definitions for all UI users (see `min-ui-user-rbac.yaml` above).

### User can read policies only in both the default tier and namespace

In this example, we give user 'john' permission to read policies only in both the **default** tier and namespace.

```yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: tigera-example-get-default-tier
rules:
 # To access Calico policy in a tier, the user requires "get" access to that tier.
- apiGroups: ["projectcalico.org"]
  resources: ["tiers"]
  resourceNames: ["default"]
  verbs: ["get"]

---

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: tigera-example-read-policies-in-default-tier
rules:
 # This allows "get" and "list" of the Calico NetworkPolicy resources in the default tier.
- apiGroups: ["projectcalico.org"]
  resources: ["tier.networkpolicies"]
  resourceNames: ["default.*"]
  verbs: ["get", "list"]

---

# tigera-example-get-default-tier is applied globally
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: john-can-get-default-tier
subjects:
- kind: User
  name: john
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: tigera-example-get-default-tier
  apiGroup: rbac.authorization.k8s.io

---

# tigera-example-read-policies-in-default-tier is applied per-namespace
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: john-can-read-policies-in-default-tier-and-namespace
subjects:
- kind: User
  name: john
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: tigera-example-read-policies-in-default-tier
  apiGroup: rbac.authorization.k8s.io
```

With the above, user john is able to list all NetworkPolicy resources in the **default** tier:

```bash
kubectl get networkpolicies.p --all-namespaces
```

With some example policies on the cluster, returns:

```text
NAMESPACE   NAME                                        CREATED AT
blue        default.calico-np-blue-ns-default-tier      2021-07-26T09:05:11Z
default     default.calico-np-default-ns-default-tier   2021-07-26T09:05:11Z
green       default.calico-np-green-ns-default-tier     2021-07-26T09:05:13Z
red         default.calico-np-red-ns-default-tier       2021-07-26T09:05:12Z
yellow      default.calico-np-yellow-ns-default-tier    2021-07-26T09:05:13Z
```

As intended, user john can only examine those in the **default** namespace:

```bash
kubectl get networkpolicies.p default.calico-np-green-ns-default-tier -o yaml -n=green
```

Correctly returns:

```text
Error from server (Forbidden): networkpolicies.projectcalico.org "default.calico-np-green-ns-default-tier" is forbidden: User "john" cannot get networkpolicies.projectcalico.org in tier "default" and namespace "green"
```

John also still cannot access tier **net-sec**, as intended:

```bash
kubectl get networkpolicies.p -l projectcalico.org/tier==net-sec
```

This returns:

```text
Error from server (Forbidden): networkpolicies.projectcalico.org is forbidden: User "john" cannot list networkpolicies.projectcalico.org in tier "net-sec" and namespace "default" (user cannot get tier)
```

### User can read policies only in both a specific tier and in the default namespace

Let's assume that the kubernetes-admin gives user 'john' the permission to list the policies in tier **net-sec**, but only examine the detail of the policies that are also in the **default** namespace. To provide these permissions to user 'john', use the following `ClusterRoles`,`ClusterRoleBinding` and `RoleBinding`.

```yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: tigera-example-get-net-sec-tier
rules:
 # To access Calico policy in a tier, the user requires "get" access to that tier.
- apiGroups: ["projectcalico.org"]
  resources: ["tiers"]
  resourceNames: ["net-sec"]
  verbs: ["get"]

---

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: tigera-example-read-policies-in-net-sec-tier
rules:
 # This allows "get" and "list" of the Calico NetworkPolicy resources in the net-sec tier.
- apiGroups: ["projectcalico.org"]
  resources: ["tier.networkpolicies"]
  resourceNames: ["net-sec.*"]
  verbs: ["get", "list"]

---

# tigera-example-get-net-sec-tier is applied globally
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: john-can-get-net-sec-tier
subjects:
- kind: User
  name: john
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: tigera-example-get-net-sec-tier
  apiGroup: rbac.authorization.k8s.io

---

# tigera-example-read-policies-in-net-sec-tier is applied per-namespace
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: john-can-read-policies-in-net-sec-tier-and-namespace
subjects:
- kind: User
  name: john
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: tigera-example-read-policies-in-net-sec-tier
  apiGroup: rbac.authorization.k8s.io
```

### User can only view a specific tier

In this example, the following `ClusterRole` and `ClusterRoleBinding` can be used to provide 'get' access to the **net-sec** tier. This has the effect of making the **net-sec** tier visible in the Calico web console (including listing the names of the policies it contains).

However, to modify or view the details of policies within the **net-sec** tier, additional RBAC permissions would be required.

```yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: tigera-example-make-net-sec-tier-visible
rules:
 # To access Calico policy in a tier, the user requires "get" access to that tier.
- apiGroups: ["projectcalico.org"]
  resources: ["tiers"]
  resourceNames: ["net-sec"]
  verbs: ["get"]

---

# tigera-example-make-net-sec-tier-visible is applied globally
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: john-can-view-the-net-sec-tier
subjects:
- kind: User
  name: john
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: tigera-example-make-net-sec-tier-visible
  apiGroup: rbac.authorization.k8s.io
```

### User can read all policies across all tiers and namespaces

In this example, a single `ClusterRole` is used to provide read access to all policy resource types across all tiers. In this case, there is no need to use both `ClusterRoleBindings` and `RoleBindings` to map these abilities to the target user, because the intention is to for the policy to apply to all current and future namespaces on the cluster, so a `ClusterRoleBinding` provides the desired granularity.

```yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: tigera-example-all-tiers-and-namespaces-policy-reader
rules:
 # To access Calico policy in a tier, the user requires "get" access to that tier.
 # Not specifying any specific "resourceNames" provides access to all tiers.
- apiGroups: ["projectcalico.org"]
  resources: ["tiers"]
  verbs: ["get"]
 # This allows read access to the Kubernetes NetworkPolicy resources (these are always in the default tier).
- apiGroups: ["networking.k8s.io", "extensions"]
  resources: ["networkpolicies"]
  verbs: ["get","watch","list"]
 # This allows read access to the Calico NetworkPolicy and GlobalNetworkPolicies.
 # Not specifying any specific "resourceNames" provides access to them in all tiers.
- apiGroups: ["projectcalico.org"]
  resources: ["tier.networkpolicies","tier.globalnetworkpolicies"]
  verbs: ["get","watch","list"]

---

# tigera-example-all-tiers-and-namespaces-policy-reader is applied globally, with a single ClusterRoleBinding,
# since all the rules it contains apply to all current and future namespaces on the cluster.
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: read-all-tier
subjects:
- kind: User
  name: john
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: tigera-example-all-tiers-and-namespaces-policy-reader
  apiGroup: rbac.authorization.k8s.io
```

### User has full control over policies only in both a specific tier and in the default namespace

In this example, two `ClusterRole` objects are used to provide full access control of Calico NetworkPolicy resource types in the **net-sec** tier:

- The `tiers` resource is bound to a user using a `ClusterRoleBinding`, because it is a global resource. This results in the user having the ability to read the contents of the tier across all namespaces.
- The `networkpolicies` resources are bound to a user using a `RoleBinding`, because the aim in this case was to make them CRUD-able only in the default namespace. You only need this one `ClusterRole` to be defined, but it can be applied to different namespaces using additional `RoleBinding` objects. If the intention was to apply it to all current and future namespaces, a `ClusterRoleBinding` could be used.

```yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: tigera-example-get-net-sec-tier
rules:
 # To access Calico policy in a tier, the user requires "get" access to that tier.
- apiGroups: ["projectcalico.org"]
  resources: ["tiers"]
  resourceNames: ["net-sec"]
  verbs: ["get"]

---

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: tigera-example-crud-policies-in-net-sec-tier
rules:
 # This allows full CRUD access to the Calico NetworkPolicy resources in the net-sec tier.
- apiGroups: ["projectcalico.org"]
  resources: ["tier.networkpolicies"]
  resourceNames: ["net-sec.*"]
  verbs: ["*"]

---

# tigera-example-get-net-sec-tier is applied globally
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: john-can-get-net-sec-tier
subjects:
- kind: User
  name: john
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: tigera-example-get-net-sec-tier
  apiGroup: rbac.authorization.k8s.io

---

# tigera-example-crud-policies-in-net-sec-tier is applied per-namespace
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: john-can-crud-policies-in-net-sec-tier-and-namespace
subjects:
- kind: User
  name: john
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: tigera-example-crud-policies-in-net-sec-tier
  apiGroup: rbac.authorization.k8s.io
```
