@@ -253,6 +253,220 @@ vault write auth/oidc/role/your_default_role \
2532531. Save.
2542541. Visit Credentials. Select Client ID and Secret and note the generated secret.
255255
256+ ## Kubernetes
257+
258+ Kubernetes can function as an OIDC provider such that Vault can validate its
259+ service account tokens using JWT/OIDC auth.
260+
261+ -> **Note:** The JWT auth engine does **not** use Kubernetes' `TokenReview` API
262+ during authentication, and instead uses public key cryptography to verify the
263+ contents of JWTs. This means tokens that have been revoked by Kubernetes will
264+ still be considered valid by Vault until their expiry time. To mitigate this
265+ risk, use short TTLs for service account tokens or use
266+ [Kubernetes auth](/docs/auth/kubernetes) which _does_ use the `TokenReview` API.
267+
268+ ### Using service account issuer discovery
269+
270+ When using service account issuer discovery, you only need to provide the JWT
271+ auth mount with an OIDC discovery URL, and sometimes a TLS certificate authority
272+ to trust. This makes it the most straightforward method to configure if your
273+ Kubernetes cluster meets the requirements.
274+
275+ Kubernetes cluster requirements:
276+
277+ * [`ServiceAccountIssuerDiscovery`][k8s-sa-issuer-discovery] feature enabled.
278+ * Present from 1.18, defaults to enabled from 1.20.
279+ * kube-apiserver's `--service-account-issuer` flag is set to a URL that is
280+ reachable from Vault. Public by default for most managed Kubernetes solutions.
281+ * Must use short-lived service account tokens when logging in.
282+ * Tokens mounted into pods default to short-lived from 1.21.
283+
284+ Configuration steps:
285+
286+ 1. Ensure OIDC discovery URLs do not require authentication, as detailed
287+ [here][k8s-sa-issuer-discovery]:
288+
289+ ```bash
290+ kubectl create clusterrolebinding oidc-reviewer \
291+ --clusterrole=system:service-account-issuer-discovery \
292+ --group=system:unauthenticated
293+ ```
294+
295+ 1. Find the issuer URL of the cluster.
296+
297+ ```bash
298+ kubectl proxy &
299+ ISSUER="$(curl --fail --silent --show-error 127.0.0.1:8001/.well-known/openid-configuration | jq -r '.issuer')"
300+
301+ # Kill the background proxy process when you're done
302+ kill %%
303+ ```
304+
305+ 1. Enable and configure JWT auth in Vault.
306+
307+ 1. If Vault is running in Kubernetes:
308+
309+ ```bash
310+ kubectl exec vault-0 -- vault auth enable jwt
311+ kubectl exec vault-0 -- vault write auth/jwt/config \
312+ oidc_discovery_url=https://kubernetes.default.svc.cluster.local \
313+ oidc_discovery_ca_pem=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
314+ ```
315+
316+ 1. Alternatively, if Vault is _not_ running in Kubernetes:
317+
318+ -> **Note:** When Vault is outside the cluster, the `$ISSUER` endpoint below may
319+ or may not be reachable. If not, you can configure JWT auth using
320+ [`jwt_validation_pubkeys`](#using-jwt-validation-public-keys) instead.
321+
322+ ```bash
323+ vault auth enable jwt
324+ vault write auth/jwt/config oidc_discovery_url="${ISSUER}"
325+ ```
326+
327+ 1. Configure a role and log in as detailed [below](#creating-a-role-and-logging-in).
328+
329+ [k8s-sa-issuer-discovery]: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-issuer-discovery
330+
331+ ### Using JWT validation public keys
332+
333+ This method can be useful if Kubernetes' API is not reachable from Vault or if
334+ you would like a single JWT auth mount to service multiple Kubernetes clusters
335+ by chaining their public signing keys.
336+
337+ Kubernetes cluster requirements:
338+
339+ * [`ServiceAccountIssuerDiscovery`][k8s-sa-issuer-discovery] feature enabled.
340+ * Present from 1.18, defaults to enabled from 1.20.
341+ * This requirement can be avoided if you can access the Kubernetes master
342+ nodes to read the public signing key directly from disk at
343+ `/etc/kubernetes/pki/sa.pub`. In this case, you can skip the steps to
344+ retrieve and then convert the key as it will already be in PEM format.
345+ * Must use short-lived service account tokens when logging in.
346+ * Tokens mounted into pods default to short-lived from 1.21.
347+
348+ Configuration steps:
349+
350+ 1. Fetch the service account signing public key from your cluster's JWKS URI.
351+
352+ ```bash
353+ # 1. Find the issuer URL of the cluster.
354+ kubectl proxy &
355+ ISSUER="$(curl --fail --silent --show-error 127.0.0.1:8001/.well-known/openid-configuration | jq -r '.issuer')"
356+
357+ # 2. Query the jwks_uri specified in /.well-known/openid-configuration
358+ # NB: You may need to run this from a pod within the cluster if the $ISSUER
359+ # URL is not available outside the cluster.
360+ curl "$(curl --fail --silent --show-error "${ISSUER}/.well-known/openid-configuration" | jq -r '.jwks_uri')"
361+
362+ # Kill the background proxy process when you're done
363+ kill %%
364+ ```
365+
366+ 1. Convert the keys from JWK format to PEM. You can use a CLI tool or an online
367+ converter such as [this one][jwk-to-pem].
368+
369+ 1. Configure the JWT auth mount with those public keys.
370+
371+ ```bash
372+ vault write auth/jwt/config \
373+ jwt_validation_pubkeys="-----BEGIN PUBLIC KEY-----
374+ MIIBIjANBgkqhkiG9...
375+ -----END PUBLIC KEY-----","-----BEGIN PUBLIC KEY-----
376+ MIIBIjANBgkqhkiG9...
377+ -----END PUBLIC KEY-----"
378+ ```
379+
380+ 1. Configure a role and log in as detailed [below](#creating-a-role-and-logging-in).
381+
382+ [jwk-to-pem]: https://8gwifi.org/jwkconvertfunctions.jsp
383+
384+ ### Creating a role and logging in
385+
386+ Once your JWT auth mount is configured, you're ready to configure a role and
387+ log in.
388+
389+ 1. Create a role for JWT auth that the `default` service account from the
390+ `default` namespace can use. The audience of tokens defaults to the same as
391+ the issuer, but it is configurable.
392+
393+ ```bash
394+ vault write auth/jwt/role/my-role \
395+ role_type="jwt" \
396+ bound_audiences="${ISSUER}" \
397+ user_claim="sub" \
398+ bound_subject="system:serviceaccount:default:default" \
399+ policies="default" \
400+ ttl="1h"
401+ ```
402+
403+ 1. Pods or other clients with access to a service account JWT can then log in.
404+
405+ ```bash
406+ vault write auth/jwt/login \
407+ role=my-role \
408+ jwt=@/var/run/secrets/kubernetes.io/serviceaccount/token
409+ # OR equivalent to:
410+ curl \
411+ --fail \
412+ --request PUT \
413+ --header "X-Vault-Request: true" \
414+ --data '{"jwt":"<JWT-TOKEN-HERE>","role":"my-role"}' \
415+ "${VAULT_ADDR}/v1/auth/jwt/login"
416+ ```
417+
418+ ### Specifying TTL and audience
419+
420+ If you would like to specify a custom TTL or audience for service account tokens,
421+ the following pod spec illustrates a volume mount that overrides the default
422+ admission injected token. This is especially relevant if you are unable to
423+ disable the [--service-account-extend-token-expiration][k8s-extended-tokens]
424+ flag for `kube-apiserver` and want to use short TTLs.
425+
426+ When using the resulting token, you will need to set `bound_audiences=vault`
427+ when creating roles in Vault's JWT auth mount.
428+
429+ ```yaml
430+ apiVersion: v1
431+ kind: Pod
432+ metadata:
433+ name: nginx
434+ spec:
435+ # automountServiceAccountToken is redundant in this example because the
436+ # mountPath overlapping with the default path below will already stop the
437+ # default admission injected token from being created. Use this option if you
438+ # choose a different mount path.
439+ automountServiceAccountToken: false
440+ containers:
441+ - name: nginx
442+ image: nginx
443+ volumeMounts:
444+ - name: custom-token
445+ mountPath: /var/run/secrets/kubernetes.io/serviceaccount
446+ volumes:
447+ - name: custom-token
448+ projected:
449+ defaultMode: 420
450+ sources:
451+ - serviceAccountToken:
452+ path: token
453+ expirationSeconds: 600 # 10 minutes is the minimum TTL
454+ audience: vault
455+ - configMap:
456+ name: kube-root-ca.crt
457+ items:
458+ - key: ca.crt
459+ path: ca.crt
460+ - downwardAPI:
461+ items:
462+ - fieldRef:
463+ apiVersion: v1
464+ fieldPath: metadata.namespace
465+ path: namespace
466+ ```
467+
468+ [k8s-extended-tokens]: https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/#options
469+
256470## Okta
257471
2584721. Make sure an Authorization Server has been created. The "Issuer" field shown on the Setting page
0 commit comments