mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-17 20:17:00 +02:00
* Rework 1.21 content into one heading and add note at top * Add notes about extended k8s token duration * Add example of ClusterRoleBinding for using client JWTs
465 lines
18 KiB
Plaintext
465 lines
18 KiB
Plaintext
---
|
||
layout: docs
|
||
page_title: Kubernetes - Auth Methods
|
||
description: |-
|
||
The Kubernetes auth method allows automated authentication of Kubernetes
|
||
Service Accounts.
|
||
---
|
||
|
||
# Kubernetes Auth Method
|
||
|
||
The `kubernetes` auth method can be used to authenticate with Vault using a
|
||
Kubernetes Service Account Token. This method of authentication makes it easy to
|
||
introduce a Vault token into a Kubernetes Pod.
|
||
|
||
You can also use a Kubernetes Service Account Token to [log in via JWT auth][k8s-jwt-auth].
|
||
See the section on [How to work with short-lived Kubernetes tokens][short-lived-tokens]
|
||
for a summary of why you might want to use JWT auth instead and how it compares to
|
||
Kubernetes auth.
|
||
|
||
-> **Note:** If you are upgrading to Kubernetes v1.21+, ensure the config option
|
||
`disable_iss_validation` is set to true. Assuming the default mount path, you
|
||
can check with `vault read -field disable_iss_validation auth/kubernetes/config`.
|
||
See [Kubernetes 1.21](#kubernetes-1.21) below for more details.
|
||
|
||
## Authentication
|
||
|
||
### Via the CLI
|
||
|
||
The default path is `/kubernetes`. If this auth method was enabled at a
|
||
different path, specify `-path=/my-path` in the CLI.
|
||
|
||
```shell-session
|
||
$ vault write auth/kubernetes/login role=demo jwt=...
|
||
```
|
||
|
||
### Via the API
|
||
|
||
The default endpoint is `auth/kubernetes/login`. If this auth method was enabled
|
||
at a different path, use that value instead of `kubernetes`.
|
||
|
||
```shell-session
|
||
$ curl \
|
||
--request POST \
|
||
--data '{"jwt": "<your service account jwt>", "role": "demo"}' \
|
||
http://127.0.0.1:8200/v1/auth/kubernetes/login
|
||
```
|
||
|
||
The response will contain a token at `auth.client_token`:
|
||
|
||
```json
|
||
{
|
||
"auth": {
|
||
"client_token": "38fe9691-e623-7238-f618-c94d4e7bc674",
|
||
"accessor": "78e87a38-84ed-2692-538f-ca8b9f400ab3",
|
||
"policies": ["default"],
|
||
"metadata": {
|
||
"role": "demo",
|
||
"service_account_name": "vault-auth",
|
||
"service_account_namespace": "default",
|
||
"service_account_secret_name": "vault-auth-token-pd21c",
|
||
"service_account_uid": "aa9aa8ff-98d0-11e7-9bb7-0800276d99bf"
|
||
},
|
||
"lease_duration": 2764800,
|
||
"renewable": true
|
||
}
|
||
}
|
||
```
|
||
|
||
## Configuration
|
||
|
||
Auth methods must be configured in advance before users or machines can
|
||
authenticate. These steps are usually completed by an operator or configuration
|
||
management tool.
|
||
|
||
1. Enable the Kubernetes auth method:
|
||
|
||
```bash
|
||
vault auth enable kubernetes
|
||
```
|
||
|
||
1. Use the `/config` endpoint to configure Vault to talk to Kubernetes. Use
|
||
`kubectl cluster-info` to validate the Kubernetes host address and TCP port.
|
||
For the list of available configuration options, please see the
|
||
[API documentation](/api/auth/kubernetes).
|
||
|
||
```bash
|
||
vault write auth/kubernetes/config \
|
||
token_reviewer_jwt="<your reviewer service account JWT>" \
|
||
kubernetes_host=https://192.168.99.100:<your TCP port or blank for 443> \
|
||
kubernetes_ca_cert=@ca.crt
|
||
```
|
||
|
||
!> **Note:** The pattern Vault uses to authenticate Pods depends on sharing
|
||
the JWT token over the network. Given the [security model of
|
||
Vault](/docs/internals/security), this is allowable because Vault is
|
||
part of the trusted compute base. In general, Kubernetes applications should
|
||
**not** share this JWT with other applications, as it allows API calls to be
|
||
made on behalf of the Pod and can result in unintended access being granted
|
||
to 3rd parties.
|
||
|
||
1. Create a named role:
|
||
|
||
```text
|
||
vault write auth/kubernetes/role/demo \
|
||
bound_service_account_names=vault-auth \
|
||
bound_service_account_namespaces=default \
|
||
policies=default \
|
||
ttl=1h
|
||
```
|
||
|
||
This role authorizes the "vault-auth" service account in the default
|
||
namespace and it gives it the default policy.
|
||
|
||
For the complete list of configuration options, please see the [API
|
||
documentation](/api/auth/kubernetes).
|
||
|
||
## Kubernetes 1.21
|
||
|
||
Starting in version [1.21][k8s-1.21-changelog], the Kubernetes
|
||
`BoundServiceAccountTokenVolume` feature defaults to enabled. This changes the
|
||
JWT token mounted into containers by default in two ways that are important for
|
||
Kubernetes auth:
|
||
|
||
* It has an expiry time and is bound to the lifetime of the pod and service account.
|
||
* The value of the JWT's `"iss"` claim depends on the cluster's configuration.
|
||
|
||
The changes to token lifetime are important when configuring the
|
||
[`token_reviewer_jwt`](/api-docs/auth/kubernetes#token_reviewer_jwt) option. You
|
||
must avoid using a short-lived token because Vault stores that token in Vault
|
||
storage and does not automatically refresh it. If a short-lived token is used,
|
||
Kubernetes will revoke it as soon as the pod or service account are deleted, or
|
||
if the expiry time passes, and Vault will no longer be able to use the
|
||
`TokenReview` API. See [How to work with short-lived Kubernetes tokens][short-lived-tokens]
|
||
below for details on handling this change.
|
||
|
||
In response to the issuer changes, Kubernetes auth has been updated in Vault
|
||
1.9.0 to not validate the issuer by default. The Kubernetes API does the same
|
||
validation when reviewing tokens, so enabling issuer validation on the Vault
|
||
side is duplicated work. Without disabling Vault's issuer validation, it is not
|
||
possible for a single Kubernetes auth configuration to work for default mounted
|
||
pod tokens with both Kubernetes 1.20 and 1.21. Note that auth mounts created
|
||
before Vault 1.9 will maintain the old default, and you will need to explicitly
|
||
set `disable_iss_validation=true` before upgrading Kubernetes to 1.21. See
|
||
[Discovering the service account `issuer`](#discovering-the-service-account-issuer)
|
||
below for guidance if you wish to enable issuer validation in Vault.
|
||
|
||
[k8s-1.21-changelog]: https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.21.md#api-change-2
|
||
[short-lived-tokens]: #how-to-work-with-short-lived-kubernetes-tokens
|
||
|
||
### How to work with short-lived Kubernetes tokens
|
||
|
||
There are a few different ways to configure auth for Kubernetes pods when
|
||
default mounted pod tokens are short-lived, each with their own tradeoffs. This
|
||
table summarizes the options, each of which is explained in more detail below.
|
||
|
||
| Option | All tokens are short-lived | Can revoke tokens early | Other considerations |
|
||
| ------------------------------------ | -------------------------- | ----------------------- | -------------------- |
|
||
| Use client JWT as reviewer JWT | Yes | Yes | Operational overhead |
|
||
| Use long-lived token as reviewer JWT | No | Yes | |
|
||
| Use JWT auth instead | Yes | No | |
|
||
|
||
-> **Note:** By default, Kubernetes currently extends the lifetime of admission
|
||
injected service account tokens to a year to help smooth the transition to
|
||
short-lived tokens. If you would like to disable this, set
|
||
[--service-account-extend-token-expiration=false][k8s-extended-tokens] for
|
||
`kube-apiserver` or specify your own `serviceAccountToken` volume mount. See
|
||
[here](/docs/auth/jwt/oidc_providers#specifying-ttl-and-audience) for an example.
|
||
|
||
[k8s-extended-tokens]: https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/#options
|
||
|
||
#### Use the Vault client's JWT as the reviewer JWT
|
||
|
||
When configuring Kubernetes auth, you can omit the `token_reviewer_jwt`, and Vault
|
||
will use the Vault client's JWT as its own auth token when communicating with
|
||
the Kubernetes `TokenReview` API. If Vault is running in Kubernetes, you also need
|
||
to set `disable_local_ca_jwt=true`.
|
||
|
||
This means Vault does not store any JWTs and allows you to use short-lived tokens
|
||
everywhere but adds some operational overhead to maintain the cluster role
|
||
bindings on the set of service accounts you want to be able to authenticate with
|
||
Vault. Each client of Vault would need the `system:auth-delegator` ClusterRole:
|
||
|
||
```bash
|
||
kubectl create clusterrolebinding vault-client-auth-delegator \
|
||
--clusterrole=system:auth-delegator \
|
||
--group=group1 \
|
||
--serviceaccount=default:svcaccount1 \
|
||
...
|
||
```
|
||
|
||
#### Continue using long-lived tokens
|
||
|
||
The default Kubernetes secret created for a service account is still long lived,
|
||
and can be used as the `token_reviewer_jwt` without needing to refresh it. To
|
||
find the secret, run:
|
||
|
||
```bash
|
||
kubectl get secret "$(kubectl get serviceaccount default -o jsonpath='{.secrets[0].name}')"
|
||
```
|
||
|
||
Using this maintains previous workflows but does not fully take advantage of the
|
||
new default short-lived tokens.
|
||
|
||
#### Use JWT auth
|
||
|
||
Kubernetes auth is specialized to use Kubernetes' `TokenReview` API. However, the
|
||
JWT tokens Kubernetes generates can also be verified using Kubernetes as an OIDC
|
||
provider. The JWT auth method documentation has [instructions][k8s-jwt-auth] for
|
||
setting up JWT auth with Kubernetes as the OIDC provider.
|
||
|
||
[k8s-jwt-auth]: /docs/auth/jwt/oidc_providers#kubernetes
|
||
|
||
This solution allows you to use short-lived tokens for all clients and removes
|
||
the need for a reviewer JWT. However, the client tokens cannot be revoked before
|
||
their TTL expires, so it is recommended to keep the TTL short with that
|
||
limitation in mind.
|
||
|
||
### Discovering the service account `issuer`
|
||
|
||
-> **Note:** From Vault 1.9.0, `disable_iss_validation` and `issuer` are deprecated
|
||
and the default for `disable_iss_validation` has changed to `true` for new
|
||
Kubernetes auth mounts. The following section only applies if you have set
|
||
`disable_iss_validation=false` or created your mount before 1.9 with the default
|
||
value, but `disable_iss_validation=true` is the new recommended value for all
|
||
versions of Vault.
|
||
|
||
Kubernetes 1.21+ clusters may require setting the service account
|
||
[`issuer`](/api-docs/auth/kubernetes#issuer) to the same value as
|
||
`kube-apiserver`'s `--service-account-issuer` flag. This is because the service
|
||
account JWTs for these clusters may have an issuer specific to the cluster
|
||
itself, instead of the old default of `kubernetes/serviceaccount`. If you are
|
||
unable to check this value directly, you can run the following and look for the
|
||
`"iss"` field to find the required value:
|
||
|
||
```bash
|
||
kubectl proxy &
|
||
curl --silent http://127.0.0.1:8001/api/v1/namespaces/default/serviceaccounts/default/token \
|
||
-H "Content-Type: application/json" \
|
||
-X POST \
|
||
-d '{"apiVersion": "authentication.k8s.io/v1", "kind": "TokenRequest"}' \
|
||
| jq -r '.status.token' \
|
||
| cut -d . -f2 \
|
||
| base64 -D
|
||
|
||
# Kill the background proxy process when you're done
|
||
kill %%
|
||
```
|
||
|
||
Most clusters will also have that information available at the
|
||
`.well-known/openid-configuration` endpoint:
|
||
|
||
```bash
|
||
kubectl proxy &
|
||
curl --silent http://127.0.0.1:8001/.well-known/openid-configuration | jq -r .issuer
|
||
|
||
# Kill the background proxy process when you're done
|
||
kill %%
|
||
```
|
||
|
||
This value is then used when configuring Kubernetes auth, e.g.:
|
||
|
||
```bash
|
||
vault write auth/kubernetes/config \
|
||
kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443" \
|
||
issuer="\"test-aks-cluster-dns-d6cbb78e.hcp.uksouth.azmk8s.io\""
|
||
```
|
||
|
||
## Configuring Kubernetes
|
||
|
||
This auth method accesses the [Kubernetes TokenReview API][k8s-tokenreview] to
|
||
validate the provided JWT is still valid. Kubernetes should be running with
|
||
`--service-account-lookup`. This is defaulted to true from Kubernetes 1.7.
|
||
Otherwise deleted tokens in Kubernetes will not be properly revoked and
|
||
will be able to authenticate to this auth method.
|
||
|
||
Service Accounts used in this auth method will need to have access to the
|
||
TokenReview API. If Kubernetes is configured to use RBAC roles, the Service
|
||
Account should be granted permissions to access this API. The following
|
||
example ClusterRoleBinding could be used to grant these permissions:
|
||
|
||
```yaml
|
||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||
kind: ClusterRoleBinding
|
||
metadata:
|
||
name: role-tokenreview-binding
|
||
namespace: default
|
||
roleRef:
|
||
apiGroup: rbac.authorization.k8s.io
|
||
kind: ClusterRole
|
||
name: system:auth-delegator
|
||
subjects:
|
||
- kind: ServiceAccount
|
||
name: vault-auth
|
||
namespace: default
|
||
```
|
||
|
||
## API
|
||
|
||
The Kubernetes Auth Plugin has a full HTTP API. Please see the
|
||
[API docs](/api/auth/kubernetes) for more details.
|
||
|
||
[k8s-tokenreview]: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#tokenreview-v1-authentication-k8s-io
|
||
|
||
## Code Example
|
||
|
||
The following code snippet demonstrates the Kubernetes auth method to authenticate
|
||
with Vault.
|
||
|
||
<CodeTabs heading="kubernetes auth example">
|
||
|
||
<CodeBlockConfig lineNumbers>
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
|
||
vault "github.com/hashicorp/vault/api"
|
||
auth "github.com/hashicorp/vault/api/auth/kubernetes"
|
||
)
|
||
|
||
// Fetches a key-value secret (kv-v2) after authenticating to Vault with a Kubernetes service account.
|
||
//
|
||
// As the client, all we need to do is pass along the JWT token representing our application's Kubernetes Service Account in our login request to Vault.
|
||
//
|
||
// For a more in-depth setup explanation, please see the full version of this code in the hashicorp/vault-examples repo.
|
||
func getSecretWithKubernetesAuth() (string, error) {
|
||
// If set, the VAULT_ADDR environment variable will be the address that
|
||
// your pod uses to communicate with Vault.
|
||
config := vault.DefaultConfig() // modify for more granular configuration
|
||
|
||
client, err := vault.NewClient(config)
|
||
if err != nil {
|
||
return "", fmt.Errorf("unable to initialize Vault client: %w", err)
|
||
}
|
||
|
||
// The service-account token will be read from the path where the token's
|
||
// Kubernetes Secret is mounted. By default, Kubernetes will mount it to
|
||
// /var/run/secrets/kubernetes.io/serviceaccount/token, but an administrator
|
||
// may have configured it to be mounted elsewhere.
|
||
// In that case, we'll use the option WithServiceAccountTokenPath to look
|
||
// for the token there.
|
||
k8sAuth, err := auth.NewKubernetesAuth(
|
||
"dev-role-k8s",
|
||
auth.WithServiceAccountTokenPath("path/to/service-account-token"),
|
||
)
|
||
if err != nil {
|
||
return "", fmt.Errorf("unable to initialize Kubernetes auth method: %w", err)
|
||
}
|
||
|
||
authInfo, err := client.Auth().Login(context.TODO(), k8sAuth)
|
||
if err != nil {
|
||
return "", fmt.Errorf("unable to log in with Kubernetes auth: %w", err)
|
||
}
|
||
if authInfo == nil {
|
||
return "", fmt.Errorf("no auth info was returned after login")
|
||
}
|
||
|
||
// get secret from Vault
|
||
secret, err := client.Logical().Read("kv-v2/data/creds")
|
||
if err != nil {
|
||
return "", fmt.Errorf("unable to read secret: %w", err)
|
||
}
|
||
|
||
data, ok := secret.Data["data"].(map[string]interface{})
|
||
if !ok {
|
||
return "", fmt.Errorf("data type assertion failed: %T %#v", secret.Data["data"], secret.Data["data"])
|
||
}
|
||
|
||
// data map can contain more than one key-value pair,
|
||
// in this case we're just grabbing one of them
|
||
key := "password"
|
||
value, ok := data[key].(string)
|
||
if !ok {
|
||
return "", fmt.Errorf("value type assertion failed: %T %#v", data[key], data[key])
|
||
}
|
||
|
||
return value, nil
|
||
}
|
||
```
|
||
</CodeBlockConfig>
|
||
|
||
<CodeBlockConfig lineNumbers>
|
||
|
||
```cs
|
||
using System;
|
||
using System.IO;
|
||
using VaultSharp;
|
||
using VaultSharp.V1.AuthMethods;
|
||
using VaultSharp.V1.AuthMethods.Kubernetes;
|
||
using VaultSharp.V1.Commons;
|
||
|
||
namespace Examples
|
||
{
|
||
public class KubernetesAuthExample
|
||
{
|
||
const string DefaultTokenPath = "path/to/service-account-token";
|
||
|
||
// Fetches a key-value secret (kv-v2) after authenticating to Vault with a Kubernetes service account.
|
||
//
|
||
// As the client, all we need to do is pass along the JWT token representing our application's Kubernetes Service Account in our login request to Vault.
|
||
// This token is automatically mounted to your application's container by Kubernetes. Read more at https://www.vaultproject.io/docs/auth/kubernetes
|
||
//
|
||
// SETUP NOTES: If an operator has not already set up Kubernetes auth in Vault for you, then you must also first configure the Vault server with its own Service Account token to be able to communicate with the Kubernetes API
|
||
// so it can verify that the client's service-account token is valid. The service account that will be performing that verification needs the ClusterRole system:auth-delegator.
|
||
//
|
||
// export TOKEN_REVIEW_JWT=$(kubectl get secret $TOKEN_REVIEWER_SECRET --output='go-template={{ .data.token }}' | base64 --decode)
|
||
// export KUBE_HOST=$(kubectl config view --raw --minify --flatten --output='jsonpath={.clusters[].cluster.server}')
|
||
// kubectl config view --raw --minify --flatten --output='jsonpath={.clusters[].cluster.certificate-authority-data}' | base64 --decode > path/to/kube_ca_cert
|
||
//
|
||
// vault write auth/kubernetes/config \
|
||
// token_reviewer_jwt=${TOKEN_REVIEW_JWT} \
|
||
// kubernetes_host=${KUBE_HOST} \
|
||
// kubernetes_ca_cert=@path/to/kube_ca_cert \
|
||
// issuer="kubernetes/serviceaccount"
|
||
//
|
||
// The "issuer" field is normally only required when running Kubernetes 1.21 or above, and may differ from the default value above:
|
||
// https://www.vaultproject.io/docs/auth/kubernetes#discovering-the-service-account-issuer.
|
||
//
|
||
// Finally, make sure to create a role in Vault bound to your pod's service account:
|
||
//
|
||
// vault write auth/kubernetes/role/dev-role-k8s \
|
||
// policies="dev-policy" \
|
||
// bound_service_account_names="my-app" \
|
||
// bound_service_account_namespaces="default"
|
||
public string GetSecretWithK8s()
|
||
{
|
||
var vaultAddr = Environment.GetEnvironmentVariable("VAULT_ADDR");
|
||
if(String.IsNullOrEmpty(vaultAddr))
|
||
{
|
||
throw new System.ArgumentNullException("Vault Address");
|
||
}
|
||
|
||
var roleName = Environment.GetEnvironmentVariable("VAULT_ROLE");
|
||
if(String.IsNullOrEmpty(roleName))
|
||
{
|
||
throw new System.ArgumentNullException("Vault Role Name");
|
||
}
|
||
|
||
// Get the path to service account token or fall back on default path
|
||
string pathToToken = String.IsNullOrEmpty(Environment.GetEnvironmentVariable("SA_TOKEN_PATH")) ? DefaultTokenPath : Environment.GetEnvironmentVariable("SA_TOKEN_PATH");
|
||
string jwt = File.ReadAllText(pathToToken);
|
||
|
||
IAuthMethodInfo authMethod = new KubernetesAuthMethodInfo(roleName, jwt);
|
||
var vaultClientSettings = new VaultClientSettings(vaultAddr, authMethod);
|
||
|
||
IVaultClient vaultClient = new VaultClient(vaultClientSettings);
|
||
|
||
// We can retrieve the secret after creating our VaultClient object
|
||
Secret<SecretData> kv2Secret = null;
|
||
kv2Secret = vaultClient.V1.Secrets.KeyValue.V2.ReadSecretAsync(path: "/creds").Result;
|
||
|
||
var password = kv2Secret.Data.Data["password"];
|
||
|
||
return password.ToString();
|
||
}
|
||
}
|
||
}
|
||
```
|
||
</CodeBlockConfig>
|
||
|
||
</CodeTabs>
|