NKP login with Microsoft Entra ID: from app registration to group-based access
Tested onNKP 2.19Kubernetes 1.36.2Microsoft Entra ID FreeLab date On a different version, check the compatibility matrix and the release notes before applying.

Summary: the lab’s NKP 2.19 now accepts sign-in with Microsoft Entra ID accounts, and Entra’s sign-in policies, such as MFA, now apply to the dashboard and to
kubectlas well. From saving the Identity Provider to the first Entra login took 12 minutes. Access is granted to groups, never to individuals: three Entra groups became three NKP profiles, tested with real users. Ana administers the whole platform, Bruno operates only his workspace’s cluster and Carla works only in the project namespace. A fourth account, with no group, authenticates and sees nothing. Everything worked with Entra ID on the free license. The post shows the configuration on both sides, the right roles for each profile and the best practices that got each one ready.
The result in one table
| User (Entra group) | NKP roles | In the dashboard | In kubectl (services cluster) |
|---|---|---|---|
Ana (nkp-plataforma-admins) | Global: Cluster Admin Role + dkp-kommander-admin | Everything: 4 workspaces, 2 clusters, Administration | can-i '*' '*' = yes; sees all 6 nodes |
Bruno (nkp-servicos-operadores) | Workspace: custom role “Cluster Operator (full)” + Kommander Workspace Admin | Only the services workspace and his cluster | create deployments in all namespaces = yes |
Carla (nkp-servicos-devs) | Global: dkp-kommander-view · Workspace: Kommander Workspace View · Project: App Deployer + Kommander Project Edit | Only the services workspace and her project | yes in the project namespace, no in default, nodes forbidden |
| Eva (no group) | None | Authenticates in Entra and gets “Not Authorized” | — |
| NKP local admin | Local password | Full dashboard the whole time | Certificate kubeconfig |
The local admin and the certificate kubeconfigs kept working throughout the configuration. That’s the emergency (break-glass) access in case the Entra login becomes unavailable.
How identity works in NKP
NKP uses Dex, an OpenID Connect provider that runs only once, on the management cluster. Workload clusters don’t have their own Dex: each one gets a kube-oidc-proxy (for kubectl) and a traefik-forward-auth (for the web UIs), which trust the central Dex.
Three practical consequences:
- Entra handles authentication (password, MFA, account lockout). NKP handles authorization (what each group can do).
- The API servers don’t change. I checked in the lab: none of them has
--oidc-*flags. OIDC comes in through kube-oidc-proxy, so there’s no control plane restart. - In NKP, permissions are granted to groups. The bindings screen itself says so: “Both groups and roles must be defined before setting up cluster role bindings”. The order is always: Entra group → NKP group → role → binding.
NKP editions
Login through Dex (RBAC and single sign-on) is in every edition: Starter, Pro and Ultimate. Workspaces and projects, used in Bruno’s and Carla’s scenarios, are part of NKP Ultimate, the lab’s license. On Pro and Starter, you get the same result with native Kubernetes RBAC, applying the bindings cluster by cluster.
What to prepare first
| Item | Why | How I checked it in the lab |
|---|---|---|
| An Entra ID tenant | NKP talks to one tenant | Entra ID Free: app registration, groups and Security Defaults (MFA) are included |
| Verified domain (optional) | Sign-ins with the company domain | lab.leimar.dev verified with a TXT record |
| Users with the Email attribute filled in | NKP uses the email as the username; without it, the login doesn’t complete | Each user’s profile → Contact information → Email |
| Security groups in Entra | The token carries the groups | 4 groups, one per profile |
Outbound access from management to login.microsoftonline.com and graph.microsoft.com (443) | It’s the Dex pod that calls Microsoft, not the browser | Test pod in the kommander namespace: 20 out of 20 HTTP 200 responses |
| Emergency access | If the Entra login fails, you’re not locked out | NKP local admin + certificate kubeconfig on both clusters |
| Synchronized clock (NTP) | Tokens have a lifetime; a wrong clock gets the token rejected | Bastion and API servers within the same second |


Do this first: test outbound access from the management cluster
A temporary pod in the kommander namespace running curl against https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration tells you in seconds whether the network is right. Do it before opening the NKP form: a DNS or proxy block shows up here, not in the middle of the first login.
1. The app in Entra ID
NKP needs an app registration in Entra, which becomes Dex’s OIDC “client”.
- App registrations → New registration: name
nkp-dex, Accounts in this organizational directory only. - Redirect URI (Web):
https://<nkp-address>/dex/callback. In the lab,https://nkp.lab.example.local/dex/callback. The browser resolves the internal name; Entra only requireshttps.

- Token configuration:
- Add groups claim → Security groups, Group ID format in the ID token;
- Add optional claim (ID token) →
emailandupn.

- API permissions → Microsoft Graph, delegated permissions:
openid,profile,email,offline_access,User.ReadandGroupMember.Read.All. Then Grant admin consent.

- Certificates & secrets → New client secret. Write down the expiration date and set a reminder 30 days before it.

Best practice: grant admin consent
Without admin consent, every user sees, on their first login, a Microsoft screen asking permission for “basic profile” and “continuous access”. With consent granted on behalf of the organization, that screen goes away for everyone.
Best practice: copy the secret’s “Value”, not the “Secret ID”
The Certificates & secrets screen shows two similar columns. NKP needs the Value, which is shown only once, right after you create the secret. Store it straight in the password vault. If the login comes back with AADSTS7000215 invalid client secret, this is almost always the cause.
Write down three values for the next step: the Tenant ID, the app’s Client ID and the Object ID of each group. Entra sends the group’s ID in the token, not its name.

2. Entra ID as the Identity Provider in NKP
In Kommander, logged in as the local admin: Identity Providers → Add Identity Provider → OIDC.
| Field | Value | Detail |
|---|---|---|
| Name | Entra ID | Becomes the button text on the login screen |
| Issuer URL | https://login.microsoftonline.com/<tenant-id>/v2.0 | |
| Client ID | the app’s Client ID | |
| Client Secret | the secret’s Value | |
| Username Key | email | The user shows up in NKP by email |
| Get User Info | checked | |
| Insecure Enable Groups | checked | Without it, Dex drops the groups claim |
| Insecure Skip Email Verified | checked | Entra doesn’t send the email_verified claim |
| Workspaces | all |

In this screenshot, the Client Secret field got the Entra Secret ID instead of the Value: that’s the detail from the tip in step 1. In the lab, the fix was the Identity Provider’s Edit, which leaves the Client ID and Client Secret fields empty to keep the previous values.
Before saving, check the Redirect URI the form shows: it must be exactly the one registered in the Entra app. If NKP shows an IP and Entra has the name, the login comes back with AADSTS50011.

The full setup in Kommander, in 31 seconds (no audio, identifiers hidden):

The NKP dashboard lives at /dkp/kommander/dashboard. There, the login screen gets the Log in with Entra ID button, next to the local login:
| Before | After |
|---|---|
![]() | ![]() |
The first test is with Eva, who isn’t in any group. She signs in with email and password in Entra and lands back in NKP with “Not Authorized”. That’s the expected result, and it proves the separation: authenticated is not authorized. This test also validates the secret, the redirect and the issuer in one go.

Best practice: one user per private window
The browser’s private session lasts as long as any private window is open. Before testing another user, close them all and open a new one. On Entra’s “Stay signed in?” screen, answer No. Otherwise, the next user’s test reuses the previous user’s session.
3. The NKP groups: oidc: + Object ID
Identity Providers → Groups → Create Group. For each Entra group, one NKP group with the same name (it stays readable) and, under Identity Provider Groups, the value oidc:<group Object ID>. The oidc: prefix is what NKP adds to groups that arrive through the OIDC connector, and the screen itself points this out.


An NKP group on its own grants no permissions at all. Access comes from the binding, at one of three levels: global (all clusters), workspace (a team’s clusters) or project (a namespace distributed to the workspace’s clusters).
4. Platform admin: Ana
Global Access Control → Cluster Role Bindings → group nkp-plataforma-admins:
- Cluster Admin Role: admin on every managed cluster;
- dkp-kommander-admin: admin of the Kommander dashboard.

The two go together. With only dkp-kommander-admin, Ana got past the dashboard authorization, but the screen stayed blank: the dashboard queries the clusters with the permissions of whoever is logged in, and the role on the clusters was missing. With both, the full dashboard showed up in under 5 minutes, the time it takes NKP to federate the role to the clusters.
| Only dkp-kommander-admin | Cluster Admin Role + dkp-kommander-admin |
|---|---|
![]() | ![]() |
Ana’s sign-in through Entra ID, from the button to the full dashboard (no audio, internal names hidden):

5. Workspace operator: Bruno
Bruno operates the services workspace’s cluster and shouldn’t see the rest of the platform. The binding is on the workspace: workspace selector → Access Control → Cluster Role Bindings → group nkp-servicos-operadores. The first choice was the Workspace Cluster Admin Role + Kommander Workspace Admin Role:

The binding in the dashboard, with that first choice of roles:

In the dashboard, the result was as expected from the very first configuration: Bruno’s workspace selector lists only the services workspace, without Global and without the others.

Bruno’s sign-in, landing straight in his workspace:

In kubectl, the choice of role matters. The workspace role list has about 30 similar names. In NKP 2.19, the default Workspace Cluster Admin Role is an aggregated role: it has no rules of its own and receives those of the roles labeled aggregate-to-cluster-admin. On the cluster, that meant access to the Cluster API resources and the dashboard URLs, but not to the core Kubernetes resources: can-i create deployments returned no and get nodes returned Forbidden. Ana’s global admin role, by contrast, has explicit * rules.
With that default role, Bruno signs in through Entra, but kubectl answers no and Forbidden (recorded before the fix; the kubeconfig commands, which contain the token, were cut):

The solution was a custom Workspace Role, created in the dashboard (Access Control → Roles → Create Role), of type Cluster Role, with two rules: all resources and verbs, and all nonResourceURLs. I named it “Cluster Operator (full)”.

The group’s final binding swapped the Workspace Cluster Admin Role for that role and kept the Kommander Workspace Admin Role (the workspace dashboard). NKP federated the new role to the cluster in under 1 minute:
# node names and Object ID anonymized$ kubectl auth whoamiATTRIBUTE VALUEUsername [email protected]Groups [oidc:<object-id-do-grupo> system:authenticated]$ kubectl auth can-i create deployments --all-namespacesyes$ kubectl get nodesNAME STATUS ROLES AGE VERSIONcluster-servicos-control-plane-1 Ready control-plane 3d2h v1.36.2cluster-servicos-control-plane-2 Ready control-plane 3d2h v1.36.2cluster-servicos-control-plane-3 Ready control-plane 3d2h v1.36.2cluster-servicos-worker-1 Ready <none> 3d2h v1.36.2cluster-servicos-worker-2 Ready <none> 3d2h v1.36.2cluster-servicos-worker-3 Ready <none> 3d2h v1.36.2Best practice: test the role before handing over access
As an admin, ask the cluster while impersonating the group: kubectl auth can-i create deployments -A --as=teste --as-group='oidc:<object-id>'. That’s how the aggregated role showed up in seconds, without depending on the user’s login.
6. Project developer: Carla
Carla works only in one project’s namespace. In the services workspace: Projects → Create Project (app-demo, placed only on the services cluster). NKP creates the app-demo-mkgxc namespace on the cluster. In the project, the nkp-servicos-devs group gets App Deployer (creates and changes workloads) + Kommander Project Edit (the project in the dashboard).

For a project-only developer to use the dashboard, it takes three bindings, one at each level:
| Level | Role | What it unlocks |
|---|---|---|
| Global | dkp-kommander-view | The front door: the dashboard URL (/dkp/kommander/dashboard) |
| Workspace | Kommander Workspace View | Reading the workspace and the project list |
| Project | App Deployer + Kommander Project Edit | Working in the project namespace |
Workspace roles already include the front door; project roles don’t. That’s why, with only the project binding, Carla got “Not Authorized”; with the door but without workspace read access, the screen stayed blank. With all three, she sees the services workspace and her project:

$ kubectl auth can-i create deployments -n app-demo-mkgxcyes$ kubectl auth can-i create deployments -n defaultno$ kubectl get nodesError from server (Forbidden): nodes is forbidden: User "[email protected]" cannot list resource "nodes" in API group "" at the cluster scope$ kubectl create deployment podinfo --image=ghcr.io/stefanprodan/podinfo:6.7.1 -n app-demo-mkgxcdeployment.apps/podinfo created$ kubectl get pods -n app-demo-mkgxcNAME READY STATUS RESTARTS AGEpodinfo-7f5848f5f6-7ztzk 1/1 Running 0 1sBest practice: least privilege at the front door
dkp-kommander-view grants read access to /dkp/*. For a more restricted developer profile, create an NKP Role with only /dkp/kommander/dashboard* and use it instead.
7. kubectl with the Entra login
In the dashboard, the user menu has Generate Token: pick the cluster and copy the commands that build the kubeconfig. The token is issued by Dex with the Entra groups and is valid for 24 hours (in Ana’s token, exp − iat = 86,400 s). The kubeconfig points to the chosen cluster’s kube-oidc-proxy.


Ana’s whole flow, from Generate Token to kubectl get nodes (the kubeconfig commands, which contain the token, were cut):

Best practice: one kubeconfig file per user
The Generate Token commands write to the active kubeconfig. Before pasting them, run export KUBECONFIG=~/.kube/<usuario>.conf. That way your admin ~/.kube/config stays intact.
The numbers
| Measure | Value |
|---|---|
| From saving the Identity Provider to the first Entra login | 12 min |
| Entra ID license used | Free (MFA through Security Defaults) |
| Outbound access from management to Microsoft (test pod) | 20 out of 20 HTTP 200; DNS in 3–10 ms |
| Delegated Graph permissions on the app | 6, with admin consent |
| Workspace roles available in the dashboard | ~30 |
| Federation of a global role to the clusters | < 5 min |
| Federation of the custom Workspace Role | < 1 min |
| Generate Token token lifetime | 24 h |
| Bindings for a project-only developer to use the dashboard | 3 (global, workspace, project) |
What I learned
- Authentication and authorization live in different places. Entra says who the person is (with MFA); NKP says what their group can do. Eva proves it in the first test.
- Groups, always. The token carries the Object ID; NKP uses
oidc:<ID>. Joining or leaving a group in Entra changes access from the next token on, without touching NKP. - Dashboard roles and cluster roles go together. The dashboard queries the clusters with the permissions of whoever is logged in.
- Check what a role grants before using it. In NKP 2.19, the default Workspace Cluster Admin Role is aggregated; for an operator with full cluster access, use a custom Workspace Role.
- A project developer needs three levels. The global front door, workspace read access and work in the project.
- Keep emergency access. Local admin and certificate kubeconfig, stored in a vault, before touching the login.
Checklist: NKP login with Entra ID
- Certificate kubeconfig and local admin password stored in the vault (break-glass)
- Outbound access from management to
login.microsoftonline.comandgraph.microsoft.comtested from a pod - Entra users with the Email attribute filled in
- Security groups in Entra, one per profile, with the Object IDs written down
- App registration with a Redirect URI identical to the one in the NKP form
- Groups claim (Group ID) and optional claims
emailandupn - Delegated permissions with
GroupMember.Read.Alland admin consent - Client secret Value in the vault and a reminder 30 days before expiration
- OIDC Identity Provider with Username Key
email, Insecure Enable Groups and Insecure Skip Email Verified - Test with an account with no group: it authenticates and gets “Not Authorized”
- NKP groups with
oidc:<Object ID> - Bindings tested with
kubectl auth can-i --as-groupbefore granting access - One kubeconfig per user (
export KUBECONFIG) when using Generate Token
What I haven’t tested yet
kubectl-only access, without the dashboard, for an Entra user who shouldn’t get into Kommander (thek8s-servicos-kubectlgroup already exists in the lab’s Entra). For exceptions, access with a certificate issued by the cluster itself, bound to one namespace, is covered in NKP teams with native RBAC and quotas.- User offboarding: how long access remains after disabling the account in Entra, with the 24-hour token.
- MFA on every login with Conditional Access (Entra ID P1). With Security Defaults, Entra prompts for MFA based on risk evaluation, not on every login.
- The same profiles on a cluster with a Pro or Starter license. The recipe with native Kubernetes RBAC (namespace, RoleBinding for the group and quota), which does not depend on workspaces or projects, was tested in the lab and is covered in NKP teams with native RBAC and quotas.
- Rotating the client secret before it expires, without interrupting logins.
References
Nutanix
- Configure NKP single sign-on with Microsoft Entra ID (nutanix.dev)
- Integrating NKP with Microsoft Active Directory via LDAP (nutanix.dev)
- Nutanix Kubernetes Platform: datasheet and editions
- Nutanix Kubernetes Platform Guide 2.19, Identity Providers and Access Control sections (Nutanix portal)
Microsoft
Projects and community




Comments
Comments use GitHub Discussions; you need a GitHub account.