Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs to clarify k8s auth options with short-lived tokens #13275

Merged
merged 22 commits into from
Dec 8, 2021
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 203 additions & 0 deletions website/content/docs/auth/jwt/oidc_providers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,209 @@ vault write auth/oidc/role/your_default_role \
1. Save.
1. Visit Credentials. Select Client ID and Secret and note the generated secret.

## Kubernetes
tomhjp marked this conversation as resolved.
Show resolved Hide resolved

Kubernetes can function as an OIDC provider such that Vault can validate its
service account tokens using JWT/OIDC auth.

-> **Note:** The JWT auth engine does **not** use Kubernetes' `TokenReview` API
during authentication, and instead uses public key cryptography to verify the
contents of JWTs. This means tokens that have been revoked by Kubernetes will
still be considered valid by Vault until their expiry time. To mitigate this
risk, use short TTLs for service account tokens or use
[Kubernetes auth](/docs/auth/kubernetes) which _does_ use the `TokenReview` API.

### Using service account issuer discovery

When using service account issuer discovery, you only need to provide the JWT
auth mount with an OIDC discovery URL, and sometimes a TLS certificate authority
to trust. This makes it the most straightforward method to configure if your
Kubernetes cluster meets the requirements.

Kubernetes cluster requirements:

* [`ServiceAccountIssuerDiscovery`][k8s-sa-issuer-discovery] feature enabled.
* Present from 1.18, defaults to enabled from 1.20.
* kube-apiserver's `--service-account-issuer` flag is set to a URL that is
reachable from Vault. Public by default for most managed Kubernetes solutions.
* Must use short-lived service account tokens when logging in.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the default TTL for these short-lived tokens?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a little complicated - nominally 3607 seconds, but as mentioned elsewhere there's currently an extension introduced by Kubernetes to ease the transition to short-lived tokens, so the default pod tokens actually last for a year (unless the associated pod or service account is deleted first).

* Tokens mounted into pods default to short-lived from 1.21.

Configuration steps:

1. Ensure OIDC discovery URLs do not require authentication, as detailed
tomhjp marked this conversation as resolved.
Show resolved Hide resolved
[here][k8s-sa-issuer-discovery]:

```bash
kubectl create clusterrolebinding oidc-reviewer \
--clusterrole=system:service-account-issuer-discovery \
--group=system:unauthenticated
```

1. Find the issuer URL of the cluster.
calvn marked this conversation as resolved.
Show resolved Hide resolved

```bash
timeout 30s kubectl proxy &
ISSUER="$(curl --fail --silent --show-error 127.0.0.1:8001/.well-known/openid-configuration | jq -r '.issuer')"
tomhjp marked this conversation as resolved.
Show resolved Hide resolved
```

1. Enable and configure JWT auth in Vault.

1. If Vault is running in Kubernetes:
tomhjp marked this conversation as resolved.
Show resolved Hide resolved

```bash
kubectl exec vault-0 -- vault auth enable jwt
kubectl exec vault-0 -- vault write auth/jwt/config \
oidc_discovery_url=https://kubernetes.default.svc.cluster.local \
oidc_discovery_ca_pem=@/run/secrets/kubernetes.io/serviceaccount/ca.crt
tomhjp marked this conversation as resolved.
Show resolved Hide resolved
```

1. Alternatively, if Vault is _not_ running in Kubernetes:
tomhjp marked this conversation as resolved.
Show resolved Hide resolved

-> **Note:** When Vault is outside the cluster, the `$ISSUER` endpoint below may
or may not be reachable. If not, you can configure JWT auth using
[`jwt_validation_pubkeys`](#using-jwt-validation-public-keys) instead.

```bash
vault auth enable jwt
vault write auth/jwt/config oidc_discovery_url="${ISSUER}"
```

1. Configure a role and log in as detailed [below](#creating-a-role-and-logging-in).

[k8s-sa-issuer-discovery]: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-issuer-discovery

### Using JWT validation public keys

This method can be useful if Kubernetes' API is not reachable from Vault or if
you would like a single JWT auth mount to service multiple Kubernetes clusters
by chaining their public signing keys.

Kubernetes cluster requirements:

* [`ServiceAccountIssuerDiscovery`][k8s-sa-issuer-discovery] feature enabled.
* Present from 1.18, defaults to enabled from 1.20.
tomhjp marked this conversation as resolved.
Show resolved Hide resolved
* This requirement can be avoided if you can access the Kubernetes master
nodes to read the public signing key directly from disk at
`/etc/kubernetes/pki/sa.pub`. In this case, you can skip the steps to
retrieve and then convert the key as it will already be in PEM format.
* Must use short-lived service account tokens when logging in.
* Tokens mounted into pods default to short-lived from 1.21.

Configuration steps:

1. Fetch the service account signing public key from your cluster's JWKS URI.
tomhjp marked this conversation as resolved.
Show resolved Hide resolved

```bash
# 1. Find the issuer URL of the cluster.
timeout 30s kubectl proxy &
tomhjp marked this conversation as resolved.
Show resolved Hide resolved
ISSUER="$(curl --fail --silent --show-error 127.0.0.1:8001/.well-known/openid-configuration | jq -r '.issuer')"

# 2. Query the jwks_uri specified in /.well-known/openid-configuration
# NB: You may need to run this from a pod within the cluster if the $ISSUER
# URL is not available outside the cluster.
curl "$(curl --fail --silent --show-error "${ISSUER}/.well-known/openid-configuration" | jq -r '.jwks_uri')"
```

1. Convert the keys from JWK format to PEM. You can use a CLI tool or an online
austingebauer marked this conversation as resolved.
Show resolved Hide resolved
converter such as [this one][jwk-to-pem].

1. Configure the JWT auth mount with those public keys.

```bash
vault write auth/jwt/config \
jwt_validation_pubkeys="-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9...
-----END PUBLIC KEY-----","-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9...
-----END PUBLIC KEY-----"
```

1. Configure a role and log in as detailed [below](#creating-a-role-and-logging-in).

[jwk-to-pem]: https://8gwifi.org/jwkconvertfunctions.jsp
tomhjp marked this conversation as resolved.
Show resolved Hide resolved

### Creating a role and logging in

Once your JWT auth mount is configured, you're ready to configure a role and
log in.

1. Create a role for JWT auth that the `default` service account from the
`default` namespace can use. The audience of tokens defaults to the same as
the issuer, but it is configurable.

```bash
vault write auth/jwt/role/my-role \
role_type="jwt" \
bound_audiences="${ISSUER}" \
user_claim="sub" \
bound_subject="system:serviceaccount:default:default" \
policies="default" \
ttl="1h"
```

1. Pods or other clients with access to a service account JWT can then log in.

```bash
vault write auth/jwt/login \
role=my-role \
jwt=@/var/run/secrets/kubernetes.io/serviceaccount/token
# OR equivalent to:
curl \
--fail \
--request PUT \
tomhjp marked this conversation as resolved.
Show resolved Hide resolved
--header "X-Vault-Request: true" \
--data '{"jwt":"<JWT-TOKEN-HERE>","role":"my-role"}' \
"${VAULT_ADDR}/v1/auth/jwt/login"
```

### Specifying TTL and audience

If you would like to specify a custom TTL or audience for service account tokens,
the following pod spec illustrates a volume mount that overrides the default
admission injected token. This is especially relevant if you are unable to
disable the [--service-account-extend-token-expiration][k8s-extended-tokens]
flag for `kube-apiserver` and want to use short TTLs.

When using the resulting token, you will need to set `bound_audiences=vault`
when creating roles in Vault's JWT auth mount.

```yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: custom-token
mountPath: /var/run/secrets/kubernetes.io/serviceaccount
volumes:
- name: custom-token
projected:
defaultMode: 420
sources:
- serviceAccountToken:
path: token
expirationSeconds: 600 # 10 minutes is the minimum TTL
audience: vault
- configMap:
name: kube-root-ca.crt
items:
- key: ca.crt
path: ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
```

[k8s-extended-tokens]: https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/#options

## Okta

1. Make sure an Authorization Server has been created. The "Issuer" field shown on the Setting page
Expand Down
Loading