Secure the NVKE API
This page describes how to make the NVIDIA Kubernetes Engine (NVKE) API require a signed JSON Web Token (JWT) from an OpenID Connect (OIDC) identity provider (IdP) that you already run. Do this after you install the management-plane services and before you give anyone else access to the API.
Authentication has three modes. Off is the default.
NVKE does no authorization. Every caller that NVKE admits can call every endpoint, including the ones that create and delete workload clusters.
Warning: The NVKE API serves plain HTTP on NodePort 30080, and neither NVKE nor its chart serves it over TLS. Every bearer token a caller sends to
http://<MANAGEMENT_ADDRESS>:30080crosses the network unencrypted, and anyone who captures it can call the API until it expires. Send tokens only to a TLS endpoint that you put in front of the API, such as a reverse proxy that terminates TLS and forwards to port 30080, or admit to the API, in the NetworkPolicy from Run NVKE end to end on one machine, only clients on a trusted, isolated network.
Before you begin
-
You have completed Run NVKE end to end on one machine. You can do this before or after Configure an infrastructure provider: NICo.
-
You run the install and
kubectlcommands on the host you installed from, from the root of the samenvke/checkout, with the site spec,<CLUSTER_NAME>.kubeconfigand, if you installed from one,release.jsonin the directory above it, as in Run NVKE end to end on one machine. SetKUBECONFIGforkubectl:The install created the Helm release
nke-controllerand the Deploymentnke-controller, both in thenke-controllernamespace. -
The shell you run the install from has the
NVKECTL_*variables from Run NVKE end to end on one machine exported, as described in Run NVKE end to end on one machine. -
Your IdP publishes an OIDC discovery document over HTTPS, and the management cluster can reach its signing-key endpoint.
-
Your IdP has a group for the people and automation allowed to call the NVKE API, and puts it in a token claim named
groups, a list of strings. NVKE reads group membership only from that claim. -
Your IdP has an OIDC client for
nvkectlthat needs no client secret. -
The
kubectl oidc-loginplugin,jqandpython3are installed on the workstation you call the API from.
Collect the identity provider values
-
Read the IdP’s discovery document.
<ISSUER_URL>is your IdP’s issuer URL, such ashttps://idp.example.com/realms/example.Record the output:
issuer: use this exact string as<ISSUER_URL>. NVKE compares it with the token’sissclaim byte for byte, including any trailing slash.jwks_uriis<JWKS_URI>, where NVKE fetches the signing keys. It must usehttps.id_token_signing_alg_values_supportedlists the signing algorithms. NVKE accepts only asymmetric algorithms, such asRS256andES256.
-
Get a token from the IdP and read its claims.
<CLIENT_ID>is the ID of the OIDC client fornvkectl.kubectl oidc-loginasks you to log in to the IdP the first time.Record two claims:
audis<AUDIENCE>, the value callers must present. For a token fromkubectl oidc-loginit is usually<CLIENT_ID>.groupsmust contain the group you will admit,<ADMITTED_GROUP>. If it is missing, configure the IdP to release it. Some IdPs release it only when the client asks for a scope such asgroups; pass that with--oidc-extra-scope. Use the value exactly as the claim shows it.
Turn on authentication in permissive mode
-
Write the Helm values to a file named
auth-values.yaml, in the directory above thenvke/checkout.audiences: everyaudvalue NVKE accepts. A token is accepted if it names any of them, so list a second value for callers whose tokens carry a different audience. An audience must not equal an issuer URL.name: a label of your choice. It appears in logs and in theissuerlabel of the metrics.algorithms: the algorithms from step 1 that you accept.admit.groups: a token is admitted if itsgroupsclaim contains any listed group.
issuersis a list. Add another entry for a second IdP.nameandissuerare unique in the file. Setaudienceson an issuer to replace the top-levelaudiencesfor that issuer.NVKE uses each value exactly as written. A value with leading or trailing whitespace stops NVKE from starting.
-
If the IdP’s certificate is signed by a private certificate authority (CA), add the CA certificate to the same file. The chart places it at
/etc/nke/auth/idp-ca.crt.Merge these keys into the file from step 1:
extraFilesunderconfigMap, andcaBundlePathunder the existing issuer’sjwks, rather than adding a secondapiAuthblock or issuer. WithoutcaBundlePath, NVKE trusts the system CAs. -
Run the install command from Run NVKE end to end on one machine again, with the same flags, including any
--values-for nktp=<FILE>, and--values-for controller=../auth-values.yamladded.The wrapper reads the site spec, the stack config and the kubeconfig from
NVKE_SITESPEC,NVKE_STACK_CONFIGandNVKE_KUBECONFIG, whichenv.shexports, so the controller chart and image stay the ones you installed. The install upgrades each component in place, and appliesauth-values.yamlto the controller after the deploy path’s own values, which turn authentication off. The chart refuses to render ifconfigis set withoutcreate, or ifcaBundlePathnames a file under/etc/nke/auththatextraFilesdoes not list. -
Wait for the new pods and check the startup log.
The output contains a
fetched issuer signing keysline forexample-idpand anAPI authentication is enabledline with"mode":"permissive".If the keys line is missing, look for a
JWKS fetch failedwarning and fix the URI, the CA bundle or the network path now. In permissive mode NVKE starts without the keys, counts that issuer’s tokens asjwks_stale, and retries as they arrive. In enforcing mode it does not start.
Call the API
Configure your callers now, while NVKE still serves every request. From the moment the install creates the authentication configuration, nvkectl configured with <CLUSTER_NAME>.kubeconfig fails before it sends a request, with an error that contains minting token for nke-clients/nke-client: with that kubeconfig it requests a token for a service account that the install does not create.
With nvkectl
-
Generate a kubeconfig that gets tokens from your IdP.
The command copies the cluster address and CA from
<CLUSTER_NAME>.kubeconfigand replaces the user withkubectl oidc-login. Add--oidc-extra-scope <SCOPE>for each scope your IdP needs to release thegroupsclaim. On a machine with no browser, add--headless.kubectl oidc-loginthen prints a URL to open on another machine. The IdP client must allow the device authorization grant. -
Point
nvkectlat the API with that kubeconfig.nvkectlstores the kubeconfig path as you give it, so give an absolute path; a relative one works only from the directory you ranset-sitein.<API_URL>is the base address of the NVKE API without/api:http://<MANAGEMENT_ADDRESS>:30080, as in Run NVKE end to end on one machine, or thehttpsaddress of the TLS endpoint you put in front of it. A token sent to thehttpaddress crosses the network unencrypted; see the warning at the top of this page.nvkectl get kubeconfig,nvkectl get providers, and the machine image, cluster release and node pool release commands, such asnvkectl get nodepoolreleases, read the management cluster’s Kubernetes API directly with this kubeconfig. The bootstrap does not configure that API server to accept tokens from your IdP, so these commands fail. Usekubectlwith<CLUSTER_NAME>.kubeconfigfor them instead, as described in Access a workload cluster and Create a workload cluster. -
Call the API.
kubectl oidc-loginasks you to log in to the IdP if it has no valid token, then the command lists the workload clusters.
With curl
-
Get a token.
Automation that gets its tokens another way works the same, as long as each token carries an accepted
audand an admitted group. -
Call the API with the token in the
Authorizationheader.<API_URL>ishttp://<MANAGEMENT_ADDRESS>:30080, or thehttpsaddress of the TLS endpoint you put in front of it. Other pages write this header as-H "Authorization: Bearer <TOKEN>", where<TOKEN>is a token like the one inTOKEN.The response is the list of workload clusters in JSON.
Find callers that enforcing mode would reject
Leave NVKE in permissive mode while your existing callers keep working, and watch which of them would fail.
-
Read the authentication counter.
Requests that pass are counted with
outcome="authenticated". Eachwould_rejectseries represents requests that enforcing mode would reject. Use the logs to trace the source requests. -
Read the
reasonlabel and fix each caller. -
Find the caller behind each rejection in the log.
Each line names the
reason,issuer,method,pathandremote_addrof the request.
Continue when would_reject stops increasing.
Switch to enforcing mode
-
In
auth-values.yaml, setpermissive: false, then run the install again with the same flags as in Turn on authentication in permissive mode, step 3. -
Wait for the rollout.
In enforcing mode NVKE does not start unless it fetches the signing keys of every configured issuer. If the new pod does not become ready, its log contains
Failed to set up API authenticationwith the issuer and the error.In enforcing mode, NVKE logs each rejected request as
rejected request, with the same fields as the permissive-mode message.
NVKE reads the authentication configuration once, at startup. If you later change only apiAuth.config or apiAuth.configMap.extraFiles, restart the Deployment with kubectl -n nke-controller rollout restart deploy/nke-controller. Add a new issuer in permissive mode first, because an issuer whose keys NVKE cannot fetch stops an enforcing NVKE from starting.
The deploy path’s own values turn authentication off. Every time you run the install again, for any reason, add --values-for controller=../auth-values.yaml, along with the other inputs listed in Run NVKE end to end on one machine.
Verify
-
Check the mode the controller reports.
The output is
nvke_auth_enabled{mode="enforcing"} 1. -
Call the API without a token.
The response is
401with the messageAuthentication required: no bearer token was presented.A200means authentication is still off or permissive.Test with
/api/clusters, not/api/version./healthz,/readyz,/api/version,/openapi.yaml,/docsand the API documentation under/api/docs/need no token, so a successful call to them tells you nothing about authentication. -
Call the API with a token from a user who is not in
<ADMITTED_GROUP>. The response is403with the messageNot admitted: the authenticated identity matches no admission rule.