GKE RBAC のベスト プラクティス


このドキュメントでは、ロールベース アクセス制御(RBAC)ポリシーの計画に関するベスト プラクティスを説明します。Google Kubernetes Engine(GKE)に RBAC を実装する方法については、ロールベース アクセス制御を構成するをご覧ください。

RBAC は Kubernetes のコア セキュリティ機能です。きめ細かい権限を作成して、ユーザーとワークロードがクラスタ内のリソースに対して実行できるアクションを管理できます。プラットフォーム管理者として RBAC のロールを作成し、作成したロールをサブジェクト(サービス アカウントや Google グループなどの認証済みユーザー)にバインドします。

始める前に

このドキュメントを読む前に、次のコンセプトをよく理解しておいてください。

このガイダンスのチェックリストについては、チェックリストの概要をご覧ください。

RBAC の仕組み

RBAC では、次のタイプのロールとバインディングがサポートされています。

  • ClusterRole: 任意の Namespace またはクラスタ全体に適用できる一連の権限。
  • Role: 1 つの Namespace に制限されている一連の権限。
  • ClusterRoleBinding: クラスタ内のすべての Namespace のユーザーまたはグループに ClusterRole をバインドします。
  • RoleBinding: Role または ClusterRole を特定の Namespace 内のユーザーまたはグループにバインドします。

Role または ClusterRole で権限を rules として定義します。ロールの各 rules フィールドは、API グループ、その API グループ内の API リソース、それらのリソースで使用できる動詞(アクション)で構成されています。必要に応じて、resourceNames フィールドを使用して、動詞のスコープを API リソースの名前付きインスタンスに設定できます。例については、特定のリソース インスタンスへのアクセスを制限するをご覧ください。

ロールを定義したら、RoleBinding または ClusterRoleBinding を使用してロールをサブジェクトにバインドします。1 つの Namespace と複数の Namespace のどちらで権限を付与するかに基づいて、バインディングのタイプを選択します。

RBAC ロールの設計

最小権限の原則を使用する

RBAC ロールで権限を割り当てる場合は、最小権限の原則に従い、タスクの実行に必要な最小限の権限を付与します。最小権限の原則を使用すると、クラスタが侵害された場合の権限昇格のリスクが軽減され、過剰なアクセスによってセキュリティ インシデントが発生する可能性が低くなります。

ロールを設計するときには、escalate 動詞または bind 動詞、PersistentVolume の create アクセス、証明書署名リクエストの create アクセスなど、一般的な権限昇格のリスクを慎重に考慮してください。リスクの一覧については、Kubernetes RBAC - 権限昇格のリスクをご覧ください。

デフォルトのロールとグループを使用しない

Kubernetes では、API の検出やマネージド コンポーネント機能の有効化に使用できるデフォルトの ClusterRole と ClusterRoleBinding が作成されます。これらのデフォルトのロールによって付与される権限は、ロールによっては広範囲にわたる場合があります。Kubernetes には、(system: 接頭辞で識別される)デフォルトのユーザーとユーザー グループもあります。デフォルトでは、Kubernetes と GKE は、これらのロールをデフォルトのグループとさまざまなサブジェクトに自動的にバインドします。Kubernetes によって作成されるすべてのデフォルトのロールとバインディングの一覧については、デフォルトのロールとロール バインディングをご覧ください。

次の表に、デフォルトのロール、ユーザー、グループを示します。これらのロール、ユーザー、グループを利用する場合は、慎重に評価することをおすすめします。これは、これらのリソースを利用すると、クラスタのセキュリティ ポスチャーに意図しない結果をもたらす可能性があるためです。

名前 説明
cluster-admin ClusterRole クラスタ内の任意のリソースに対してあらゆる操作を行う権限をサブジェクトに付与します。
system:anonymous ユーザー

Kubernetes は、認証情報が指定されていない API サーバー リクエストにこのユーザーを割り当てます。

このユーザーにロールをバインドすると、認証されていないユーザーに、そのロールによって付与される権限が付与されます。

system:unauthenticated グループ

Kubernetes は、認証情報が指定されていない API サーバー リクエストにこのグループを割り当てます。

このグループにロールをバインドすると、認証されていないユーザーに、そのロールによって付与される権限が付与されます。

system:authenticated グループ

GKE は、Google アカウント(すべての Gmail アカウントを含む)でログインしているすべてのユーザーが実行する API サーバー リクエストにこのグループを割り当てます。Google アカウントは誰でも作成できるため、system:unauthenticated との間に実質的な違いはありません。

このグループにロールをバインドすると、Google アカウント(すべての Gmail アカウントを含む)を持つすべてのユーザーに、そのロールによって付与される権限が付与されます。

system:masters グループ

Kubernetes はデフォルトでこのグループに cluster-admin ClusterRole を割り当て、システム機能を有効にします。

このグループに独自のサブジェクトを追加すると、それらのサブジェクトに対し、クラスタ内のすべてのリソースに対して任意の操作を行えるアクセス権が付与されます。

可能であれば、デフォルトのユーザー、ロール、グループを含むバインディングを作成しないでください。これにより、クラスタのセキュリティ対策で意図しない結果が生じる可能性があります。次に例を示します。

  • デフォルトの cluster-admin ClusterRole を system:unauthenticated グループにバインドすると、未認証のユーザーがクラスタ内のすべてのリソース(Secret を含む)にアクセスできるようになります。このような高い権限を持つバインディングは、大規模なマルウェア キャンペーンなどの攻撃の標的にされやすくなります。
  • カスタムロールを system:unauthenticated グループにバインドすると、そのロールによって付与される権限が未認証のユーザーに付与されることになります。

可能な場合は、次のガイドラインを使用してください。

  • system:masters グループに独自のサブジェクトを追加しないでください。
  • system:unauthenticated グループを RBAC ロールにバインドしないでください。
  • system:authenticated グループを RBAC ロールにバインドしないでください。
  • system:anonymous ユーザーを RBAC ロールにバインドしないでください。
  • cluster-admin ClusterRole は、独自のサブジェクトまたはデフォルトのユーザーとグループのいずれにもバインドしないでください。アプリケーションで多数の権限が必要な場合は、厳密に必要な権限を特定し、その目的のための特定のロールを作成します。
  • サブジェクトをバインドする前に、他のデフォルトのロールによって付与される権限を評価します。
  • グループのメンバーを変更する前に、デフォルト グループにバインドされているロールを評価します。

デフォルトのロールとグループの使用を検出して防止する

クラスタを評価して、system:anonymous ユーザー、system:unauthenticated グループ、system:authenticated グループが、ClusterRoleBinding と RoleBinding の両方を使用してバインドされているかどうかを確認する必要があります。

ClusterRoleBinding
  1. サブジェクト system:anonymoussystem:unauthenticated、または system:authenticated を含む ClusterRoleBinding の名前を一覧表示します。

    kubectl get clusterrolebindings -o json \
      | jq -r '["Name"], ["-----"], (.items[] | select((.subjects | length) > 0) | select(any(.subjects[]; .name == "system:anonymous" or .name == "system:unauthenticated" or .name == "system:authenticated")) | [.metadata.namespace, .metadata.name]) | @tsv'
    

    出力には、次の ClusterRoleBinding のみが一覧表示されます。

    Name
    ----
    "system:basic-user"
    "system:discovery"
    "system:public-info-viewer"
    

    出力にデフォルト以外の追加のバインディングが含まれている場合は、バインディングごとに次の操作を行います。出力にデフォルト以外のバインディングが含まれていない場合は、次の手順をスキップします。

  2. バインディングに関連付けられているロールの権限を一覧表示します。

    kubectl get clusterrolebinding CLUSTER_ROLE_BINDING_NAME -o json \
        | jq ' .roleRef.name +" " + .roleRef.kind' \
        | sed -e 's/"//g' \
        | xargs -l bash -c 'kubectl get $1 $0 -o yaml'
    

    CLUSTER_ROLE_BINDING_NAME は、デフォルト以外の ClusterRoleBinding の名前に置き換えます。

    出力は次のようになります。

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
    ...
    rules:
    - apiGroups:
      - ""
      resources:
      - secrets
      verbs:
      - get
      - watch
      - list
    

    出力内の権限がデフォルトのユーザーまたはグループに付与されても安全であると判断した場合は、何もする必要はありません。バインディングによって付与される権限が安全ではないと判断した場合は、次のステップに進みます。

  3. 安全でないバインディングをクラスタから削除します。

    kubectl delete clusterrolebinding CLUSTER_ROLE_BINDING_NAME
    

    CLUSTER_ROLE_BINDING_NAME は、削除する ClusterRoleBinding の名前に置き換えます。

RoleBinding
  1. サブジェクト system:anonymoussystem:unauthenticated、または system:authenticated を持つ RoleBinding の Namespace と名前を一覧表示します。

    kubectl get rolebindings -A -o json \
      | jq -r '["Namespace", "Name"], ["---------", "-----"], (.items[] | select((.subjects | length) > 0) | select(any(.subjects[]; .name == "system:anonymous" or .name == "system:unauthenticated" or .name == "system:authenticated")) | [.metadata.namespace, .metadata.name]) | @tsv'
    

    クラスタが正しく構成されている場合、出力は空白になります。出力にデフォルト以外のバインディングが含まれている場合は、バインディングごとに次の操作を行います。出力が空白の場合は、次の手順をスキップします。

    RoleBinding の名前のみがわかっている場合は、次のコマンドを使用して、すべての Namespace で一致する RoleBinding を検索できます。

    kubectl get rolebindings -A -o json \
      | jq -r '["Namespace", "Name"], ["---------", "-----"], (.items[] | select((.subjects | length) > 0) | select(.metadata.name == "ROLE_BINDING_NAME") | [.metadata.namespace, .metadata.name]) | @tsv'
    

    ROLE_BINDING_NAME は、デフォルト以外の RoleBinding の名前に置き換えます。

  2. バインディングに関連付けられている Role の権限を一覧表示します。

    kubectl get rolebinding ROLE_BINDING_NAME --namespace ROLE_BINDING_NAMESPACE -o json \
        | jq ' .roleRef.name +" " + .roleRef.kind' \
        | sed -e 's/"//g' \
        | xargs -l bash -c 'kubectl get $1 $0 -o yaml --namespace ROLE_BINDING_NAMESPACE'
    

    次のように置き換えます。

    • ROLE_BINDING_NAME: デフォルト以外の RoleBinding の名前。
    • ROLE_BINDING_NAMESPACE: デフォルト以外の RoleBinding の Namespace。

    出力は次のようになります。

    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
    ...
    rules:
    - apiGroups:
      - ""
      resources:
      - secrets
      verbs:
      - get
      - watch
      - list
    

    出力内の権限がデフォルトのユーザーまたはグループに付与されても安全であると判断した場合は、何もする必要はありません。バインディングによって付与される権限が安全ではないと判断した場合は、次のステップに進みます。

  3. 安全でないバインディングをクラスタから削除します。

    kubectl delete rolebinding ROLE_BINDING_NAME --namespace ROLE_BINDING_NAMESPACE
    

    次のように置き換えます。

    • ROLE_BINDING_NAME: 削除する RoleBinding の名前。
    • ROLE_BINDING_NAMESPACE: 削除する RoleBinding の Namespace。

Namespace レベルで権限のスコープを設定する

ワークロードまたはユーザーのニーズに応じて、バインディングとロールを次のように使用します。

  • 1 つの Namespace 内のリソースへのアクセス権を付与するには、RoleRoleBinding を使用します。
  • 複数の Namespace 内のリソースへのアクセス権を付与するには、ClusterRole と各 Namespace の RoleBinding を使用します。
  • すべての Namespace 内のリソースへのアクセス権を付与するには、ClusterRoleClusterRoleBinding を使用します。

権限を付与する Namespace はできるだけ少なくしてください。

ワイルドカードを使用しない

* 文字は、すべてに適用されるワイルドカードです。ルールにワイルドカードを使用しないでください。RBAC ルールでは API グループ、リソース、動詞を明示的に指定してください。たとえば、verbs フィールドに * を指定すると、リソースに対する getlistwatchpatchupdatedeletecollectiondelete の各権限が付与されます。次の表に、ルールでワイルドカードを避ける例を示します。

推奨 非推奨
- rules:
    apiGroups: ["apps","extensions"]
    resources: ["deployments"]
    verbs: ["get","list","watch"]

動詞 getlistwatchapps API グループと extensions API グループに明示的に付与します。

- rules:
    apiGroups: ["*"]
    resources: ["deployments"]
    verbs: ["get","list","watch"]

任意の API グループの deployments に動詞を付与します。

- rules:
    apiGroups: ["apps", "extensions"]
    resources: ["deployments"]
    verbs: ["get", "list", "watch"]

動詞 getlistwatch のみを、apps API グループおよび extensions API グループ内の deployments に付与します。

- rules:
    apiGroups: ["apps", "extensions"]
    resources: ["deployments"]
    verbs: ["*"]

patchdelete を含むすべての動詞を付与します。

個別のルールを使用して特定のリソースに対する最小権限アクセスを付与する

ルールを計画するときには、次に示す各ロールで最小権限ルールをより効率的に設計するための大まかな手順をお試しください。

  1. サブジェクトがアクセスする必要がある各リソースに対する動詞ごとに個別の RBAC ルールを作成します。
  2. ルールのドラフトを作成したら、ルールを分析して、複数のルールに同じ verbs リストがあるかどうかを確認します。このようなルールを結合して 1 つのルールにします。
  3. 残りのルールはすべて互いに区別します。

このアプローチでは、ルールの設計がより整理されたものになります。同じ動詞を複数のリソースに付与するルールが結合され、異なる動詞をリソースに付与するルールは分離されます。

たとえば、ワークロードで deployments リソースに対する get 権限と、daemonsets リソースに対する listwatch が必要な場合は、ロールを作成するときに、分離したルール使用する必要があります。RBAC ロールをワークロードにバインドすると、deployments に対する watch が使用できなくなります。

別の例として、ワークロードで pods リソースと daemonsets リソースの両方に対する getwatch を必要とする場合、ワークロードは両方のリソースで同じ動詞を必要とするため、これらを 1 つのルールに結合できます。

次の表ではいずれのルール設計も機能しますが、分割されているルールではニーズに基づいてリソース アクセスがより細かく制限されます。

推奨 非推奨
- rules:
    apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get"]
- rules:
    apiGroups: ["apps"]
    resources: ["daemonsets"]
    verbs: ["list", "watch"]

Deployment に対する get アクセス権を付与し、DaemonSet に対する watchlist アクセス権を付与します。サブジェクトは Deployment を一覧表示できません。

- rules:
    apiGroups: ["apps"]
    resources: ["deployments", "daemonsets"]
    verbs: ["get","list","watch"]

Deployment と DaemonSet の両方に対する動詞を付与します。deployments オブジェクトに対する list アクセス権を必要としない可能性があるサブジェクトにも、引き続きそのアクセス権が付与されます。

- rules:
    apiGroups: ["apps"]
    resources: ["daemonsets", "deployments"]
    verbs: ["list", "watch"]

サブジェクトが daemonsets リソースと deployments リソースの両方について同じ動詞を必要とするため、2 つのルールを結合します。

- rules:
    apiGroups: ["apps"]
    resources: ["daemonsets"]
    verbs: ["list", "watch"]
- rules:
    apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["list", "watch"]

分割されているこれらのルールは、結合されたルールと同じ結果になりますが、ロールのマニフェストで不要な煩雑さが生じます。

特定のリソース インスタンスへのアクセスを制限する

RBAC では、ルールの resourceNames フィールドを使用して、リソースの特定の名前付きインスタンスへのアクセスを制限できます。たとえば、seccomp-high ConfigMap のみに対する update が必要な RBAC ロールを作成する場合は、resourceNames を使用してその ConfigMap のみを指定できます。可能な限り resourceNames を使用してください。

推奨 非推奨
- rules:
    apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["seccomp-high"]
    verbs: ["update"]

seccomp-high ConfigMap のみを更新するようにサブジェクトを制限します。サブジェクトは、Namespace 内の他の ConfigMap を更新できません。

- rules:
    apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["update"]

サブジェクトは、seccomp-high ConfigMap と Namespace 内の他の ConfigMap を更新できます。

- rules:
    apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["list"]
- rules:
    apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["seccomp-high"]
    verbs: ["update"]

Namespace 内のすべての ConfigMap(seccomp-high を含む)に対する list アクセス権を付与します。update アクセス権を seccomp-high ConfigMap のみに制限します。名前付きリソースに対する list を付与できないため、ルールは分割されます。

- rules:
    apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["update", "list"]

すべての ConfigMap に対する update アクセス権と list アクセス権を付与します。

サービス アカウントによる RBAC リソースの変更を許可しない

rbac.authorization.k8s.io API グループで bindescalatecreateupdate、または patch 権限を持つ Role または ClusterRole リソースは、どの Namespace 内のサービス アカウントにもバインドしないでください。特に escalatebind では、攻撃者が RBAC に組み込まれているエスカレーション防止メカニズムを回避できる可能性があります。

Kubernetes サービス アカウント

ワークロードごとに Kubernetes サービス アカウントを作成する

ワークロードごとに個別の Kubernetes サービス アカウントを作成します。そのサービス アカウントに、最小権限の Role または ClusterRole をバインドします。

デフォルトのサービス アカウントを使用しない

Kubernetes は、すべての Namespace に default という名前のサービス アカウントを作成します。default サービス アカウントは、マニフェストでサービス アカウントを明示的に指定していない Pod に自動的に割り当てられます。Role または ClusterRoledefault サービス アカウントにバインドしないようにします。Kubernetes は、これらのロールで付与されたアクセス権を必要としない Pod に default サービス アカウントを割り当てることがあります。

サービス アカウント トークンを自動的にマウントしない

Pod 仕様の automountServiceAccountToken フィールドは、Kubernetes サービス アカウントの認証情報トークンを Pod に挿入するように Kubernetes に指示します。Pod はこのトークンを使用して、Kubernetes API サーバーに認証済みリクエストを送信できます。このフィールドのデフォルト値は true です。

すべての GKE バージョンで、Pod が API サーバーと通信する必要がない場合は Pod 仕様で automountServiceAccountToken=false を設定します。

Secret ベースのトークンよりもエフェメラル トークンを優先する

デフォルトでは、ノードの kubelet プロセスは、Pod ごとに自動的にローテーションされる有効期間の短いサービス アカウント トークンを取得します。Pod 仕様で automountServiceAccountToken フィールドを false に設定しない限り、kubelet はこのトークンを予測ボリュームとして Pod にマウントします。Pod から Kubernetes API への呼び出しを行うと、このトークンを使用して API サーバーに対する認証が行われます。

サービス アカウント トークンを手動で取得する場合は、Kubernetes Secret を使用してトークンを保存しないでください。Secret ベースのサービス アカウント トークンは以前の認証情報であり、有効期限がなく、自動的にローテーションされません。サービス アカウントの認証情報が必要な場合は、TokenRequest API を使用して、自動的にローテーションされる有効期間の短いトークンを取得します。

RBAC 権限を継続的に確認する

RBAC のロールとアクセス権を定期的に確認し、潜在的なエスカレーション パスと冗長なルールを特定します。たとえば、特別な権限を含む Role を削除済みのユーザーにバインドする RoleBinding を削除しない状況を考えてみます。攻撃者が、削除済みユーザーと同じ名前のユーザー アカウントを Namespace に作成すると、その Role にバインドされ、同じアクセス権を継承します。定期的な確認を行うことでこのリスクを最小限に抑えることができます。

チェックリストの概要

次のステップ