diff --git a/changelog/15527.txt b/changelog/15527.txt new file mode 100644 index 000000000..2dfb63f1b --- /dev/null +++ b/changelog/15527.txt @@ -0,0 +1,3 @@ +```release-note:improvement +website/docs: Add usage documentation for Kubernetes Secrets Engine +``` diff --git a/website/content/docs/secrets/kubernetes.mdx b/website/content/docs/secrets/kubernetes.mdx new file mode 100644 index 000000000..8be2974a1 --- /dev/null +++ b/website/content/docs/secrets/kubernetes.mdx @@ -0,0 +1,343 @@ +--- +layout: docs +page_title: Kubernetes - Secrets Engines +description: >- + The Kubernetes secrets engine for Vault generates Kubernetes service account + tokens, service accounts, role bindings, and roles dynamically. +--- + +# Kubernetes Secrets Engine + +The Kubernetes Secrets Engine for Vault generates Kubernetes service account tokens, and +optionally service accounts, role bindings, and roles. The created service account tokens have +a configurable TTL and any objects created are automatically deleted when the Vault lease expires. + +For each lease, Vault will create a service account token attached to the +defined service account. The service account token is returned to the caller. + +To learn more about service accounts in Kubernetes, visit the +[Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) +and [Kubernetes RBAC](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) +documentation. + +~> **Note:** We do not recommend using tokens created by the Kubernetes Secrets Engine to + authenticate with the [Vault Kuberenetes Auth Method](/docs/auth/kubernetes). This will + generate many unique identities in Vault that will be hard to manage. + +## Setup + +The Kubernetes Secrets Engine must be configured in advance before it +can perform its functions. These steps are usually completed by an operator or configuration +management tool. + +1. By default, Vault will connect to Kubernetes using its own service account. + If using the [standard Helm chart](https://github.com/hashicorp/vault-helm), this service account + is created automatically by default and named after the Helm release (often `vault`, but this can be + configured via the Helm value `server.serviceAccount.name`). + + It's necessary to ensure that the service account Vault uses will have permissions to manage + service account tokens, and optionally manage service accounts, roles, and role bindings. These + permissions can be managed using a Kuberentes role or cluster role. The role is attached to the + Vault service account with a role binding or cluster role binding. + + For example, a minimal cluster role to create service account tokens is: + ```yaml + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + name: k8s-minimal-secrets-abilities + rules: + - apiGroups: [""] + resources: ["serviceaccounts/token"] + verbs: ["create"] + ``` + + Similarly, you can create a more permissive cluster role with full permissions to manage tokens, + service accounts, bindings, and roles. + + ```yaml + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + name: k8s-full-secrets-abilities + rules: + - apiGroups: [""] + resources: ["serviceaccounts", "serviceaccounts/token"] + verbs: ["create", "update", "delete"] + - apiGroups: ["rbac.authorization.k8s.io"] + resources: ["rolebindings", "clusterrolebindings"] + verbs: ["create", "update", "delete"] + - apiGroups: ["rbac.authorization.k8s.io"] + resources: ["roles", "clusterroles"] + verbs: ["bind", "escalate", "create", "update", "delete"] + ``` + + Create this role in Kubernetes (e.g., with `kubectl apply -f`). + + ~> **Note:** Getting the right permissions for Vault will require some trial and error most + likely since Kubernetes has strict protections against privilege escalation. You can read more + in the + [Kubernetes RBAC documentation](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping). + + ~> **Note:** Protect the Vault service account, especially if you use broader permissions for it, + as it is essentially a cluster administrator account. + +1. Create a role binding to bind the role to Vault's service account and grant Vault permission + to manage tokens. + + ```yaml + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + name: vault-token-creator-binding + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: k8s-minimal-secrets-abilities + subjects: + - kind: ServiceAccount + name: vault + namespace: vault + ``` + + For more information on Kubernetes roles, service accounts, bindings, and tokens, visit the + [Kubernetes RBAC documentation](https://kubernetes.io/docs/reference/access-authn-authz/rbac/). + +1. If Vault will not be automatically managing roles or service accounts (see + [Automatically Managing Roles and Service Accounts](#automatically-managing-roles-and-service-accounts)), + then you will need to set up a service account that Vault will issue tokens for. + + ~> **Note**: It is highly recommended that the service account that Vault issues tokens for + is **NOT** the same service account that Vault itself uses. + + The examples we will use will under the namespace `test`, which you can create if it does not + already exist. + + ```shell-session + $ kubectl create namespace test + namespace/test created + ``` + + Here is a simple set up of a service account, role, and role binding in the Kubernetes `test` + namespace with basic permissions we will use for this document: + ```yaml + apiVersion: v1 + kind: ServiceAccount + metadata: + name: test-service-account-with-generated-token + namespace: test + --- + apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + name: test-role-list-pods + namespace: test + rules: + - apiGroups: [""] + resources: ["pods"] + verbs: ["list"] + --- + apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + name: test-role-abilities + namespace: test + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: test-role-list-pods + subjects: + - kind: ServiceAccount + name: test-service-account-with-generated-token + namespace: test + ``` + + You can create these objects with `kubectl apply -f`. + +1. Enable the Kubernetes Secrets Engine: + + ```shell-session + $ vault secrets enable kubernetes + Success! Enabled the kubernetes Secrets Engine at: kubernetes/ + ``` + + By default, the secrets engine will mount at the same name as the engine, i.e., + `kubernetes/` here. This can be changed by passing the `-path` argument when enabling. + +1. Configure the mount point. An empty config is allowed. + + ```shell-session + $ vault write -f kubernetes/config + ``` + + Configuration options are available as specified in the + [API docs](/api-docs/secret/kubernetes). + +1. You can now configure Kubernetes Secrets Engine to create a Vault role (**not** the same as a + Kubernetes role) that can generate service account tokens for the given service account: + + ```shell-session + $ vault write kubernetes/roles/my-role \ + allowed_kubernetes_namespaces="*" \ + service_account_name="test-service-account-with-generated-token" \ + token_default_ttl="10m" + ``` + +## Generating Credentials + +After a user has authenticated to Vault and has sufficient permissions, a write to the +`creds` endpoint for the Vault role will generate and return a new service account token. + +```shell-session +$ vault write kubernetes/creds/my-role \ + kubernetes_namespace=test + +Key Value +–-- ----- +lease_id kubernetes/creds/my-role/31d771a6-... +lease_duration 10m0s +lease_renwable false +service_account_name test-service-account-with-generated-token +service_account_namespace test +service_account_token eyJHbGci0iJSUzI1NiIsImtpZCI6ImlrUEE... +``` + +You can use the service account token above (`eyJHbG...`) with any Kubernetes API request that +its service account is authorized for (through role bindings). + +```shell-session +$ curl -sk $(kubectl config view --minify -o 'jsonpath={.clusters[].cluster.server}')/api/v1/namespaces/test/pods \ + --header "Authorization: Bearer eyJHbGci0iJSUzI1Ni..." +{ + "kind": "PodList", + "apiVersion": "v1", + "metadata": { + "resourceVersion": "1624" + }, + "items": [] +} +``` + +When the lease expires, you can verify that the token has been revoked. +```shell-session +$ curl -sk $(kubectl config view --minify -o 'jsonpath={.clusters[].cluster.server}')/api/v1/namespaces/test/pods \ + --header "Authorization: Bearer eyJHbGci0iJSUzI1Ni..." +{ + "kind": "Status", + "apiVersion": "v1", + "metadata": {}, + "status": "Failure", + "message": "Unauthorized", + "reason": "Unauthorized", + "code": 401 +} +``` + +## TTL + +Kubernetes service account tokens have a time-to-live (TTL). When a token expires it is +automatically revoked. + +You can set a default (`token_default_ttl`) and a maximum TTL (`token_max_ttl`) when +creating or tuning the Vault role. + +```shell-session +$ vault write kubernetes/roles/my-role \ + allowed_kubernetes_namespaces="*" \ + service_account_name="new-service-account-with-generated-token" \ + token_default_ttl="10m" \ + token_max_ttl="2h" +``` + +You can also set a TTL (`ttl`) when you generate the token from the credentials endpoint. +The TTL of the token will be given the default if not specified (and cannot exceed the +maximum TTL of the role, if present). + +```shell-session +$ vault write kubernetes/creds/my-role \ + kubernetes_namespace=test \ + ttl=20m + +Key Value +–-- ----- +lease_id kubernetes/creds/my-role/31d771a6-... +lease_duration 20m0s +lease_renwable false +service_account_name new-service-account-with-generated-token +service_account_namespace test +service_account_token eyJHbGci0iJSUzI1NiIsImtpZCI6ImlrUEE... +``` + + +You can verify the token's TTL by decoding the JWT token and extracting the `iat` +(issued at) and `exp` (expiration time) claims. + +```shell-session +$ echo 'eyJhbGc...' | cut -d'.' -f2 | base64 -d | jq -r '.iat,.exp|todate' +2022-05-20T17:14:50Z +2022-05-20T17:34:50Z +``` + +## Automatically Managing Roles and Service Accounts + +When configuring the Vault role, you can pass in parameters to specify that you want to +automatically generate the Kubernetes service account and role binding, +and optionally generate the Kubernetes role itself. + +If you want to configure the Vault role to use a pre-existing Kubernetes role, but generate +the service account and role binding automatically, you can set the `kubernetes_role_name` +parameter. + +```shell-session +$ vault write kubernetes/roles/auto-managed-sa-role \ + allowed_kubernetes_namespaces="test" \ + kubernetes_role_name="test-role-list-pods" +``` + +~> **Note**: Vault's service account will also need access to the resources it is granting +access to. This can be done for the examples above with `kubectl -n test create rolebinding --role test-role-list-pods --serviceaccount=vault:vault vault-test-role-abilities`. +This is how Kuberentes prevents privilege escalation. +You can read more in the +[Kubernetes RBAC documentation](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping). + +You can then get credentials with the automatically generated service account. +```shell-session +$ vault write kubernetes/creds/auto-managed-sa-role \ + kubernetes_namespace=test +Key Value +--- ----- +lease_id kubernetes/creds/auto-managed-sa-role/cujRLYjKZUMQk6dkHBGGWm67 +lease_duration 768h +lease_renewable false +service_account_name v-token-auto-man-1653001548-5z6hrgsxnmzncxejztml4arz +service_account_namespace test +service_account_token eyJHbGci0iJSUzI1Ni... +``` + +Furthermore, Vault can also automatically create the role in addition to the service account and +role binding by specifying the `generated_role_rules` parameter, which accepts a set of JSON or YAML +rules for the generated role. + +```shell-session +$ vault write kubernetes/roles/auto-managed-sa-and-role \ + allowed_kubernetes_namespaces="test" \ + generated_role_rules='{"rules":[{"apiGroups":[""],"resources":["pods"],"verbs":["list"]}]}' +``` +You can then get credentials in the same way as before. +```shell-session +$ vault write kubernetes/creds/auto-managed-sa-and-role \ + kubernetes_namespace=test +Key Value +--- ----- +lease_id kubernetes/creds/auto-managed-sa-and-role/pehLtegoTP8vCkcaQozUqOHf +lease_duration 768h +lease_renewable false +service_account_name v-token-auto-man-1653002096-4imxf3ytjh5hbyro9s1oqdo3 +service_account_namespace test +service_account_token eyJHbGci0iJSUzI1Ni... +``` + +## API + +The Kubernetes Secrets Engine has a full HTTP API. Please see the +[Kubernetes Secrets Engine API docs](/api-docs/secret/kubernetes) for more details. \ No newline at end of file diff --git a/website/data/docs-nav-data.json b/website/data/docs-nav-data.json index 6fc812e15..06009836d 100644 --- a/website/data/docs-nav-data.json +++ b/website/data/docs-nav-data.json @@ -1056,6 +1056,10 @@ } ] }, + { + "title": "Kubernetes", + "path": "secrets/kubernetes" + }, { "title": "Identity", "routes": [