authentication
kubernetes
OriginalZ LabDay 7

KubernetesのToken Review API

KubernetesのAPIサーバと同じ認証を自前のpodとかでやりたいことがありませんか?

そんなときはTokenReview APIを使ってみるといいかもしれません。

これはKubernetesのAPIサーバに対してBearerトークンをリクエストパラメータとして渡すと、APIサーバが有効かどうかを判断して結果を返してくれるAPIです。例えばServiceAccountのトークンや、OIDCを有効にしている場合にはID Tokenなんかを渡すことができます。

v1.7以降ではTokenReview APIには以下のURIでアクセスできます。

https://${API_SERVER_ADDR}/apis/authentication.k8s.io/v1/tokenreviews

リクエストパラメータは以下のようなJSONデータです。

{
    "apiVersion": "authentication.k8s.io/v1",
    "kind": "TokenReview",
    "spec": {
        "token": <USER_TOKEN>
    }
}

これをTokenReviewAPIにPOSTしてやるとレスポンスが返ってきます。

$ curl -H "Content-Type: application/json" \
       -H 'Authorization: Bearer ${SYTEM_TOKEN}' \
       -d '{"apiVersion": "authentication.k8s.io/v1","kind": "TokenReview","spec": {"token": <USER_TOKEN>}'
       https://${API_SERVER_ADDR}/apis/authentication.k8s.io/v1/tokenreviews \

例えばkube-apiserverに対してOIDCの認証設定かつusernameのフィールドとしてemailを使う設定をしている場合などは、TokenReview APIにID Tokenを渡すと、認証した結果のusernameの値としてemailアドレスが入っています。

{
  "kind": "TokenReview",
  "apiVersion": "authentication.k8s.io/v1",
  "metadata": {
    "creationTimestamp": null
  },
  "spec": {
    "token": <USER_TOKEN>
  },
  "status": {
    "authenticated": true,
    "user": {
      "username": "foo@example.org"
      "groups": [
        "system:authenticated"
      ]
    }
  }
}

ServiceAccountトークンを渡した場合はusernameが以下のようなフォーマットの文字列になります。

system:serviceaccount:<namespace>:<name>

Tokenが有効でない場合や期限が切れている場合などは status.authenticated の値が false になります。

{
  "kind": "TokenReview",
  "apiVersion": "authentication.k8s.io/v1",
  "metadata": {
    "creationTimestamp": null
  },
  "spec": {
    "token": <USER_TOKEN>
  },
  "status": {
    "authenticated": false,
  }
}

このAPIを呼び出すには system:auth-delegator の権限が必要なので、TokenReview APIを呼びすアカウントに対してClusterRoleBindingまたはRoleBindingで権限を付与してあげてください。

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: sample-ns:system:auth-delegator
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:auth-delegator
subjects:
- kind: ServiceAccount
  name: default
  namespace: sample-ns

さらにGoLangのコードからこれらを呼び出す場合にはvaultのKubernetes Auth Backend プラグインのコードを参考にするとよいでしょう。client-goのパッケージを使ってTokenReview APIを呼び出しています。

https://github.com/hashicorp/vault-plugin-auth-kubernetes

最後にひとつ注意すべき点を記載しておきます。

今回TokenReview APIで扱っているID TokenやServiceAccount TokenはいずれもJWT形式なので署名を検証するための公開鍵さえあれば自前で検証できてしまいますが、TokenのRevokeや公開鍵ペアのローテーションが発生した場合の考慮を十分にしておく必要があります。

ID Tokenの場合にはトークンのclaimとしてexpなどが含まれているため、Revokeやキーのローテーションが発生したとしても最悪の場合でもexpの値である期限までしか使えませんが、ServiceAccount Tokenは期限を持っていないためRevokeやローテーションに関係なく永遠に使い続けられてしまいとても危険な状態になります。

よほど問題がない限りは正しくTokenReview APIを使って検証しましょう。