Skip to content

Kubernetes Auth Method

原文: https://www.vaultproject.io/docs/auth/kubernetes

kubernetes auth 方法可用於使用 Kubernetes 服務帳戶(Service account)令牌向 Vault 進行身份驗證。這種身份驗證方法可以輕鬆地將 Vault 令牌引入 Kubernetes Pod。

您還可以使用 Kubernetes 服務帳戶令牌通過 JWT 身份驗證登錄。請參閱如何使用短期 Kubernetes 令牌部分,了解您可能希望使用 JWT 身份驗證的原因以及它與 Kubernetes 身份驗證的比較。

Info

注意:如果您要升級到 Kubernetes v1.21+,請確保配置選項 disable_iss_validation 設置為 true。假設默認掛載路徑,您可以使用 vault read -field disable_iss_validation auth/kubernetes/config 檢查。有關更多詳細信息,請參閱下面的 Kubernetes 1.21

Authentication

Via the CLI

默認路徑是 /kubernetes。如果在不同的路徑上啟用了此身份驗證方法,請在 CLI 中指定 -path=/my-path

$ vault write auth/kubernetes/login role=demo jwt=...

Via the API

默認端點是 auth/kubernetes/login。如果在不同的路徑上啟用了此身份驗證方法,請使用該值而不是 kubernetes。

$ curl \
    --request POST \
    --data '{"jwt": "<your service account jwt>", "role": "demo"}' \
    http://127.0.0.1:8200/v1/auth/kubernetes/login

響應將在 auth.client_token 包含一個令牌:

{
  "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

在用戶或機器可以進行身份驗證之前,必須提前在 Vault 配置身份驗證方法。這些步驟通常由 Vault 管理員或配置管理工具完成。

  1. 啟用 Kubernetes 身份驗證方法:
$ vault auth enable kubernetes

使用 /config 端點配置 Vault 以與 Kubernetes 通信。使用 kubectl cluster-info 驗證 Kubernetes 主機地址和 TCP 端口。有關可用配置選項的列表,請參閱 API 文檔。

$ 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

創建 name role:

$ vault write auth/kubernetes/role/demo \
    bound_service_account_names=vault-auth \
    bound_service_account_namespaces=default \
    policies=default \
    ttl=1h

此角色授權默認命名空間中的 vault-auth 服務帳戶,並為其提供默認策略。

有關配置選項的完整列表,請參閱 API 文檔

Kubernetes 1.21

從版本 1.21 開始,Kubernetes BoundServiceAccountTokenVolume 功能默認為啟用。默認情況下,這會以兩種對 Kubernetes 身份驗證很重要的方式更改掛載到容器中的 JWT 令牌:

  • 它有一個過期時間,並且綁定到 pod 和服務帳戶的生命週期。
  • JWT 的 iss 聲明的值取決於集群的配置。

配置 token_reviewer_jwt 選項時,對令牌生命週期的更改很重要。如果使用短期令牌,Kubernetes 將在 pod 或服務帳戶被刪除或過期時間過後立即撤銷它,並且 Vault 將無法再使用 TokenReview API

為了響應 iss 的更改,Kubernetes auth 已在 Vault 1.9.0 中更新,默認情況下不驗證頒發者。 Kubernetes API 在審查令牌時執行相同的驗證,因此在 Vault 端啟用頒發者驗證是重複的工作。

在不禁用 Vault 的頒發者驗證的情況下,單個 Kubernetes 身份驗證配置無法同時用於 Kubernetes 1.20 和 1.21 的默認掛載 pod 令牌。請注意,在 Vault 1.9 之前創建的身份驗證掛載將保持舊的默認值,您需要在將 Kubernetes 升級到 1.21 之前顯式設置 disable_iss_validation=true。如果您希望在 Vault 中啟用頒發者驗證,請參閱下面的發現服務帳戶頒發者以獲取指導。

如何克服 short-lived Kubernetes tokens 帶來的問題

當默認掛載的 pod 令牌是短暫的時,有幾種不同的方法可以為 Kubernetes pod 配置身份驗證,每種方法都有自己的權衡。此表總結了選項,下面將更詳細地解釋每個選項。

Option All tokens are short-lived Can revoke tokens early Other considerations
Use local token as reviewer JWT Yes Yes Requires Vault (1.9.3+) to be deployed on the Kubernetes cluster
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

Use local service account token as the reviewer JWT

在 Kubernetes pod 中運行 Vault 時,推薦的選項是使用 pod 的本地服務帳戶令牌。 Vault 將定期重新讀取文件以支持短期令牌。要使用本地令牌和 CA 證書,請在配置 auth 方法時省略 token_reviewer_jwtkubernetes_ca_cert。 Vault 將嘗試分別從默認掛載文件夾 /var/run/secrets/kubernetes.io/serviceaccount/ 中的 tokenca.crt 加載它們。

$ vault write auth/kubernetes/config \
    kubernetes_host=https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT

Warning

注意:需要 Vault 1.9.3+。在早期版本中,服務帳戶令牌和 CA 證書被讀取一次並存儲在 Vault 存儲中。當服務帳戶令牌過期或被撤銷時,Vault 將無法再使用 TokenReview API,並且客戶端身份驗證將失敗。

Use the Vault client's JWT as the reviewer JWT

配置 Kubernetes auth 時,可以省略 token_reviewer_jwt,Vault 在與 Kubernetes TokenReview API 通信時會使用 Vault 客戶端的 JWT 作為自己的 auth token。如果 Vault 在 Kubernetes 中運行,您還需要設置 disable_local_ca_jwt=true

這意味著 Vault 不存儲任何 JWT 並允許您在任何地方使用短期令牌,但會增加一些操作開銷來維護您希望能夠使用 Vault 進行身份驗證的服務帳戶集上的集群角色綁定。 Vault 的每個客戶端都需要 system:auth-delegator ClusterRole:

$ kubectl create clusterrolebinding vault-client-auth-delegator \
  --clusterrole=system:auth-delegator \
  --group=group1 \
  --serviceaccount=default:svcaccount1 \
  ...

Continue using long-lived tokens

您可以使用此處的說明創建一個長期存在的機密並將其用作 token_reviewer_jwt。在此示例中,vault 服務帳戶需要 system:auth-delegator ClusterRole:

$ kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: vault-k8s-auth-secret
  annotations:
    kubernetes.io/service-account.name: vault
type: kubernetes.io/service-account-token
EOF

使用它可以維護以前的工作流程,但不會從短期令牌的改進安全狀態中受益。

Use JWT auth

Kubernetes auth 專門用於使用 Kubernetes 的 TokenReview API。但是,Kubernetes 生成的 JWT 令牌也可以使用 Kubernetes 作為 OIDC 提供程序進行驗證。 JWT auth 方法文檔包含使用 Kubernetes 作為 OIDC 提供程序設置 JWT auth 的說明。

此解決方案允許您為所有客戶端使用短期令牌,並且無需審查者 JWT。但是,客戶端令牌在其 TTL 到期之前不能被撤銷,因此建議在記住該限制的情況下保持 TTL 較短。

發現 Service account issuer

Info

注意:從 Vault 1.9.0 開始,disable_iss_validationissuer 已被棄用,並且 disable_iss_validation 的默認值已更改為 true 以用於新的 Kubernetes 身份驗證掛載。以下部分僅適用於您已設置 disable_iss_validation=false 或在 1.9 之前使用默認值創建掛載,但 disable_iss_validation=true 是所有 Vault 版本的新推薦值。

Kubernetes 1.21+ 集群可能需要將服務帳戶頒發者設置為與 kube-apiserver 的 --service-account-issuer 標誌相同的值。這是因為這些集群的服務帳戶 JWT 可能具有特定於集群本身的頒發者,而不是舊默認的 kubernetes/serviceaccount。如果您無法直接檢查此值,則可以運行以下命令並查找 iss字段以找到所需的值:

echo '{"apiVersion": "authentication.k8s.io/v1", "kind": "TokenRequest"}' \
  | kubectl create -f- --raw /api/v1/namespaces/default/serviceaccounts/default/token \
  | jq -r '.status.token' \
  | cut -d . -f2 \
  | base64 -D

大多數集群還將在 .well-known/openid-configuration 端點上提供該信息:

$ kubectl get --raw /.well-known/openid-configuration | jq -r .issuer

然後在配置 Kubernetes 身份驗證時使用此值,例如:

$ vault write auth/kubernetes/config \
  kubernetes_host="https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT" \
  issuer="\"test-aks-cluster-dns-d6cbb78e.hcp.uksouth.azmk8s.io\""
{
  "issuer": "https://kubernetes.default.svc.cluster.local",
  "jwks_uri": "https://192.168.49.2:8443/openid/v1/jwks",
  "response_types_supported": [
    "id_token"
  ],
  "subject_types_supported": [
    "public"
  ],
  "id_token_signing_alg_values_supported": [
    "RS256"
  ]
}

配置 Kubernetes

此身份驗證方法訪問 Kubernetes TokenReview API 以驗證提供的 JWT 是否仍然有效。 Kubernetes 應該使用 --service-account-lookup 運行。這在 Kubernetes 1.7 中默認為 true。否則 Kubernetes 中已刪除的令牌將無法正確撤銷,並且能夠通過此身份驗證方法進行身份驗證。

此身份驗證方法中使用的服務帳戶需要有權訪問 TokenReview API。如果 Kubernetes 配置為使用 RBAC 角色,則應授予服務帳戶訪問此 API 的權限。以下示例 ClusterRoleBinding 可用於授予這些權限:

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

Kubernetes Auth 插件具有完整的 HTTP API。有關更多詳細信息,請參閱 API 文檔

代碼示例

以下示例演示了使用 Vault 進行身份驗證的 Kubernetes auth 方法。

package main

import (
    "fmt"
    "os"

    vault "github.com/hashicorp/vault/api"
  auth "github.com/hashicorp/vault/api-docs/auth/kubernetes"
)

// Fetches a key-value secret (kv-v2) after authenticating to Vault with a Kubernetes service account.
// For a more in-depth setup explanation, please see the relevant readme 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, from the default mount path for KV v2 in dev mode, "secret"
    secret, err := client.KVv2("secret").Get(context.Background(), "creds")
    if err != nil {
        return "", fmt.Errorf("unable to read secret: %w", err)
    }

    // data map can contain more than one key-value pair,
    // in this case we're just grabbing one of them
    value, ok := secret.Data["password"].(string)
    if !ok {
        return "", fmt.Errorf("value type assertion failed: %T %#v", secret.Data["password"], secret.Data["password"])
    }

    return value, nil
}