Podにサービスアカウントを設定する

Podにサービスアカウントを設定する

Kubernetesは、クラスター内で実行されるクライアント、またはクラスターのコントロールプレーンと何らかの関係を持つクライアントが、APIサーバーに対して認証を行うために、2つの異なる方法を提供しています。

サービスアカウント は、Pod内で実行されるプロセスにアイデンティティを提供し、ServiceAccountオブジェクトにマッピングされます。 APIサーバーに対して認証を行う際、あなたは特定の ユーザー として自分自身を識別します。 Kubernetesはユーザーという概念を認識していますが、Kubernetes自体はUser APIを持っていません

このタスクガイドでは、Kubernetes APIに存在するServiceAccountについて説明します。 このガイドでは、PodにServiceAccountを設定する方法をいくつか示します。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

デフォルトのサービスアカウントを使用してAPIサーバーにアクセスする

PodがAPIサーバーに接続する際、Podは特定のServiceAccount(例: default)として認証を行います。 それぞれの名前空間には、常に少なくとも1つのServiceAccountが存在します。

すべてのKubernetes名前空間には、少なくとも1つのServiceAccountが含まれています。 それは、その名前空間のデフォルトのServiceAccountであり、defaultという名前が付けられています。 Podを作成する際にServiceAccountを指定しない場合、Kubernetesは自動的にその名前空間内に、defaultという名前のServiceAccountを割り当てます。

作成したPodの詳細を取得するには、たとえば下記を実行します:

kubectl get pods/<podname> -o yaml

出力には、spec.serviceAccountNameフィールドが表示されます。 Podを作成する際にこの値を指定しない場合、Kubernetesは自動的にこの値を設定します。

Pod内で実行されているアプリケーションは、自動的にマウントされたサービスアカウントの認証情報を使用して、Kubernetes APIにアクセスできます。 詳細については、クラスターへのアクセスを参照してください。

PodがServiceAccountとして認証を行う場合、そのアクセスレベルは、使用する認可プラグインとポリシーに依存します。

ファイナライザーが設定されている場合でも、Podが削除されるとAPI認証情報は自動的に取り消されます。 具体的には、Podに設定された.metadata.deletionTimestamp(削除タイムスタンプは通常、削除リクエストが受け入れられた時刻にPodの終了猶予期間を加えた時刻です)から60秒後にAPI認証情報が取り消されます。

API認証情報の自動マウントをオプトアウトする

kubeletが、ServiceAccountのAPI認証情報を自動的にマウントしないようにしたい場合、デフォルトの挙動をオプトアウトできます。 サービスアカウントに対して、automountServiceAccountToken: falseを設定することで、/var/run/secrets/kubernetes.io/serviceaccount/tokenへのAPI認証情報の自動マウントをオプトアウトできます。

例:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot
automountServiceAccountToken: false
...

また、特定のPodに対してAPI認証情報の自動マウントをオプトアウトすることもできます:

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  serviceAccountName: build-robot
  automountServiceAccountToken: false
  ...

ServiceAccountとPodの.specの両方がautomountServiceAccountTokenの値を指定している場合、Podの仕様が優先されます。

複数のServiceAccountを使用する

すべての名前空間には、少なくとも1つのServiceAccountが存在します。 それは、defaultという名前のデフォルトのServiceAccountリソースです。 現在の名前空間内のすべてのServiceAccountリソースを次のコマンドで一覧表示できます:

kubectl get serviceaccounts

出力は以下のようになります:

NAME      SECRETS    AGE
default   1          1d

次のように、追加のServiceAccountオブジェクトを作成できます:

kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot
EOF

ServiceAccountオブジェクトの名前は、有効なDNSサブドメイン名である必要があります。

次のように、サービスアカウントオブジェクトの完全なダンプを取得する場合:

kubectl get serviceaccounts/build-robot -o yaml

出力は以下のようになります:

apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: 2019-06-16T00:12:34Z
  name: build-robot
  namespace: default
  resourceVersion: "272500"
  uid: 721ab723-13bc-11e5-aec2-42010af0021e

認可プラグインを使用して、サービスアカウントに権限を設定できます。

デフォルト以外のサービスアカウントを使用するには、Podのspec.serviceAccountNameフィールドを、使用したいServiceAccountの名前に設定します。

serviceAccountNameフィールドは、Podを作成する際、または新しいPodのテンプレート内でのみ設定できます。 既に存在するPodの.spec.serviceAccountNameフィールドは更新できません。

備考:

.spec.serviceAccountフィールドは、.spec.serviceAccountNameの非推奨のエイリアスです。 ワークロードリソースからこれらのフィールドを削除したい場合は、Podテンプレートで両方のフィールドを明示的に空に設定してください。

クリーンアップ

上記の例でbuild-robot ServiceAccountを作成した場合、次のコマンドを実行してクリーンアップできます:

kubectl delete serviceaccount/build-robot

ServiceAccountのAPIトークンを手動で作成する

前述のように、「build-robot」という名前の既存のサービスアカウントがあるとします。

kubectlを使用して、そのServiceAccountの期限付きAPIトークンを取得できます:

kubectl create token build-robot

このコマンドの出力は、そのServiceAccountとして認証するために使用できるトークンです。 kubectl create token--durationコマンドライン引数を使用して、特定のトークン期間を要求できます(発行されるトークンの実際の期間は短くなる場合や、長くなる場合もあります)。

FEATURE STATE: GA since Kubernetes v1.33
More information about this feature

This is a stable feature in Kubernetes, and has been since version v1.33. It was first available in the v1.29 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate ServiceAccountTokenNodeBinding, Kubernetes ignores it but does not report any error.

kubectl v1.31以降を使用すると、ノードに直接バインドされたサービスアカウントトークンを作成できます:

kubectl create token build-robot --bound-object-kind Node --bound-object-name node-001 --bound-object-uid 123...456

トークンは、有効期限が切れるか、関連するノードまたはサービスアカウントが削除されるまで有効です。

備考:

Kubernetes v1.22以前のバージョンでは、Kubernetes APIにアクセスするための長期的な認証情報が自動的に作成されていました。 この古いメカニズムは、実行中のPodにマウントされるトークンSecretの作成に基づいていました。 Kubernetes v1.37を含む最近のバージョンでは、API認証情報はTokenRequest APIを使用して直接取得され、projected volumeを使用してPodにマウントされます。 この方法で取得されたトークンには有効期限があり、マウント先のPodが削除されると自動的に無効になります。

サービスアカウント用のトークンSecretを手動で作成することも可能です。 例えば、有効期限のないトークンが必要な場合などです。 ただし、APIにアクセスするためのトークンを取得するには、TokenRequestサブリソースを使用することを推奨します。

ServiceAccount用の長期間有効なAPIトークンを手動で作成する

ServiceAccount用のAPIトークンを取得したい場合は、特別なアノテーションkubernetes.io/service-account.nameを持つ新しいSecretを作成します。

kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: build-robot-secret
  annotations:
    kubernetes.io/service-account.name: build-robot
type: kubernetes.io/service-account-token
EOF

Secretを表示するには、次のコマンドを使用します:

kubectl get secret/build-robot-secret -o yaml

Secretに「build-robot」ServiceAccount用のAPIトークンが含まれていることが確認できます。

設定したアノテーションにより、コントロールプレーンはそのServiceAccount用のトークンを自動的に生成し、関連するSecretに保存します。 また、コントロールプレーンは削除されたServiceAccount用のトークンをクリーンアップします。

kubectl describe secrets/build-robot-secret

出力は以下のようになります:

Name:           build-robot-secret
Namespace:      default
Labels:         <none>
Annotations:    kubernetes.io/service-account.name: build-robot
                kubernetes.io/service-account.uid: da68f9c6-9d26-11e7-b84e-002dc52800da

Type:   kubernetes.io/service-account-token

Data
====
ca.crt:         1338 bytes
namespace:      7 bytes
token:          ...

備考:

tokenの内容はここでは省略されています。

ターミナルやコンピューターの画面が覗き見される可能性がある場所で、kubernetes.io/service-account-token Secretの内容を表示しないよう注意してください。

関連するSecretを持つServiceAccountを削除すると、KubernetesコントロールプレーンはそのSecretから長期的なトークンを自動的にクリーンアップします。

備考:

ServiceAccountを表示するには、次のコマンドを使用します:

kubectl get serviceaccount build-robot -o yaml

ServiceAccount APIオブジェクトの.secretsフィールドにbuild-robot-secret Secretは表示されません。 このフィールドには自動生成されたSecretのみが入力されるためです。

サービスアカウントにImagePullSecretを追加する

まず、imagePullSecretを作成します。 次に、作成されたことを確認します。 以下は、その例です:

  • PodでのImagePullSecretの指定で説明されているように、imagePullSecretを作成します。

      kubectl create secret docker-registry myregistrykey --docker-server=<レジストリ名> \
              --docker-username=DUMMY_USERNAME --docker-password=DUMMY_DOCKER_PASSWORD \
              --docker-email=DUMMY_DOCKER_EMAIL
    
  • 作成されたことを確認します。

      kubectl get secrets myregistrykey
    

    出力は以下のようになります:

      NAME             TYPE                              DATA    AGE
      myregistrykey    kubernetes.io/.dockerconfigjson   1       1d
    

サービスアカウントにイメージプルシークレットを追加する

次に、名前空間のデフォルトのサービスアカウントを変更して、このSecretをimagePullSecretとして使用するようにします。

kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "myregistrykey"}]}'

オブジェクトを手動で編集することでも同じ結果を得られます:

kubectl edit serviceaccount/default

sa.yamlファイルの出力は以下のようになります:

使用しているテキストエディターが開き、以下のような設定が表示されます:

apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: 2021-07-07T22:02:39Z
  name: default
  namespace: default
  resourceVersion: "243024"
  uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6

エディターを使用して、resourceVersionキーが記述されている行を削除し、imagePullSecrets:の行を追加して保存します。 uidの値は変更しないでおきます。

これらの変更を行うと、編集後のServiceAccountは以下のようになります:

apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: 2021-07-07T22:02:39Z
  name: default
  namespace: default
  uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6
imagePullSecrets:
  - name: myregistrykey

新しいPodにimagePullSecretが設定されていることを確認する

現在の名前空間に、デフォルトのServiceAccountを使用して新しいPodが作成されると、新しいPodのspec.imagePullSecretsフィールドが自動的に設定されます:

kubectl run nginx --image=<レジストリ名>/nginx --restart=Never
kubectl get pod nginx -o=jsonpath='{.spec.imagePullSecrets[0].name}{"\n"}'

出力は以下のようになります:

myregistrykey

ServiceAccountトークンボリューム投影

FEATURE STATE: GA since Kubernetes v1.20

備考:

トークンリクエスト投影を有効化して使用するには、kube-apiserverに以下の各コマンドライン引数を指定する必要があります:

--service-account-issuer
サービスアカウントトークン発行者の識別子を定義します。 --service-account-issuer引数は複数回指定でき、発行者をダウンタイムなしで変更するのに役立ちます。 このフラグを複数回指定した場合、最初のものがトークンの生成に使用され、すべてが受け入れ可能な発行者の判定に使用されます。 --service-account-issuerを複数回指定するには、Kubernetes v1.22以降を実行する必要があります。
--service-account-key-file
ServiceAccountトークンの検証に使用される、PEMエンコードされたX.509秘密鍵または公開鍵(RSAまたはECDSA)を含むファイルのパスを指定します。 指定したファイルには複数の鍵を含めることができ、フラグは異なるファイルで複数回指定できます。 複数回指定された場合、指定された鍵のいずれかで署名されたトークンは、Kubernetes APIサーバーによって有効と見なされます。
--service-account-signing-key-file
サービスアカウントトークン発行者の現在の秘密鍵を含むファイルのパスを指定します。 発行者はこの秘密鍵を使用して発行されたIDトークンに署名します。
--api-audiences(省略可能)
ServiceAccountトークンのオーディエンスを定義します。 サービスアカウントトークン認証機能は、APIに対して使用されるトークンが、これらのオーディエンスの少なくとも1つにバインドされていることを検証します。 api-audiencesを複数回指定した場合、指定したオーディエンスのいずれかに対応するトークンは、Kubernetes APIサーバーによって有効と見なされます。 --service-account-issuerコマンドライン引数を指定しても--api-audiencesを設定しない場合、コントロールプレーンはデフォルトで発行者URLのみを含むオーディエンスリストを使用します。

kubeletも、ServiceAccountトークンをPodに投影できます。 トークンの望ましいプロパティ(オーディエンスや有効期間など)を指定できます。 これらのプロパティは、デフォルトのServiceAccountトークンでは設定 できません。 また、PodまたはServiceAccountのいずれかが削除されると、トークンはAPIに対して無効になります。

この挙動は、ServiceAccountTokenと呼ばれる投影ボリュームタイプを使用してPodのspecで設定できます。

この投影ボリュームのトークンはJSON Web Token(JWT)です。 このトークンのJSONペイロードは明確に定義されたスキーマに従い、以下はPodにバインドされたトークンのペイロードの例です:

{
  "aud": [  # リクエストされたオーディエンス、または明示的にリクエストされない場合はAPIサーバーのデフォルトオーディエンスと一致
    "https://kubernetes.default.svc"
  ],
  "exp": 1731613413,
  "iat": 1700077413,
  "iss": "https://kubernetes.default.svc",  # --service-account-issuerフラグに渡された最初の値と一致
  "jti": "ea28ed49-2e11-4280-9ec5-bc3d1d84661a", 
  "kubernetes.io": {
    "namespace": "kube-system",
    "node": {
      "name": "127.0.0.1",
      "uid": "58456cb0-dd00-45ed-b797-5578fdceaced"
    },
    "pod": {
      "name": "coredns-69cbfb9798-jv9gn",
      "uid": "778a530c-b3f4-47c0-9cd5-ab018fb64f33"
    },
    "serviceaccount": {
      "name": "coredns",
      "uid": "a087d5a0-e1dd-43ec-93ac-f13d89cd13af"
    },
    "warnafter": 1700081020
  },
  "nbf": 1700077413,
  "sub": "system:serviceaccount:kube-system:coredns"
}

サービスアカウントトークン投影を使用してPodを起動する

オーディエンスがvaultで、有効期間が2時間のトークンをPodに提供するには、以下のようなPodマニフェストを定義します:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /var/run/secrets/tokens
      name: vault-token
  serviceAccountName: build-robot
  volumes:
  - name: vault-token
    projected:
      sources:
      - serviceAccountToken:
          path: vault-token
          expirationSeconds: 7200
          audience: vault

Podを作成します:

kubectl create -f https://k8s.io/examples/pods/pod-projected-svc-token.yaml

kubeletは、Podに代わってトークンをリクエストして保存し、設定可能なファイルパスでPodがトークンを利用できるようにします。 また、トークンの有効期限が近づくと更新します。 kubeletは、トークンの全生存時間(TTL)の80%を経過した場合、または24時間を経過した場合に、積極的にトークンのローテーションをリクエストします。

トークンがローテーションされた際にトークンを再読み込みするのは、アプリケーション側の責任です。 多くの場合、実際の有効期限を追跡しなくても、定期的に(例えば5分ごとに)トークンを読み込むだけで十分です。

サービスアカウント発行者のディスカバリ

FEATURE STATE: GA since Kubernetes v1.21

クラスター内のServiceAccountに対してトークン投影を有効にしている場合、ディスカバリ機能も利用できます。 Kubernetesは、クライアントが アイデンティティプロバイダー として連携できる方法を提供し、1つ以上の外部システムが リライングパーティ として機能できます。

備考:

発行者URLはOIDC Discovery仕様に準拠している必要があります。 実際には、これはhttpsスキームを使用する必要があり、{service-account-issuer}/.well-known/openid-configurationでOpenIDプロバイダー設定を提供する必要があることを意味します。

URLが準拠していない場合、ServiceAccount発行者のディスカバリエンドポイントは登録されず、アクセスできません。

有効にすると、Kubernetes APIサーバーはHTTP経由でOpenIDプロバイダー設定ドキュメントを公開します。 設定ドキュメントは/.well-known/openid-configurationで公開されます。 OpenIDプロバイダー設定は、ディスカバリドキュメント と呼ばれることもあります。 Kubernetes APIサーバーは、関連するJSON Web Key Set(JWKS)もHTTP経由で/openid/v1/jwksで公開します。

備考:

/.well-known/openid-configuration/openid/v1/jwksで提供される応答は、OIDC互換となるように設計されていますが、厳密にはOIDC準拠ではありません。 これらのドキュメントには、Kubernetesサービスアカウントトークンの検証を実行するために必要なパラメーターのみが含まれています。

RBACを使用するクラスターには、system:service-account-issuer-discoveryというデフォルトのClusterRoleが含まれています。 デフォルトのClusterRoleBindingは、このロールをsystem:serviceaccountsグループに割り当てます。 すべてのServiceAccountは暗黙的にこのグループに属しています。 これにより、クラスター上で実行されているPodは、マウントされたサービスアカウントトークンを介してサービスアカウントディスカバリドキュメントにアクセスできます。 管理者は、セキュリティ要件や連携する予定の外部システムに応じて、さらにsystem:authenticatedまたはsystem:unauthenticatedにロールをバインドすることもできます。

JWKS応答には、リライングパーティがKubernetesサービスアカウントトークンを検証するために使用できる公開鍵が含まれています。 リライングパーティは、まずOpenIDプロバイダー設定をクエリし、応答内のjwks_uriフィールドを使用してJWKSを見つけます。

多くの場合、Kubernetes APIサーバーはパブリックインターネット上では利用できませんが、APIサーバーからのキャッシュされた応答を提供するパブリックエンドポイントをユーザーまたはサービスプロバイダーが利用可能にすることができます。 これらの場合、APIサーバーに--service-account-jwks-uriフラグを渡すことで、OpenIDプロバイダー設定内のjwks_uriをオーバーライドして、APIサーバーのアドレスではなくパブリックエンドポイントを指すようにすることができます。 発行者URLと同様に、JWKS URIもhttpsスキームを使用する必要があります。

次の項目

以下も参照してください: