From 392691f200bc6b2249a4e7cccf24c8e4af6aff2c Mon Sep 17 00:00:00 2001 From: Dimitri Nicolopoulos Date: Mon, 21 Sep 2026 16:05:31 -0700 Subject: [PATCH 1/6] Document the RBAC management UI for Calico Enterprise Add "Manage roles in the web console" under Operations > Calico Enterprise Manager UI, covering what the feature needs to be usable: turning it on, creating and scoping a role, granting it to a subject, binding it to an identity provider group, reviewing who holds what, and exporting roles to another cluster. Scoped to Calico Enterprise 3.24 (next) only, plus its sidebar entry and a cross-link from "Configure user roles and permissions". Behaviour the page is deliberate about, since each is easy to get wrong: - Role names take any non-empty string up to 253 characters, matching ValidateIdentity. Spaces, '@' and non-ASCII are all valid and necessary, since the name has to equal the group claim the IdP sends. - Turning the feature off uses get | jq | kubectl replace, because tigera-network-admin holds get and update on rbac-ui-config, not patch. - Subjects added by hand go on the ClusterRoleBindings. Those are what FindExistingMemberSubjects reads back, so a subject added only to a namespaced RoleBinding is dropped the next time the role is edited in the console. - IdP group binding is LDAP-only and single-homed on the management cluster: the manager's egress opens 389/636 only when Authentication.spec.ldap is set and scopes the destination to spec.ldap.host, and the /team/idp-groups routes always target the management cluster. The directory-sync secret is a second secret, distinct from tigera-ldap-credentials. - Export carries bindings, not the ClusterRoles they reference, so the target cluster needs role management on and the same tiers and namespaces. Co-Authored-By: Claude Opus 5 (1M context) --- .../operations/cnx/manage-roles.mdx | 142 ++++++++++++++++++ .../operations/cnx/roles-and-permissions.mdx | 2 + sidebars-calico-enterprise.js | 1 + 3 files changed, 145 insertions(+) create mode 100644 calico-enterprise/operations/cnx/manage-roles.mdx diff --git a/calico-enterprise/operations/cnx/manage-roles.mdx b/calico-enterprise/operations/cnx/manage-roles.mdx new file mode 100644 index 0000000000..5c17918c52 --- /dev/null +++ b/calico-enterprise/operations/cnx/manage-roles.mdx @@ -0,0 +1,142 @@ +--- +description: Grant scoped access to Calico Enterprise by creating custom roles in the web console and binding them to groups from your identity provider, instead of writing Kubernetes RBAC manifests by hand. +--- + +import IconUser from '/img/icons/user-icon.svg'; + +# Grant access with custom roles and IdP groups + +## Big picture + +Create and scope $[prodname] roles from the **Manage Team** page in the web console, and bind them to groups from your identity provider, instead of writing Kubernetes RBAC manifests by hand. + +## Value + +Giving a team access to one tier, one namespace, or one web console feature normally means hand-writing `ClusterRole` and `ClusterRoleBinding` manifests, and knowing which $[prodname] API resources each feature reads. **Manage Team** turns that into a list of named permissions: you pick what a role can view or modify and where it applies, and $[prodname] writes and reconciles the underlying Kubernetes RBAC for you. + +## Concepts + +### A role is a named set of permissions + +A role in the console is a set of permissions under a name, and that name is a Kubernetes **group**. Membership in the group is what grants the role: anyone whose login carries the group has it. Nothing else decides who holds a role. + +Behind each role, $[prodname] writes the Kubernetes RBAC that grants its permissions — `ClusterRoleBindings`, or `RoleBindings` where you scoped a permission to a namespace — labeled `app.kubernetes.io/managed-by=calico-ui-rbac`. These are the console's to manage; you do not edit them by hand. + +The console does not create or invite users. The **Users** tab is read-only, and lists the subjects it finds on those bindings and in any bound identity provider groups, with the roles in effect for each. + +### Role management is per cluster + +Role management is off by default and is turned on one cluster at a time, including each managed cluster in a multi-cluster deployment. Roles are local to the cluster they were created on and are not synchronized; use **Export YAML** to copy them to another cluster. + +## Before you begin + +**Required** + +- A cluster running $[prodname] 3.24.0-3.0 or later +- [Access to the web console](access-the-manager.mdx) as a user bound to `tigera-network-admin` + +**Limitations** + +- Roles do not synchronize between clusters. Exported roles are a copy, not a link. + +## How to + +- [Turn on role management](#turn-on-role-management) +- [Connect an identity provider directory](#connect-an-identity-provider-directory) +- [Create a role](#create-a-role) +- [See who has access](#see-who-has-access) +- [Copy roles to another cluster](#copy-roles-to-another-cluster) + +### Turn on role management + +1. In the web console, select the cluster you want to manage roles on. +1. Click the user icon > **Manage Team**. +1. Click **Enable RBAC management**. + +$[prodname] sets `rbac-ui-enabled` to `true` in the `rbac-ui-config` ConfigMap in the `calico-system` namespace, then builds the catalogue of permissions. This takes a few seconds, after which the **Roles** and **Users** tabs appear. + +To turn role management off again, set the flag back to `false`; the console has no control for this. Roles you already created keep working, but are no longer manageable from the console. + +```bash +kubectl get configmap rbac-ui-config -n calico-system -o json \ + | jq '.data["rbac-ui-enabled"] = "false"' \ + | kubectl replace -f - +``` + +`kubectl patch` fails here: `tigera-network-admin` holds `get` and `update` on this ConfigMap, not `patch`. + +### Connect an identity provider directory + +Connecting your directory lets roles bind to groups that already exist in it, so membership stays managed in your identity provider and users pick their roles up at the next sign-in. + +This requires the cluster to authenticate users with [LDAP](configure-identity-provider.mdx), on port 389 or 636. The directory is read on the management cluster only, so create the secret there and turn role management on there too, even when the role applies to a managed cluster. + +Create the directory-sync secret. This is separate from the `tigera-ldap-credentials` secret that authentication uses; its `url` must point at the same host as `Authentication.spec.ldap.host`, written as a full URL. $[prodname] walks the directory with these credentials and offers the groups it finds when you create a role. + +```bash +kubectl create secret generic tigera-idp-ldap-config -n calico-system \ + --from-literal=url=ldaps://ad.example.com:636 \ + --from-literal=bindDN='cn=admin,dc=example,dc=com' \ + --from-literal=bindPassword='' \ + --from-literal=baseDN='ou=groups,dc=example,dc=com' +``` + +Optionally add `groupFilter` (default `(objectClass=groupOfNames)`), `nameAttribute` (default `cn`; Active Directory typically uses `sAMAccountName`), `caBundle` for a private certificate authority, and `refreshIntervalSeconds` (default 300, clamped to 60–86400). + +### Create a role + +A role's name is a group, so choosing the name decides who gets it. There are two ways to set it: + +- **Bind an IdP group** takes the name from a group in your [connected directory](#connect-an-identity-provider-directory), so everyone already in that group has the role. Membership stays managed in your identity provider, and there is nothing to do outside the console. This is the usual choice. +- **Manual group binding** lets you type the group name yourself, for a group the directory sync does not offer. + +1. Click the user icon > **Manage Team** > **Roles** > **Create Role**. +1. Choose what the role binds to, then click **Next**. With no directory connected this step does not appear and the role form opens straight away. + - **Bind an IdP group** is selected by default. Pick a group from the list; a group can back only one role, so any that already do are shown as unavailable. + - **Manual group binding** opens the form with an empty name for you to fill in. +1. Name the role: + - For an IdP group, **IdP Group** is fixed to the group you picked. **Role Display Name** is cosmetic: it is what the **Roles** list shows, so you can make it readable without changing who holds the role. + - For a manual role, **Role Name** is the group itself. Any non-empty value up to 253 characters is accepted, spaces and `@` included. +1. Click **Add Permission** and choose one. Permissions are per feature area — policies, network sets, dashboards, service graph, packet captures, egress gateways and so on — each offered as **View** or **Modify**. The picker lists what is available on the cluster. Note that **Alerts and Security Events Settings** covers alert configuration, not the events themselves. +1. Scope the permission, where it supports it: + - **Tier** — for policy permissions. Leave it as **all tiers** to apply the permission to every tier. + - **Namespace** — for namespaced permissions. Leave it as **any namespace** to apply the permission cluster-wide. +1. Add any further permissions, then click **Save**. A role must carry at least one permission. + +A role bound to a directory group appears in the list as **Custom - IdP**. Its membership is owned by your identity provider, so its subjects cannot be edited from the console. + +To change a role later, select **Actions** > **Edit Permissions**. **Delete Role** removes the role and its bindings from this cluster; subjects bound to it lose the access it granted. + +### See who has access + +Click the user icon > **Manage Team** > **Users** to review which subjects hold which roles, and when each last signed in. Select a subject to see the permissions its roles carry. + +The tab is read-only, and lists the subjects on the roles' bindings plus the members of any bound identity provider groups; **Last signed in** fills in once a subject authenticates. Effective permissions are the union of these roles and anything bound to the subject outside the console, which this view does not show. + +To audit which group each role binds, list the bindings the console manages. + +```bash +kubectl get clusterrolebinding -l app.kubernetes.io/managed-by=calico-ui-rbac \ + -o custom-columns='NAME:.metadata.name,ROLE:.roleRef.name,GROUP:.subjects[0].name' +``` + +### Copy roles to another cluster + +Roles apply only to the cluster they were created on. To reuse them elsewhere, export them and apply them to a cluster that also has role management turned on. The export carries each role's bindings, not the permissions they point at, and those exist only where the console has built its catalogue. + +1. Select the source cluster, then click the user icon > **Manage Team** > **Roles** > **Export YAML**. +1. Apply the file to each target cluster. + + ```bash + kubectl apply -f .yaml + ``` + +Check first that the target cluster has the tiers and namespaces the roles were scoped to. A binding naming a tier that is missing there applies without error but grants nothing, and a binding into a namespace that does not exist is rejected. + +The roles are independent copies: later changes on the source cluster are not propagated. + +## Additional resources + +- [Configure user roles and permissions](roles-and-permissions.mdx) +- [Configure an external identity provider](configure-identity-provider.mdx) +- [Configure RBAC for tiered policies](../../network-policy/policy-tiers/rbac-tiered-policies.mdx) diff --git a/calico-enterprise/operations/cnx/roles-and-permissions.mdx b/calico-enterprise/operations/cnx/roles-and-permissions.mdx index 1c183ccad2..e3efb154c8 100644 --- a/calico-enterprise/operations/cnx/roles-and-permissions.mdx +++ b/calico-enterprise/operations/cnx/roles-and-permissions.mdx @@ -40,6 +40,8 @@ $[prodname] provides the following predefined roles and permissions: ## Additional resources +- [Grant access with custom roles and IdP groups](manage-roles.mdx) — create and scope roles in the web console, without writing RBAC manifests. + For RBAC details on any given feature, see the feature. For example: - [Tiered policy RBAC](../../network-policy/policy-tiers/rbac-tiered-policies.mdx) diff --git a/sidebars-calico-enterprise.js b/sidebars-calico-enterprise.js index de01d7ebe9..0809051e62 100644 --- a/sidebars-calico-enterprise.js +++ b/sidebars-calico-enterprise.js @@ -549,6 +549,7 @@ module.exports = { 'operations/cnx/authentication-quickstart', 'operations/cnx/configure-identity-provider', 'operations/cnx/roles-and-permissions', + 'operations/cnx/manage-roles', ], }, 'operations/comms/index', From 4f2b1fe3ad9662b6696df1cc64dc96d15617554a Mon Sep 17 00:00:00 2001 From: Dimitri Nicolopoulos Date: Mon, 28 Sep 2026 17:07:22 -0700 Subject: [PATCH 2/6] Document managed-cluster roles, export, directory setup and permission notes for the RBAC management UI --- .../operations/cnx/manage-roles.mdx | 46 +++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/calico-enterprise/operations/cnx/manage-roles.mdx b/calico-enterprise/operations/cnx/manage-roles.mdx index 5c17918c52..9b10ef56af 100644 --- a/calico-enterprise/operations/cnx/manage-roles.mdx +++ b/calico-enterprise/operations/cnx/manage-roles.mdx @@ -26,7 +26,9 @@ The console does not create or invite users. The **Users** tab is read-only, and ### Role management is per cluster -Role management is off by default and is turned on one cluster at a time, including each managed cluster in a multi-cluster deployment. Roles are local to the cluster they were created on and are not synchronized; use **Export YAML** to copy them to another cluster. +Role management is off by default and is turned on one cluster at a time, including each managed cluster in a multi-cluster deployment. A role belongs to the cluster it was created on and is not synchronized to other clusters; use **Export YAML** to copy it. + +On a managed cluster, a role's log and Kibana permissions are held on the management cluster, because that is where the logs and Kibana live. The console writes and removes them with the rest of the role, so you manage the role from the managed cluster as usual. ## Before you begin @@ -38,6 +40,8 @@ Role management is off by default and is turned on one cluster at a time, includ **Limitations** - Roles do not synchronize between clusters. Exported roles are a copy, not a link. +- Directory groups come from LDAP only. With an OIDC-only identity provider, such as Okta or Microsoft Entra ID, **Bind an IdP group** is not offered; use **Manual group binding** instead. +- Role management cannot be turned on for a multi-tenant management cluster. ## How to @@ -71,7 +75,9 @@ Connecting your directory lets roles bind to groups that already exist in it, so This requires the cluster to authenticate users with [LDAP](configure-identity-provider.mdx), on port 389 or 636. The directory is read on the management cluster only, so create the secret there and turn role management on there too, even when the role applies to a managed cluster. -Create the directory-sync secret. This is separate from the `tigera-ldap-credentials` secret that authentication uses; its `url` must point at the same host as `Authentication.spec.ldap.host`, written as a full URL. $[prodname] walks the directory with these credentials and offers the groups it finds when you create a role. +On the management cluster, open **Manage Team** and connect the directory, giving its URL, a bind DN and password, and the base DN to search for groups. The URL must point at the same host as `Authentication.spec.ldap.host`, written as a full URL. $[prodname] walks the directory with these credentials and offers the groups it finds when you create a role. The bind password is not shown again after you save it. + +The console stores these settings in the `tigera-idp-ldap-config` secret in the `calico-system` namespace, separate from the `tigera-ldap-credentials` secret that authentication uses. You can create the secret with `kubectl` instead: ```bash kubectl create secret generic tigera-idp-ldap-config -n calico-system \ @@ -103,15 +109,24 @@ A role's name is a group, so choosing the name decides who gets it. There are tw - **Namespace** — for namespaced permissions. Leave it as **any namespace** to apply the permission cluster-wide. 1. Add any further permissions, then click **Save**. A role must carry at least one permission. +#### What some permissions include + +- **Logs** are granted per log type (**View Flow Logs**, **View DNS Logs**, **View Audit Logs (Timeline)** and so on, or **View All Logs**) and apply to the cluster the role is on. +- **View Kibana** signs the user in to Kibana, which is a single instance on the management cluster shared by all clusters. What they see there comes only from their log permissions, cluster by cluster. +- **Policy** permissions do not include audit logs. To see a policy's change history on the policy board, add **View Audit Logs (Timeline)**. +- **Policy** permissions include Kubernetes network policies only when they cover the `default` tier, where $[prodname] enforces them. +- **Modify Alerts** also reads the logs its alerts query, because an alert copies what it finds into the events it raises. **View IP Pools** and **View L2 Networks** list pods in every namespace. The permission picker says so for each. +- **Policy Recommendations** is offered as **Modify** only. + A role bound to a directory group appears in the list as **Custom - IdP**. Its membership is owned by your identity provider, so its subjects cannot be edited from the console. -To change a role later, select **Actions** > **Edit Permissions**. **Delete Role** removes the role and its bindings from this cluster; subjects bound to it lose the access it granted. +To change a role later, select **Actions** > **Edit Permissions**. **Delete Role** removes the role and its bindings from this cluster; subjects bound to it lose the access it granted. For a role bound to a directory group, the group stays in the directory and can be bound to a new role. ### See who has access Click the user icon > **Manage Team** > **Users** to review which subjects hold which roles, and when each last signed in. Select a subject to see the permissions its roles carry. -The tab is read-only, and lists the subjects on the roles' bindings plus the members of any bound identity provider groups; **Last signed in** fills in once a subject authenticates. Effective permissions are the union of these roles and anything bound to the subject outside the console, which this view does not show. +The tab is read-only, and lists the subjects on the roles' bindings plus the members of any bound identity provider groups. A directory group's members appear once they have signed in to the web console at least once; until then the group's role applies to them, but they are not listed. Effective permissions are the union of these roles and anything bound to the subject outside the console, which this view does not show. To audit which group each role binds, list the bindings the console manages. @@ -125,11 +140,26 @@ kubectl get clusterrolebinding -l app.kubernetes.io/managed-by=calico-ui-rbac \ Roles apply only to the cluster they were created on. To reuse them elsewhere, export them and apply them to a cluster that also has role management turned on. The export carries each role's bindings, not the permissions they point at, and those exist only where the console has built its catalogue. 1. Select the source cluster, then click the user icon > **Manage Team** > **Roles** > **Export YAML**. -1. Apply the file to each target cluster. +1. Apply the file: + - For a standalone or management cluster, apply it to the target cluster. + + ```bash + kubectl apply -f + ``` - ```bash - kubectl apply -f .yaml - ``` + - For a managed cluster, the file also holds the bindings kept on the management cluster, labeled `rbac.tigera.io/managed-cluster`. Apply each part to its cluster. + + ```bash + kubectl --context apply -f -l '!rbac.tigera.io/managed-cluster' + kubectl --context apply -f -l rbac.tigera.io/managed-cluster + ``` + +The console exports a managed cluster's roles for that same cluster. To copy them to a different managed cluster, request the export for the target through the API, then apply it as above. + +```bash +curl -H "Authorization: Bearer " \ + "https:///ui-apis/team/export?cluster=&target=" > +``` Check first that the target cluster has the tiers and namespaces the roles were scoped to. A binding naming a tier that is missing there applies without error but grants nothing, and a binding into a namespace that does not exist is rejected. From d109d2e7da77f9d883bd0955e5221c4ef6e541ca Mon Sep 17 00:00:00 2001 From: Dimitri Nicolopoulos Date: Fri, 2 Oct 2026 14:27:48 -0700 Subject: [PATCH 3/6] Describe directory setup as creating the secret The console has no form for the LDAP directory connection in this release, so the section now leads with creating tigera-idp-ldap-config and explains the URL and host requirement after the example. Drop the note about the bind password not being shown again, which only applied to the API, and the remark that the console cannot turn role management off. --- calico-enterprise/operations/cnx/manage-roles.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/calico-enterprise/operations/cnx/manage-roles.mdx b/calico-enterprise/operations/cnx/manage-roles.mdx index 9b10ef56af..db82899ccd 100644 --- a/calico-enterprise/operations/cnx/manage-roles.mdx +++ b/calico-enterprise/operations/cnx/manage-roles.mdx @@ -59,7 +59,7 @@ On a managed cluster, a role's log and Kibana permissions are held on the manage $[prodname] sets `rbac-ui-enabled` to `true` in the `rbac-ui-config` ConfigMap in the `calico-system` namespace, then builds the catalogue of permissions. This takes a few seconds, after which the **Roles** and **Users** tabs appear. -To turn role management off again, set the flag back to `false`; the console has no control for this. Roles you already created keep working, but are no longer manageable from the console. +To turn role management off again, set the flag back to `false`. Roles you already created keep working, but are no longer manageable from the console. ```bash kubectl get configmap rbac-ui-config -n calico-system -o json \ @@ -75,9 +75,7 @@ Connecting your directory lets roles bind to groups that already exist in it, so This requires the cluster to authenticate users with [LDAP](configure-identity-provider.mdx), on port 389 or 636. The directory is read on the management cluster only, so create the secret there and turn role management on there too, even when the role applies to a managed cluster. -On the management cluster, open **Manage Team** and connect the directory, giving its URL, a bind DN and password, and the base DN to search for groups. The URL must point at the same host as `Authentication.spec.ldap.host`, written as a full URL. $[prodname] walks the directory with these credentials and offers the groups it finds when you create a role. The bind password is not shown again after you save it. - -The console stores these settings in the `tigera-idp-ldap-config` secret in the `calico-system` namespace, separate from the `tigera-ldap-credentials` secret that authentication uses. You can create the secret with `kubectl` instead: +On the management cluster, create the `tigera-idp-ldap-config` secret in the `calico-system` namespace with the directory's URL, a bind DN and password, and the base DN to search for groups. This secret is separate from the `tigera-ldap-credentials` secret that authentication uses. ```bash kubectl create secret generic tigera-idp-ldap-config -n calico-system \ @@ -87,6 +85,8 @@ kubectl create secret generic tigera-idp-ldap-config -n calico-system \ --from-literal=baseDN='ou=groups,dc=example,dc=com' ``` +The URL must be a full `ldap://` or `ldaps://` URL whose host matches `Authentication.spec.ldap.host`. The web console can connect only to that host, so a URL pointing anywhere else is accepted but no groups are found. $[prodname] picks up the secret, searches the directory with these credentials, and offers the groups it finds when you create a role. It searches again every five minutes by default. + Optionally add `groupFilter` (default `(objectClass=groupOfNames)`), `nameAttribute` (default `cn`; Active Directory typically uses `sAMAccountName`), `caBundle` for a private certificate authority, and `refreshIntervalSeconds` (default 300, clamped to 60–86400). ### Create a role From 5cecfbd662e48be0b1b7b67e2672092e37bb825c Mon Sep 17 00:00:00 2001 From: Dimitri Nicolopoulos Date: Fri, 2 Oct 2026 14:41:38 -0700 Subject: [PATCH 4/6] Describe copying managed-cluster roles by editing the export Replace the curl to /ui-apis/team/export?target= with the edits it makes: in the bindings labeled rbac.tigera.io/managed-cluster, the source cluster's name appears in the label, the annotation, the end of metadata.name and the end of roleRef.name. Changing those four values to the target cluster gives the same file the API would return. Every other binding in the export does not name a cluster and applies unchanged. --- .../operations/cnx/manage-roles.mdx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/calico-enterprise/operations/cnx/manage-roles.mdx b/calico-enterprise/operations/cnx/manage-roles.mdx index db82899ccd..4f3eaa60cf 100644 --- a/calico-enterprise/operations/cnx/manage-roles.mdx +++ b/calico-enterprise/operations/cnx/manage-roles.mdx @@ -154,11 +154,17 @@ Roles apply only to the cluster they were created on. To reuse them elsewhere, e kubectl --context apply -f -l rbac.tigera.io/managed-cluster ``` -The console exports a managed cluster's roles for that same cluster. To copy them to a different managed cluster, request the export for the target through the API, then apply it as above. - -```bash -curl -H "Authorization: Bearer " \ - "https:///ui-apis/team/export?cluster=&target=" > +The file is written for the cluster it was exported from. To copy a managed cluster's roles to a different managed cluster, edit the bindings labeled `rbac.tigera.io/managed-cluster` before you apply them, replacing the source cluster's name with the target's in four places: the label, the annotation with the same key, the end of `metadata.name`, and the end of `roleRef.name`. The rest of the file does not name a cluster and applies as it is. + +```yaml +metadata: + name: calico-ui-- + labels: + rbac.tigera.io/managed-cluster: + annotations: + rbac.tigera.io/managed-cluster: +roleRef: + name: calico-ui-logs-view-flows- ``` Check first that the target cluster has the tiers and namespaces the roles were scoped to. A binding naming a tier that is missing there applies without error but grants nothing, and a binding into a namespace that does not exist is rejected. From b923d111f7eb2728e080f2216132110d3b3396b8 Mon Sep 17 00:00:00 2001 From: Dimitri Nicolopoulos Date: Fri, 2 Oct 2026 15:14:43 -0700 Subject: [PATCH 5/6] Correct the RBAC management page against the console and backend Fixes found by checking each statement against ui-apis, rbacsync, the operator and ui-modules: - Tier has no default; all tiers has to be chosen. - The permission is Modify Alerts and Security Events Settings, and its notes show in the expanded role, not in the picker. View also reads the security events. - Not every permission comes as View and Modify; list the one-variant ones. - A managed cluster's roles also need role management on the management cluster. - Turning role management off freezes roles: all tiers stops following new tiers and directory removals stop revoking. - Exports copy between standalone and management clusters, or between managed clusters. A missing tier is rejected for tigera-network-admin. - Drop the audit command, which missed RoleBindings and listed hidden bindings. - Name the Authentication settings an IdP role depends on: groupSearch, a matching nameAttribute, and no groupsPrefix. Manual role names must match the token's group, prefix included. - Renaming a manual role changes only its display name. - Directory removals revoke roles, and tigera-network-admin cannot change the directory secret after creating it. - Create the directory secret from a manifest, keeping the password off the command line. - Drop the minimum version line. Link the page from the identity provider page. --- .../cnx/configure-identity-provider.mdx | 3 + .../operations/cnx/manage-roles.mdx | 64 +++++++++++-------- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/calico-enterprise/operations/cnx/configure-identity-provider.mdx b/calico-enterprise/operations/cnx/configure-identity-provider.mdx index 7206f154d6..f0054a3ffc 100644 --- a/calico-enterprise/operations/cnx/configure-identity-provider.mdx +++ b/calico-enterprise/operations/cnx/configure-identity-provider.mdx @@ -405,6 +405,8 @@ Or use the groups flag to assign cluster role to a group of users. kubectl create clusterrolebinding all-developers-tigera-ui-user --group= --clusterrole=tigera-ui-user ``` +To create scoped roles in the web console and bind them to the groups in your LDAP directory, see [Grant access with custom roles and IdP groups](manage-roles.mdx). + ### (Optional) Allow $[prodname] URIs in your IdP Most IdPs require redirect URIs to be allowed to redirect users at the end of the OAuth flow to the $[prodname] web console or to Kibana. Consult your IdP documentation for authorizing your domain for the respective origins and destinations. @@ -427,5 +429,6 @@ Most IdPs require redirect URIs to be allowed to redirect users at the end of th ## Additional resources - [Configure user roles and permissions](roles-and-permissions.mdx) +- [Grant access with custom roles and IdP groups](manage-roles.mdx) - [Configure RBAC for tiered policies](../../network-policy/policy-tiers/rbac-tiered-policies.mdx) - [Configure RBAC for Elasticsearch](../../observability/elastic/rbac-elasticsearch.mdx) diff --git a/calico-enterprise/operations/cnx/manage-roles.mdx b/calico-enterprise/operations/cnx/manage-roles.mdx index 4f3eaa60cf..9fba871bbd 100644 --- a/calico-enterprise/operations/cnx/manage-roles.mdx +++ b/calico-enterprise/operations/cnx/manage-roles.mdx @@ -28,13 +28,12 @@ The console does not create or invite users. The **Users** tab is read-only, and Role management is off by default and is turned on one cluster at a time, including each managed cluster in a multi-cluster deployment. A role belongs to the cluster it was created on and is not synchronized to other clusters; use **Export YAML** to copy it. -On a managed cluster, a role's log and Kibana permissions are held on the management cluster, because that is where the logs and Kibana live. The console writes and removes them with the rest of the role, so you manage the role from the managed cluster as usual. +On a managed cluster, a role's log and Kibana permissions are held on the management cluster, because that is where the logs and Kibana live. The console writes and removes them with the rest of the role, so you manage the role from the managed cluster as usual. For this to work, role management must also be turned on in the management cluster. ## Before you begin **Required** -- A cluster running $[prodname] 3.24.0-3.0 or later - [Access to the web console](access-the-manager.mdx) as a user bound to `tigera-network-admin` **Limitations** @@ -59,7 +58,7 @@ On a managed cluster, a role's log and Kibana permissions are held on the manage $[prodname] sets `rbac-ui-enabled` to `true` in the `rbac-ui-config` ConfigMap in the `calico-system` namespace, then builds the catalogue of permissions. This takes a few seconds, after which the **Roles** and **Users** tabs appear. -To turn role management off again, set the flag back to `false`. Roles you already created keep working, but are no longer manageable from the console. +To turn role management off again, set the flag back to `false`. Roles you already created keep granting what they did, but are no longer manageable from the console, and $[prodname] stops keeping them up to date: a role scoped to **all tiers** does not cover tiers created afterwards, and a role bound to a directory group keeps its access after the group is removed from the directory. ```bash kubectl get configmap rbac-ui-config -n calico-system -o json \ @@ -75,20 +74,35 @@ Connecting your directory lets roles bind to groups that already exist in it, so This requires the cluster to authenticate users with [LDAP](configure-identity-provider.mdx), on port 389 or 636. The directory is read on the management cluster only, so create the secret there and turn role management on there too, even when the role applies to a managed cluster. -On the management cluster, create the `tigera-idp-ldap-config` secret in the `calico-system` namespace with the directory's URL, a bind DN and password, and the base DN to search for groups. This secret is separate from the `tigera-ldap-credentials` secret that authentication uses. +A user gets a directory group's role only if their sign-in carries the group, so check the LDAP settings in the `Authentication` resource: -```bash -kubectl create secret generic tigera-idp-ldap-config -n calico-system \ - --from-literal=url=ldaps://ad.example.com:636 \ - --from-literal=bindDN='cn=admin,dc=example,dc=com' \ - --from-literal=bindPassword='' \ - --from-literal=baseDN='ou=groups,dc=example,dc=com' +- `spec.ldap.groupSearch` must be set. Without it, users sign in with no groups. +- `spec.ldap.groupSearch.nameAttribute` must be the same attribute as `nameAttribute` in the secret below (`cn` unless you set it), so that both give a group the same name. +- `spec.groupsPrefix` must not be set. Roles are bound to the group name as the directory has it, without a prefix. + +On the management cluster, create the `tigera-idp-ldap-config` secret in the `calico-system` namespace with the directory's URL, a bind DN and password, and the base DN to search for groups. This secret is separate from the `tigera-ldap-credentials` secret that authentication uses. Save the manifest to a file and create the secret with `kubectl create -f `. + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: tigera-idp-ldap-config + namespace: calico-system +stringData: + url: ldaps://ad.example.com:636 + bindDN: cn=admin,dc=example,dc=com + bindPassword: + baseDN: ou=groups,dc=example,dc=com ``` The URL must be a full `ldap://` or `ldaps://` URL whose host matches `Authentication.spec.ldap.host`. The web console can connect only to that host, so a URL pointing anywhere else is accepted but no groups are found. $[prodname] picks up the secret, searches the directory with these credentials, and offers the groups it finds when you create a role. It searches again every five minutes by default. Optionally add `groupFilter` (default `(objectClass=groupOfNames)`), `nameAttribute` (default `cn`; Active Directory typically uses `sAMAccountName`), `caBundle` for a private certificate authority, and `refreshIntervalSeconds` (default 300, clamped to 60–86400). +When a group leaves the directory, or stops matching `baseDN`, `groupFilter` or `nameAttribute`, $[prodname] removes the roles bound to it, so changing these settings can remove many roles at once. If the directory cannot be reached, nothing is removed. + +A `tigera-network-admin` user can create the secret but cannot read, change or delete it, so changing the settings later needs a cluster administrator. + ### Create a role A role's name is a group, so choosing the name decides who gets it. There are two ways to set it: @@ -102,12 +116,12 @@ A role's name is a group, so choosing the name decides who gets it. There are tw - **Manual group binding** opens the form with an empty name for you to fill in. 1. Name the role: - For an IdP group, **IdP Group** is fixed to the group you picked. **Role Display Name** is cosmetic: it is what the **Roles** list shows, so you can make it readable without changing who holds the role. - - For a manual role, **Role Name** is the group itself. Any non-empty value up to 253 characters is accepted, spaces and `@` included. -1. Click **Add Permission** and choose one. Permissions are per feature area — policies, network sets, dashboards, service graph, packet captures, egress gateways and so on — each offered as **View** or **Modify**. The picker lists what is available on the cluster. Note that **Alerts and Security Events Settings** covers alert configuration, not the events themselves. + - For a manual role, **Role Name** is the group itself, exactly as users' sign-in carries it, including any `groupsPrefix` set in the `Authentication` resource. Microsoft Entra ID, for example, sends a group's object ID rather than its name unless you configure it otherwise. Any non-empty value up to 253 characters is accepted, spaces and `@` included. +1. Choose a permission from the list. Permissions are per feature area — policies, network sets, dashboards, service graph, packet captures, egress gateways and so on — most of them offered as both **View** and **Modify**. 1. Scope the permission, where it supports it: - - **Tier** — for policy permissions. Leave it as **all tiers** to apply the permission to every tier. + - **Tier** — for policy permissions. Choose **all tiers** to apply the permission to every tier. - **Namespace** — for namespaced permissions. Leave it as **any namespace** to apply the permission cluster-wide. -1. Add any further permissions, then click **Save**. A role must carry at least one permission. +1. Click **Add Permission** for each further permission, then click **Save**. A role must carry at least one permission. #### What some permissions include @@ -115,30 +129,28 @@ A role's name is a group, so choosing the name decides who gets it. There are tw - **View Kibana** signs the user in to Kibana, which is a single instance on the management cluster shared by all clusters. What they see there comes only from their log permissions, cluster by cluster. - **Policy** permissions do not include audit logs. To see a policy's change history on the policy board, add **View Audit Logs (Timeline)**. - **Policy** permissions include Kubernetes network policies only when they cover the `default` tier, where $[prodname] enforces them. -- **Modify Alerts** also reads the logs its alerts query, because an alert copies what it finds into the events it raises. **View IP Pools** and **View L2 Networks** list pods in every namespace. The permission picker says so for each. -- **Policy Recommendations** is offered as **Modify** only. +- **Alerts and Security Events Settings** is the alert configuration. **View** also reads the security events, and **Modify** also reads the logs its alerts query, because an alert copies what it finds into the events it raises. To grant the security events alone, use **View Alerts**. +- **View IP Pools** and **View L2 Networks** list pods in every namespace. +- **Tiers** and **Policy Recommendations** are offered as **Modify** only, and **Kibana**, **IP Pools**, **L2 Networks** and the log permissions as **View** only. + +Click a role in the **Roles** list to see what each of its permissions includes. A role bound to a directory group appears in the list as **Custom - IdP**. Its membership is owned by your identity provider, so its subjects cannot be edited from the console. -To change a role later, select **Actions** > **Edit Permissions**. **Delete Role** removes the role and its bindings from this cluster; subjects bound to it lose the access it granted. For a role bound to a directory group, the group stays in the directory and can be bound to a new role. +To change a role later, select **Actions** > **Edit Permissions**. Editing a manual role's **Role Name** changes only the name shown in the list; the group it binds is fixed when the role is created. **Delete Role** removes the role and its bindings from this cluster; subjects bound to it lose the access it granted. For a role bound to a directory group, the group stays in the directory and can be bound to a new role. ### See who has access -Click the user icon > **Manage Team** > **Users** to review which subjects hold which roles, and when each last signed in. Select a subject to see the permissions its roles carry. +Click the user icon > **Manage Team** > **Users** to review which subjects hold which roles, and when each last signed in. Click a subject to expand the permissions its roles carry. The tab is read-only, and lists the subjects on the roles' bindings plus the members of any bound identity provider groups. A directory group's members appear once they have signed in to the web console at least once; until then the group's role applies to them, but they are not listed. Effective permissions are the union of these roles and anything bound to the subject outside the console, which this view does not show. -To audit which group each role binds, list the bindings the console manages. - -```bash -kubectl get clusterrolebinding -l app.kubernetes.io/managed-by=calico-ui-rbac \ - -o custom-columns='NAME:.metadata.name,ROLE:.roleRef.name,GROUP:.subjects[0].name' -``` - ### Copy roles to another cluster Roles apply only to the cluster they were created on. To reuse them elsewhere, export them and apply them to a cluster that also has role management turned on. The export carries each role's bindings, not the permissions they point at, and those exist only where the console has built its catalogue. +Copy roles between standalone and management clusters, or from one managed cluster to another. A role copied between a managed cluster and any other kind of cluster loses its log and Kibana permissions. + 1. Select the source cluster, then click the user icon > **Manage Team** > **Roles** > **Export YAML**. 1. Apply the file: - For a standalone or management cluster, apply it to the target cluster. @@ -167,7 +179,7 @@ roleRef: name: calico-ui-logs-view-flows- ``` -Check first that the target cluster has the tiers and namespaces the roles were scoped to. A binding naming a tier that is missing there applies without error but grants nothing, and a binding into a namespace that does not exist is rejected. +Check first that the target cluster has the tiers and namespaces the roles were scoped to. A binding into a namespace that does not exist is rejected. A binding that names a missing tier is rejected when a `tigera-network-admin` user applies it; applied by a cluster administrator, it is accepted but grants nothing until the tier exists. The roles are independent copies: later changes on the source cluster are not propagated. From 2bb65d9d69e0dc03c0251cc9654ea40bfc8d733a Mon Sep 17 00:00:00 2001 From: Dimitri Nicolopoulos Date: Fri, 2 Oct 2026 17:05:28 -0700 Subject: [PATCH 6/6] Say who connects to the directory, and how to name an in-cluster one The directory sync runs in calico-kube-controllers, not the web console, so say "Calico Enterprise can connect only to that host". An in-cluster directory is reachable only when Authentication.spec.ldap.host names it as ..svc, so say that too. --- calico-enterprise/operations/cnx/manage-roles.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/calico-enterprise/operations/cnx/manage-roles.mdx b/calico-enterprise/operations/cnx/manage-roles.mdx index 9fba871bbd..6c7d2f18f7 100644 --- a/calico-enterprise/operations/cnx/manage-roles.mdx +++ b/calico-enterprise/operations/cnx/manage-roles.mdx @@ -95,7 +95,7 @@ stringData: baseDN: ou=groups,dc=example,dc=com ``` -The URL must be a full `ldap://` or `ldaps://` URL whose host matches `Authentication.spec.ldap.host`. The web console can connect only to that host, so a URL pointing anywhere else is accepted but no groups are found. $[prodname] picks up the secret, searches the directory with these credentials, and offers the groups it finds when you create a role. It searches again every five minutes by default. +The URL must be a full `ldap://` or `ldaps://` URL whose host matches `Authentication.spec.ldap.host`. $[prodname] can connect only to that host, so a URL pointing anywhere else is accepted but no groups are found. If the directory runs in the cluster, write `Authentication.spec.ldap.host` as `..svc`, for example `openldap.ldap.svc:389`. $[prodname] picks up the secret, searches the directory with these credentials, and offers the groups it finds when you create a role. It searches again every five minutes by default. Optionally add `groupFilter` (default `(objectClass=groupOfNames)`), `nameAttribute` (default `cn`; Active Directory typically uses `sAMAccountName`), `caBundle` for a private certificate authority, and `refreshIntervalSeconds` (default 300, clamped to 60–86400).