---
title: 搭配 Kubernetes 使用 WIF
url: https://platform.claude.com/docs/zh-TW/manage-claude/wif-providers/kubernetes
description: 使用投射的服務帳戶權杖，從自行管理的 Kubernetes 叢集向 Claude API 進行身分驗證。
---

自行管理的 Kubernetes 叢集（kubeadm、k3s、OpenShift 以及內部部署發行版）會透過 [projected service account tokens](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#serviceaccount-token-volume-projection)（投射的服務帳戶權杖）為每個 pod 簽署 OIDC「JSON Web Token」（JSON 網路權杖），即 JWT。叢集的 API 伺服器充當 OIDC 簽發者（issuer），而每個權杖的 `sub` 宣告遵循 `system:serviceaccount:<namespace>:<service-account>` 的格式。您可以透過讀取叢集的探索文件（discovery document）來找到叢集的簽發者 URL：

```bash cURL
kubectl get --raw /.well-known/openid-configuration | jq -r .issuer
```

<Note>
  本頁所述的機制（投射的服務帳戶權杖、以叢集 API 伺服器作為 OIDC 簽發者）是 Kubernetes 本身原生的功能，因此它是所有 Kubernetes 發行版的基礎。如果您在託管的 Kubernetes 服務上執行，雲端供應商指南會說明在哪裡可以找到由供應商管理的簽發者 URL：[AWS (EKS)](https://platform.claude.com/docs/zh-TW/manage-claude/wif-providers/aws#use-eks-projected-service-account-tokens)、[Google Cloud (GKE)](https://platform.claude.com/docs/zh-TW/manage-claude/wif-providers/gcp) 或 [Azure (AKS)](https://platform.claude.com/docs/zh-TW/manage-claude/wif-providers/azure)。如果您的叢集執行 SPIRE，則簽發者是 SPIRE OIDC Discovery Provider，而非叢集 API 伺服器；請參閱 [SPIFFE](https://platform.claude.com/docs/zh-TW/manage-claude/wif-providers/spiffe)。對於任何其他發行版或未列於其中的託管供應商，請依照本指南操作，並使用您的叢集所回報的簽發者 URL。
</Note>

## 先決條件

* 熟悉 [WIF 概念](https://platform.claude.com/docs/zh-TW/manage-claude/workload-identity-federation#concepts)：服務帳戶、聯合簽發者（federation issuers）與聯合規則（federation rules）。

* 一個在 API 伺服器上已設定 [`--service-account-issuer`](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/) 旗標的 Kubernetes 叢集。大多數發行版預設會設定此值；kubeadm 叢集通常使用 `https://kubernetes.default.svc.cluster.local`。如果您無法直接存取 API 伺服器設定，您的平台團隊可以確認該值。

* 下列其中一項，以便 Anthropic 能夠驗證權杖簽章：

  * 簽發者的 JWKS 端點可從公開網際網路透過 HTTPS 在連接埠 443 上存取，或
  * 您可以從叢集內部擷取 JWKS，並以 `inline` 模式註冊（詳見[設定 Anthropic](https://platform.claude.com/docs/zh-TW/manage-claude/wif-providers/kubernetes#configure-anthropic)）。

* 在 Claude Console 中為您的 Anthropic 組織建立服務帳戶、聯合簽發者與聯合規則的權限。

## 設定 Kubernetes

將服務帳戶權杖投射到您的 pod 中，並使用您的聯合規則所預期的受眾（audience）與存留時間。`serviceAccountToken` 投射會將一個新的 JWT 寫入掛載路徑，並在 `expirationSeconds` 到期之前輪替它。

```yaml Pod
apiVersion: v1
kind: Pod
metadata:
  name: inference-worker
  namespace: inference
spec:
  serviceAccountName: inference-worker
  volumes:
    - name: anthropic-token
      projected:
        sources:
          - serviceAccountToken:
              audience: https://api.anthropic.com
              expirationSeconds: 3600
              path: token
  containers:
    - name: app
      image: your-registry/inference-worker:latest
      env:
        - name: ANTHROPIC_IDENTITY_TOKEN_FILE
          value: /var/run/secrets/anthropic.com/token
        - name: ANTHROPIC_FEDERATION_RULE_ID
          value: fdrl_...
        - name: ANTHROPIC_ORGANIZATION_ID
          value: 00000000-0000-0000-0000-000000000000
        - name: ANTHROPIC_SERVICE_ACCOUNT_ID
          value: svac_...
        - name: ANTHROPIC_WORKSPACE_ID  # required when the rule covers multiple workspaces
          value: wrkspc_...
      volumeMounts:
        - name: anthropic-token
          mountPath: /var/run/secrets/anthropic.com
          readOnly: true
```

為此 pod 簽發的權杖帶有 `sub: "system:serviceaccount:inference:inference-worker"` 與 `aud: ["https://api.anthropic.com"]`。

## 設定 Anthropic

在 Claude Console 中，前往 [Workload identity](https://platform.claude.com/settings/workload-identity-federation) 頁面（`platform.claude.com/settings/workload-identity-federation`），點擊 **Connect workload**，然後選擇 **Kubernetes** 圖塊。精靈會引導您完成註冊簽發者、建立服務帳戶以及建立聯合規則的步驟。

精靈會為您建立這些資源。無論您是在精靈中輸入這些值，還是將它們傳送至 [Admin API](https://platform.claude.com/docs/zh-TW/manage-claude/wif-admin-api)，請使用下列值：

**聯合簽發者：** 許多自行管理的叢集使用諸如 `https://kubernetes.default.svc.cluster.local` 之類無法從公開網際網路存取的簽發者 URL。如果您的叢集屬於這種情況，請選擇 **inline** JWKS 來源並貼上叢集的金鑰。從叢集內部擷取它們：

```bash cURL
kubectl get --raw /openid/v1/jwks
```

接著使用回傳的 `keys` 陣列內容（而非外層的 `{"keys": [...]}` 包裝）來設定簽發者：

```json
{
  "name": "onprem-k8s",
  "issuer_url": "https://kubernetes.default.svc.cluster.local",
  "jwks": {
    "type": "inline",
    "keys": [{ "kty": "RSA", "kid": "...", "n": "...", "e": "AQAB" }]
  }
}
```

在 `inline` 模式下，`issuer_url` 僅用於與 JWT 的 `iss` 宣告進行比對；Anthropic 絕不會嘗試連線至該 URL。如果您的簽發者可公開存取，請改用 `"jwks": {"type": "discovery"}`。

<Warning>
  使用 `inline` 金鑰時，當叢集輪替其服務帳戶簽署金鑰時，您必須負責更新簽發者。輪替很少發生（通常僅在叢集升級期間），但在您推送新的 JWKS 之前，權杖交換會因簽章錯誤而失敗。
</Warning>

**聯合規則：** 比對服務帳戶的 `sub` 宣告以及您在投射權杖上設定的受眾。

```json
{
  "name": "onprem-inference",
  "issuer_id": "fdis_...",
  "match": {
    "subject_prefix": "system:serviceaccount:inference:inference-worker",
    "audience": "https://api.anthropic.com"
  },
  "target": {
    "type": "service_account",
    "service_account_id": "svac_..."
  },
  "workspace_id": "wrkspc_...",
  "oauth_scope": "workspace:developer",
  "token_lifetime_seconds": 600
}
```

請在工作負載允許的範圍內盡可能具體。僅當命名空間中的每個服務帳戶都應對應到同一個 Anthropic 服務帳戶時，才將 `subject_prefix` 放寬為 `system:serviceaccount:inference:*`（結尾的 `*` 使其成為前綴比對）。將規則的 `fdrl_...` ID 加入您 pod 的 `ANTHROPIC_FEDERATION_RULE_ID` 環境變數中。

## 取得並使用權杖

[設定 Kubernetes](https://platform.claude.com/docs/zh-TW/manage-claude/wif-providers/kubernetes#configure-kubernetes) 中的 pod 規格將 `ANTHROPIC_IDENTITY_TOKEN_FILE` 設定為投射的掛載路徑，並同時設定 `ANTHROPIC_FEDERATION_RULE_ID`、`ANTHROPIC_ORGANIZATION_ID`、`ANTHROPIC_SERVICE_ACCOUNT_ID` 與 `ANTHROPIC_WORKSPACE_ID`。這些設定就緒後，SDK 會在每次交換時從磁碟讀取權杖，並自動重新整理 Anthropic 存取權杖。

<CodeGroup>
  ```bash cURL
  JWT=$(cat "$ANTHROPIC_IDENTITY_TOKEN_FILE")

  ACCESS_TOKEN=$(curl -sS https://api.anthropic.com/v1/oauth/token \
    -H "content-type: application/json" \
    --data @- <<JSON | jq -r .access_token
  {
    "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
    "assertion": "$JWT",
    "federation_rule_id": "$ANTHROPIC_FEDERATION_RULE_ID",
    "organization_id": "$ANTHROPIC_ORGANIZATION_ID",
    "service_account_id": "$ANTHROPIC_SERVICE_ACCOUNT_ID",
    "workspace_id": "$ANTHROPIC_WORKSPACE_ID"
  }
  JSON
  )

  curl https://api.anthropic.com/v1/messages \
    -H "authorization: Bearer $ACCESS_TOKEN" \
    -H "anthropic-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 1024,
      "messages": [{"role": "user", "content": "Hello, Claude"}]
    }' | jq -r '.content[] | select(.type == "text") | .text'
  ```

  ```python Python
  import anthropic

  # 讀取 ANTHROPIC_IDENTITY_TOKEN_FILE、ANTHROPIC_FEDERATION_RULE_ID、
  # ANTHROPIC_ORGANIZATION_ID、ANTHROPIC_SERVICE_ACCOUNT_ID 及 ANTHROPIC_WORKSPACE_ID
  # （取自 pod 的環境變數）。
  client = anthropic.Anthropic()

  message = client.messages.create(
      model="claude-opus-5-5",
      max_tokens=1024,
      messages=[{"role": "user", "content": "Hello, Claude"}],
  )
  print(next(block.text for block in message.content if block.type == "text"))
  ```

  ```typescript TypeScript
  import Anthropic from "@anthropic-ai/sdk";

  // 讀取 ANTHROPIC_IDENTITY_TOKEN_FILE、ANTHROPIC_FEDERATION_RULE_ID、
  // ANTHROPIC_ORGANIZATION_ID、ANTHROPIC_SERVICE_ACCOUNT_ID 與 ANTHROPIC_WORKSPACE_ID，
  // 來源為 pod 的環境變數。
  const client = new Anthropic();

  const message = await client.messages.create({
    model: "claude-opus-5-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello, Claude" }]
  });
  for (const block of message.content) {
    if (block.type === "text") {
      console.log(block.text);
    }
  }
  ```

  ```go Go
  // 讀取 ANTHROPIC_IDENTITY_TOKEN_FILE、ANTHROPIC_FEDERATION_RULE_ID、
  // ANTHROPIC_ORGANIZATION_ID、ANTHROPIC_SERVICE_ACCOUNT_ID 及 ANTHROPIC_WORKSPACE_ID
  // （取自 pod 的環境變數）。
  client := anthropic.NewClient()

  message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Hello, Claude")),
  	},
  })
  if err != nil {
  	panic(err)
  }
  for _, block := range message.Content {
  	if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
  		fmt.Println(textBlock.Text)
  		break
  	}
  }
  ```

  ```java Java
  AnthropicClient client = AnthropicOkHttpClient.fromEnv();

  var message = client.messages().create(MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(1024)
          .addUserMessage("Hello, Claude")
          .build());

  IO.println(message.content());
  ```

  ```csharp C#
  // 讀取 ANTHROPIC_IDENTITY_TOKEN_FILE、ANTHROPIC_FEDERATION_RULE_ID、
  // ANTHROPIC_ORGANIZATION_ID、ANTHROPIC_SERVICE_ACCOUNT_ID 及 ANTHROPIC_WORKSPACE_ID，
  // 來源為 pod 的環境變數。
  using var client = new AnthropicClient();

  var message = await client.Messages.Create(new()
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Messages = [new() { Role = Role.User, Content = "Hello, Claude" }],
  });
  foreach (var block in message.Content)
  {
      if (block.Value is TextBlock textBlock)
      {
          Console.WriteLine(textBlock.Text);
      }
  }
  ```

  ```bash CLI
  # 讀取 ANTHROPIC_FEDERATION_RULE_ID、ANTHROPIC_ORGANIZATION_ID、
  # ANTHROPIC_SERVICE_ACCOUNT_ID、ANTHROPIC_WORKSPACE_ID 及 ANTHROPIC_IDENTITY_TOKEN_FILE
  ant messages create \
    --model claude-opus-5-5 \
    --max-tokens 1024 \
    --message '{role: user, content: "Hello, Claude"}'
  ```

  ```php PHP
  use Anthropic\Client;

  // 讀取 ANTHROPIC_FEDERATION_RULE_ID、ANTHROPIC_ORGANIZATION_ID、
  // ANTHROPIC_SERVICE_ACCOUNT_ID、ANTHROPIC_WORKSPACE_ID 及 ANTHROPIC_IDENTITY_TOKEN_FILE
  $client = new Client();

  $message = $client->messages->create(
      model: 'claude-opus-5-5',
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Hello, Claude']],
  );
  $textBlock = array_find($message->content, static fn ($block): bool => $block->type === 'text');
  echo $textBlock->text, PHP_EOL;
  ```

  ```ruby Ruby
  require "anthropic"

  # 讀取 ANTHROPIC_FEDERATION_RULE_ID、ANTHROPIC_ORGANIZATION_ID、
  # ANTHROPIC_SERVICE_ACCOUNT_ID、ANTHROPIC_WORKSPACE_ID 及 ANTHROPIC_IDENTITY_TOKEN_FILE
  client = Anthropic::Client.new

  message = client.messages.create(
    model: "claude-opus-5-5",
    max_tokens: 1024,
    messages: [{role: "user", content: "Hello, Claude"}]
  )
  puts message.content.find { it.type == :text }.text
  ```
</CodeGroup>

## 驗證設定

成功的交換會回傳以 `sk-ant-oat01-` 開頭的 `access_token`，以及以秒為單位的 `expires_in` 值。如果交換失敗並回傳不透明的 `401` `authentication_error` 回應（訊息為 `Authentication failed`），請查看 Claude Console 中的[驗證歷史記錄頁面](https://platform.claude.com/settings/workload-identity-federation?tab=history)以了解拒絕原因，並參閱[疑難排解失敗的交換](https://platform.claude.com/docs/zh-TW/manage-claude/wif-reference#troubleshoot-a-failed-exchange)；Kubernetes 端最常見的原因是 JWKS 金鑰不符（對於 `inline` 模式，請使用 `kubectl get --raw /openid/v1/jwks` 重新擷取並更新簽發者）。

## 限定規則範圍

<Warning>
  `subject_prefix` 為 `system:serviceaccount:*` 會比對叢集中的每個服務帳戶，因此任何 pod 都可以取得聯合的 Anthropic 權杖。若沒有 `audience` 比對器，該規則也會比對叢集的預設受眾權杖，而每個 pod 都已投射了這類權杖。
</Warning>

將規則的 `match` 區塊鎖定在符合您使用情境的最小範圍：

* **固定命名空間與服務帳戶名稱：** 使用完整的 `system:serviceaccount:<namespace>:<name>` 值，結尾不加 `*`。
* **務必設定受眾：** 在規則上要求 `audience`，並在 pod 的 `serviceAccountToken` 投射上設定相同的值，以便拒絕預設受眾權杖。
* **每個命名空間使用獨立的規則：** 為每個命名空間建立各自的規則與 Anthropic 服務帳戶，而非放寬單一規則。
* **將 inline-JWKS 簽發者限定於單一叢集：** 當多個叢集共用一個簽發者 URL 時，請將每個叢集的 JWKS 註冊為各自的聯合簽發者，並僅將規則繫結至該簽發者。

## 後續步驟

* [Workload Identity Federation](https://platform.claude.com/docs/zh-TW/manage-claude/workload-identity-federation)：概念、權杖交換流程以及 SDK 設定選項。
* [WIF 參考](https://platform.claude.com/docs/zh-TW/manage-claude/wif-reference)：環境變數、JWKS 來源模式以及規則比對模式。
