ServiceAccount と RBAC の仕組み

v1.37 時点(2026年10月26日 確認)

Kubernetes 基本仕様第38回 / 全45回 ・ 第8部 セキュリティとアクセス制御
目次

この記事では、Pod の中のプログラムが Kubernetes の API を使うときの身元になる ServiceAccount と、誰にどの操作を許すかを決める RBAC の仕組みを、図で説明します。ServiceAccount のトークンが Pod に届く流れ、Role・ClusterRole・RoleBinding・ClusterRoleBinding の 4 つのオブジェクトの役割、権限が足し算だけで決まることがわかります。後半では kind のクラスタで、権限の無い ServiceAccount の API の呼び出しが断られ、Role と RoleBinding を足すと許されることを確かめる手順を示します。

第37回で見た Namespace の内容を前提にしています。Role と RoleBinding は Namespace に属し、ClusterRole と ClusterRoleBinding は Namespace に属さないリソースでした。まだの方は先にこちらをどうぞ。

基本仕様 ・ 第37回 Namespace で分けられるもの、分けられないもの1 つのクラスタの中でリソースをグループに分ける Namespace の仕組みを図で説明します。名前の範囲になること、Namespace に属さないクラスタ全体のリソース、既定では Namespace をまたいで届く通信、Namespace を消すと中身も消える流れを押さえ、kind のクラスタで確かめる手順を紹介します。

以下の図は後半の手順で作る ServiceAccount、Role、RoleBinding と、その ServiceAccount を使う Pod の関係を表しています。手順では Role と RoleBinding を最後に足すので、図は手順を終えたときの状態です。

Pod、ServiceAccount、RoleBinding、Role の関係

Pod は spec.serviceAccountName で ServiceAccount demo-sa を指し、その身元で API を使います。RoleBinding read-pods は subjects で demo-sa を、roleRef で Role pod-reader を指します。こうして Role に書いた権限(Pod の get・list・watch)が demo-sa に与えられます。RoleBinding が無ければ、demo-sa には Pod を一覧する権限がありません。

参考:Service Accounts | Kubernetes、Using RBAC Authorization | Kubernetes、Using RBAC Authorization | Kubernetes

ServiceAccount と RBAC とは

ServiceAccount は 人ではないものに、クラスタの中での身元を与えるアカウント です。公式ドキュメントによると、Pod の中のアプリケーション、システムのコンポーネント、クラスタの内外のものが、ServiceAccount の認証情報を使ってその ServiceAccount を名乗れます。この身元は kube-apiserver への認証などに使われます。ServiceAccount は kube-apiserver の中に ServiceAccount のオブジェクトとして存在し、それぞれ 1 つの Namespace に属します。

参考:Service Accounts | Kubernetes

第3回で見たとおり、kube-apiserver はリクエストを受け取ると、まず認証(誰からのリクエストか)を確かめ、次に認可(その操作をしてよいか)を確かめます。この回で扱うものでいえば、ServiceAccount とそのトークンは認証に、Role や RoleBinding(RBAC)は認可にかかわります。RBAC は認可の仕組み(認可モード)の 1 つで、ほかに kubelet に権限を与える Node や、外部のサービスに問い合わせる Webhook などがあります。2 つの段階の流れは後の「API のリクエストが許されるか断られるか」の図で示し、細部は第40回で扱います。

参考:Authorization | Kubernetes

公式ドキュメントは ServiceAccount を、人が使う利用者のアカウント(user account)と区別しています。ServiceAccount と、利用者・グループの違いを次の表にまとめます。

項目ServiceAccount利用者・グループ
置き場所Kubernetes の API(ServiceAccount のオブジェクト)クラスタの外
アクセスの制御RBAC などの認可の仕組みRBAC や、ほかの ID とアクセスの管理の仕組み
想定する使い方ワークロード、自動化人

参考:Service Accounts | Kubernetes

既定では、利用者のアカウントは kube-apiserver の中に存在しません。認証のページによると、Kubernetes には普通の利用者を表すオブジェクトが無く、API の呼び出しで利用者を足すこともできません。ただし、クラスタの認証局(CA)が署名した正しい証明書を出した利用者は、認証されたものとして扱われます。利用者名は証明書の subject の common name(/CN=bob など)から決まり、証明書の organization(O。組織)に書いた名前は、その利用者のグループとして扱われます。第3回で見た kubeconfig の users は、こうした認証情報を kubectl が使うための項目でした。

参考:Service Accounts | Kubernetes、Authenticating | Kubernetes、Authenticating | Kubernetes

RBAC(Role-Based Access Control)は、第18回で触れたとおり、利用者やプログラムごとに、どのリソースにどの操作を許すかを決める仕組みです。公式ドキュメントは RBAC を、組織の中の個々の利用者の役割(role)にもとづいてアクセスを制御する方法と説明しています。RBAC は rbac.authorization.k8s.io の API グループを使い、Role、ClusterRole、RoleBinding、ClusterRoleBinding の 4 種類のオブジェクトで決まりを表します。決まりは Kubernetes の API で作ったり変えたりでき、kubectl でほかのオブジェクトと同じように扱えます。

参考:Using RBAC Authorization | Kubernetes

なぜ ServiceAccount と RBAC が必要なのか

API を使うのは人だけではありません。第4回で見たとおり、Pod の中のアプリケーションが Kubernetes の API を呼ぶときも、接続先は kube-apiserver です。第23回の CoreDNS は Service や EndpointSlice を一覧して見張り、第22回のサービスのコントローラーは Service の status を書き換える権限を必要としていました。公式ドキュメントも ServiceAccount の使いどころとして、Pod が kube-apiserver と通信する場面を挙げています。たとえば Secret に入った機密の情報を読むことだけを許す場面や、別の Namespace のオブジェクトを読む場面です。

参考:Service Accounts | Kubernetes

こうしたプログラムには人とは別の身元を用意します。公式ドキュメントが挙げる、利用者のアカウントと ServiceAccount を区別する理由のうち、表に無い主なものは次の 3 つです。

  • 利用者名はクラスタ全体で重ならない。ServiceAccount は Namespace ごとに同じ名前を付けられる
  • ServiceAccount は作業ごとに気軽に作れる。人の登録と分けることで、最小権限の原則に沿いやすくなる
  • 人と ServiceAccount で、監査の扱いを分けやすい

参考:Managing Service Accounts | Kubernetes

身元が決まっても、それだけでは何もできません。公式ドキュメントによると、API のリクエストはすべての部分がいずれかの認可の仕組みに許されないと先へ進めません。つまり既定では、アクセスは拒否されます。RBAC は ServiceAccount ごとに必要な最小限の権限を与えるのに使えます。そうすれば、その ServiceAccount を使う Pod は、正しく動くのに要る以上の権限を持ちません。第37回で見たとおり、複数のチームで使う環境では、RBAC で利用者のアクセスを必要な Namespace に絞らなければなりません(must)。

参考:Authorization | Kubernetes、Service Accounts | Kubernetes、Multi-tenancy | Kubernetes

ServiceAccount と RBAC の仕組み

ServiceAccount と Pod の結び付き

公式ドキュメントによると、Kubernetes はクラスタを作るとき、すべての Namespace に default という名前の ServiceAccount を自動で作ります。第4回で名前だけ見た ServiceAccount のコントローラーが、動いているすべての Namespace に default があるようにしています。default を消しても、コントロールプレーンが新しいものに置き換えます。Pod に ServiceAccount を割り当てるには、Pod の spec.serviceAccountName に ServiceAccount の名前を書きます。書かなければ、その Namespace の default が割り当てられます。第5回で、書かなかったフィールドの既定値として serviceAccountName: default を見たのはこのためです。

参考:Service Accounts | Kubernetes、Managing Service Accounts | Kubernetes、Service Accounts | Kubernetes、Configure Service Accounts for Pods | Kubernetes

default を割り当てるのは、kube-apiserver の ServiceAccount というアドミッションコントローラーです。アドミッションコントローラーは、第30回で見たとおり kube-apiserver へのリクエストを保存する前に確かめるもので、第40回で詳しく扱います。公式ドキュメントによると、ServiceAccount のアドミッションコントローラーは確かめるだけでなく、作られる Pod を書き換えもします(Mutating and Validating)。Pod の作成時にすることを、次の表にまとめます。

順すること
1spec.serviceAccountName が無ければ default にする
2指定された ServiceAccount があるかを確かめ、無ければ Pod を拒否する。default でも同じ
3ServiceAccount と Pod のどちらの automountServiceAccountToken も false でなければ、API のためのトークンを入れたボリュームを足す。各コンテナの /var/run/secrets/kubernetes.io/serviceaccount にマウントする(そのパスにマウントがすでにあるコンテナは除く)
4Pod に imagePullSecrets(イメージを取得するときの認証情報。第18回)が無ければ、ServiceAccount のものを写す

参考:Managing Service Accounts | Kubernetes、Admission Control in Kubernetes | Kubernetes

公式ドキュメントはこのアドミッションコントローラーを、多くのディストリビューションで既定で有効なものと説明しています。v1.37 の公式ドキュメントでも、ServiceAccount は既定で有効なアドミッションコントローラーの一覧に入っています。

参考:Managing Service Accounts | Kubernetes、Admission Control in Kubernetes | Kubernetes

serviceAccountName を書けるのは、Pod を作るときか、新しい Pod のテンプレートの中だけです。すでにある Pod の spec.serviceAccountName は変えられません。ServiceAccount の名前は、DNS サブドメインの形でなければなりません(must)。spec.serviceAccount というフィールドもありますが、これは serviceAccountName の古い別名で、非推奨です。

参考:Configure Service Accounts for Pods | Kubernetes、Pod | Kubernetes API Reference

トークンが Pod に届くまで

Pod の中のプログラムが ServiceAccount を名乗るには、認証に使うトークンが要ります。公式ドキュメントによると、v1.22 以降の Kubernetes は、TokenRequest の API(ServiceAccount の期限付きのトークンを発行してもらうための API)で、期限が短く自動で入れ替わるトークンを取得し、projected ボリューム(第26回)として Pod にマウントします。以下は公式ドキュメントに載っている、動いている Pod に足されたボリュームの例です。

yaml
...
  - name: kube-api-access-<random-suffix>
    projected:
      sources:
        - serviceAccountToken:
            path: token # must match the path the app expects
        - configMap:
            items:
              - key: ca.crt
                path: ca.crt
            name: kube-root-ca.crt
        - downwardAPI:
            items:
              - fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.namespace
                path: namespace

参考:Service Accounts | Kubernetes、Managing Service Accounts | Kubernetes

ボリュームの名前は kube-api-access- にランダムな文字列が続く形です。このボリュームは 3 つの材料をまとめていて、それぞれが 1 つのファイルになります。3 つの材料を次の表にまとめます。

材料中身ファイル
serviceAccountTokenkubelet が kube-apiserver から取得したトークンtoken
configMap(kube-root-ca.crt)認証局の証明書。途中の機器ではなく本物の kube-apiserver に接続しているかを確かめるのに使うca.crt
downwardAPIPod がある Namespace の名前(第19回)namespace

参考:Managing Service Accounts | Kubernetes

kube-root-ca.crt の ConfigMap を誰が用意するかは、このメディアで確かめた範囲の公式ドキュメントには書かれていません。projected ボリュームの材料は Pod と同じ Namespace になければならないので、この ConfigMap は Namespace ごとにあるはずです。kube-controller-manager のコントローラーの一覧に root-ca-certificate-publisher-controller があるので、これが用意していると考えられます。

参考:Projected Volumes | Kubernetes、kube-controller-manager | Kubernetes

ボリュームを Pod に足すのは、前の節の ServiceAccount のアドミッションコントローラーです。トークンを取得するのは kubelet で、TokenRequest の API を使って kube-apiserver から期限付きのトークンを受け取ります。トークンはその Pod に結び付けられ、Pod が消されるか、決まった期間(既定では 1 時間)が過ぎると期限が切れます。kubelet は期限が切れる前にトークンを取り直します。

参考:Managing Service Accounts | Kubernetes、Managing Service Accounts | Kubernetes

Pod の中のプログラムはマウントされたファイルを使って API を呼びます。公式ドキュメントが勧めるのは、公式のクライアントライブラリを使う方法で、kube-apiserver の場所を見つけて認証するところまで自動で行えます。ライブラリを使わずに直接呼ぶ場合は、次の 3 つを組み合わせます。

  • 接続先:kubernetes.default.svc(第21回の kubernetes の Service)か、環境変数 KUBERNETES_SERVICE_HOST と KUBERNETES_SERVICE_PORT_HTTPS
  • 認証:token の中身を Authorization: Bearer <トークン> のヘッダーで送る
  • 接続先の確認:ca.crt で kube-apiserver の証明書を確かめる

参考:Accessing the Kubernetes API from a Pod | Kubernetes、Accessing the Kubernetes API from a Pod | Kubernetes、Service Accounts | Kubernetes

ヘッダーの名前は Authorization(英語で「認可」)ですが、中身は認証に使うトークンです。一般に HTTP では認証情報を送るヘッダーの名前が Authorization と決まっていて、このヘッダーは Kubernetes の認可の段階とは別のものです。

環境変数のほうは kubelet が入れるものです。公式ドキュメントによると、kubelet は Pod を動かすとき、動いている Service ごとに {SVCNAME}_SERVICE_HOST のような環境変数を入れます。KUBERNETES_SERVICE_HOST は kubernetes の Service の分だと考えられます。

参考:Service | Kubernetes

トークンは入れ替わるので、プログラムは読み直す必要があります。v1.37 時点の公式ドキュメントは、入れ替わったトークンを読み直すのはアプリケーションの責任だとしています(トークンを自分で投影する例の節の説明ですが、既定のトークンにも当てはまると考えられます)。一方、Pod を消すと、その Pod に結び付いたトークンは期限切れになります。TokenRequest で発行したトークンを無効にする専用の仕組みはありません。トークンを信頼できなくなったら、その Pod を消せます。

参考:Configure Service Accounts for Pods | Kubernetes、Managing Service Accounts | Kubernetes

トークンを自動でマウントしない

API を使わない Pod にも、既定ではトークンがマウントされます。マウントをやめるには、automountServiceAccountToken: false を書きます。公式ドキュメントの例では、Pod なら spec の中に書き、ServiceAccount なら kind や metadata と同じ階層に書きます。ServiceAccount と Pod の両方に値が書かれていたら、Pod の値が優先されます。RBAC の推奨のページはクラスタのセキュリティを強化する(hardening)方法の 1 つとして、automountServiceAccountToken: false で既定の自動マウントを避けることを挙げています。Pod に書けば ServiceAccount の設定を上書きするので、トークンが要るワークロードはマウントできる、とも書いています。

参考:Configure Service Accounts for Pods | Kubernetes、Role Based Access Control Good Practices | Kubernetes

第18回の Secret のトークンとの関係

第18回で見た kubernetes.io/service-account-token の Secret は、v1.22 より前の Kubernetes が、期限の無いトークンを Pod に渡すのに使っていた古い仕組みです。ServiceAccount ごとにこの Secret が自動で作られていたのは v1.24 より前までで、今でも手で作れますが、公式ドキュメントは TokenRequest で期限の短いトークンを取得する方法を勧めています。

参考:Service Accounts | Kubernetes、Service Accounts | Kubernetes、Managing Service Accounts | Kubernetes

RBAC の 4 つのオブジェクト

RBAC の 4 つのオブジェクトの範囲と中身を、次の表にまとめます。

オブジェクト範囲中身
RoleNamespace に属する。権限はその Namespace の中だけ権限の集まり(rules)
ClusterRoleNamespace に属さない権限の集まり(rules)
RoleBindingNamespace に属する。権限を与えるのはその Namespace の中だけ誰に(subjects)、どのロールを(roleRef)
ClusterRoleBindingNamespace に属さない。クラスタ全体に与える誰に(subjects)、どのロールを(roleRef)

参考:Using RBAC Authorization | Kubernetes、Using RBAC Authorization | Kubernetes

Role と ClusterRole が別の種類なのは、Kubernetes のオブジェクトは Namespace に属するか属さないかのどちらかで、両方にはなれないからです。公式ドキュメントによると、ClusterRole は Namespace に属するリソースの権限を個々の Namespace の中か、すべての Namespace にまたがって与えるのに使えます。ノードのような、クラスタ全体のリソースの権限を決めるのにも使えます。

参考:Using RBAC Authorization | Kubernetes

以下は公式ドキュメントの Role の例のうち、Pod を読む権限を表す rules の部分です。後半の手順の pod-reader.yaml はこの例をもとにしています。

yaml
rules:
- apiGroups: [""] # "" indicates the core API group
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

rules の 1 つ 1 つを、API リファレンスでは PolicyRule と呼びます。主なフィールドを次の表にまとめます。

フィールド意味
apiGroupsリソースが属する API グループ。"" はコアのグループ(第5回)、"*" はすべてのグループ
resources対象のリソース。API の URL に出る名前(pods など)で書く。"*" はすべてのリソース
verbs許す操作。必須のフィールド。"*" はすべての操作
resourceNames対象を名前で絞る(省略できる)。空ならすべてを許す

参考:Using RBAC Authorization | Kubernetes、PolicyRule | Kubernetes API Reference、Using RBAC Authorization | Kubernetes

verbs には第18回で見た get、list、watch のほか、create、update、patch、delete、deletecollection などを書きます。公式ドキュメントによると、kube-apiserver は HTTP のメソッドなどから操作を決めていて、たとえば POST のリクエストは create にあたります。

参考:Authorization | Kubernetes

リソースの中には、サブリソースを持つものがあります。たとえば Pod のログ(第9回)を読むリクエストは GET /api/v1/namespaces/{namespace}/pods/{name}/log で、log は pods のサブリソースです。RBAC では pods/log のように / で区切って書きます。第22回で見たサービスのコントローラーも、公式ドキュメントによると Service を更新するために status のサブリソースへの patch と update の権限を必要とします。同じページの ClusterRole の例には services/status が書かれています。

参考:Using RBAC Authorization | Kubernetes、Cloud Controller Manager | Kubernetes

resourceNames には制限があり、deletecollection と、サブリソースではない create は名前で絞れません。list や watch を名前で絞ると、クライアントは metadata.name のフィールドセレクタ(第37回)を付けてリクエストしなければ許されません。

参考:Using RBAC Authorization | Kubernetes

権限は足し算だけ

公式ドキュメントによると、Role と ClusterRole の 権限は足し算だけで、「拒否」の規則はありません。許したい操作をロールに書いて足していくだけで、ある操作を禁じる規則は書けません。禁じたい操作があるときは、それを許しているロールや結び付けを見直すことになると考えられます。

参考:Using RBAC Authorization | Kubernetes

"*" を使うと短く書けますが、公式ドキュメントは注意を促しています。新しい種類のリソースやサブリソースが加わると、ワイルドカードの規則は自動でそのアクセスも許してしまうからです。最小権限の原則に従い、具体的なリソースと操作を書いて、ワークロードが正しく動くのに要る権限だけを与えるべきだ(should)としています。

参考:Using RBAC Authorization | Kubernetes、Role Based Access Control Good Practices | Kubernetes

RoleBinding と ClusterRoleBinding

RoleBinding と ClusterRoleBinding は、ロールに書いた権限を利用者などに与えます。中身は権限を与える相手(subjects)の一覧と、与えるロールへの参照(roleRef)です。RoleBinding は同じ Namespace の Role を参照できます。さらに RoleBinding から ClusterRole を参照することもでき、そのときの権限は RoleBinding の Namespace の中だけで効きます。

公式ドキュメントの例は development の Namespace の RoleBinding で、subjects に利用者 dave が、roleRef に ClusterRole の secret-reader(Secret を読む権限)が書かれています。クラスタ全体のロールを指していても、dave が Secret を読めるのは development の Namespace の中だけです。RoleBinding の metadata.namespace が development だからです。結び付けとロールの組み合わせごとに、権限が効く範囲を次の表にまとめます。

組み合わせ権限が効く範囲
RoleBinding と RoleRoleBinding の Namespace の中
RoleBinding と ClusterRoleRoleBinding の Namespace の中。共通のロールを ClusterRole で作り、いくつもの Namespace で使い回せる
ClusterRoleBinding と ClusterRoleクラスタ全体(すべての Namespace)

参考:Using RBAC Authorization | Kubernetes、Using RBAC Authorization | Kubernetes

結び付けを作ったあとは、roleRef が指すロールを変えられません。roleRef を変えようとすると検証のエラーになり、API リファレンスにもこのフィールドは変えられない(immutable)とあります。変えたいときは、結び付けを消して作り直します。マニフェストから RBAC のオブジェクトを作ったり更新したりする kubectl auth reconcile を使えば、この削除と作り直しも行ってくれます。

参考:Using RBAC Authorization | Kubernetes、Using RBAC Authorization | Kubernetes、RoleBinding | Kubernetes API Reference

subjects に書く名前

subjects に書ける相手は利用者(User)、グループ(Group)、ServiceAccount の 3 種類です。公式ドキュメントによると、Kubernetes は利用者名を文字列として扱います。alice のような名前、メールアドレスの形の名前、数字の ID のどれでもかまいません。どんな形の利用者名にするかは、クラスタの管理者が認証の設定で決めます。グループの名前も文字列です。ただし system: で始まる名前は Kubernetes のシステムのために予約されています。そのため、うっかり system: で始まる利用者やグループを作らないようにするべきだ(should)とされています。

参考:Using RBAC Authorization | Kubernetes

ServiceAccount は認証されると決まった形の利用者名とグループを持ちます。認証のページによると、利用者名は system:serviceaccount:<Namespace>:<ServiceAccount の名前> です。グループは system:serviceaccounts と system:serviceaccounts:<Namespace> の 2 つです。利用者名の接頭辞は単数形(serviceaccount)、グループの接頭辞は複数形(serviceaccounts)です。

参考:Authenticating | Kubernetes、Using RBAC Authorization | Kubernetes

subjects に ServiceAccount を書くときは、公式ドキュメントの例のとおり、この長い名前ではなく kind: ServiceAccount に name と namespace を添えます(後半の pod-reader.yaml も同じ形です)。Namespace のすべての ServiceAccount に与えるときは、kind: Group にして name に system:serviceaccounts:<Namespace> を書きます。

参考:Using RBAC Authorization | Kubernetes

ServiceAccount への権限の与え方には、細かく与えるものから広く与えるものまであります。公式ドキュメントが安全な順に挙げる 5 つの与え方を、次の表にまとめます。

与え方公式ドキュメントの位置付け
アプリケーション専用の ServiceAccount にロールを与えるいちばん安全(ベストプラクティス)。Pod に serviceAccountName を書き、ServiceAccount を作る必要がある
Namespace の default にロールを与えるserviceAccountName を書かないその Namespace のすべての Pod が、この権限を使える
Namespace のすべての ServiceAccount に与えるグループ system:serviceaccounts:<Namespace> に与える
クラスタのすべての ServiceAccount に、限られたロールを与える勧めない(discouraged)
クラスタのすべての ServiceAccount に、スーパーユーザーの権限を与える強く勧めない(strongly discouraged)

参考:Using RBAC Authorization | Kubernetes

細かく与えるほど安全ですが、管理の手間は増えます。広く与えると管理は楽ですが、ServiceAccount に要らない権限を与えてしまい、権限を広げられる危険もあります。なお RBAC の推奨のページは、ある Namespace でワークロードを作る権限を持つ人は、その Namespace のどの ServiceAccount の API の権限も実質的に使えると注意しています。Pod はその Namespace のどの ServiceAccount としても動かせるからです。同じ節には Pod にマウントできる Secret などにも実質的にアクセスできると書かれています。これは第18回で見た「Pod を作れる人は Secret の値を見られる」という注意につながる内容です。

参考:Using RBAC Authorization | Kubernetes、Role Based Access Control Good Practices | Kubernetes

既定の ClusterRole

kube-apiserver は既定の ClusterRole と ClusterRoleBinding をいくつか作ります。多くは system: で始まる名前で、クラスタのコントロールプレーンが直接管理するものです。公式ドキュメントは system: で始まる名前の ClusterRole や ClusterRoleBinding を変えると、クラスタが動かなくなることがあると注意しています。RBAC の認可モードが有効なら、kube-apiserver は起動するたびに、既定のロールの足りない権限と、既定の結び付けの足りない相手を補います(注釈で外さない限り)。

参考:Using RBAC Authorization | Kubernetes、Using RBAC Authorization | Kubernetes

system: で始まらない既定の ClusterRole は、利用者が使うためのものです。その 4 つを、公式ドキュメントの説明に沿って次の表にまとめます。

ClusterRole既定の結び付け公式ドキュメントの説明
cluster-adminsystem:masters のグループどのリソースにもどの操作もできるスーパーユーザーの権限。RoleBinding で使うと、その Namespace(Namespace 自身も含む)のすべてのリソースを制御できる
adminなしRoleBinding で Namespace の中に与えることを想定。ほとんどのリソースを読み書きでき、Role と RoleBinding も作れるが、ResourceQuota と Namespace 自身には書き込めない
editなしNamespace のほとんどのオブジェクトを読み書きでき、Secret も読める。Role と RoleBinding は見られない
viewなしNamespace のほとんどのオブジェクトを読むだけ。Secret の中身から ServiceAccount の認証情報が読めてしまうので、Secret は見られない

参考:Using RBAC Authorization | Kubernetes

既定の結び付けは認証に成功したすべての利用者(system:authenticated のグループ)に、公開しても安全とされる API の情報を読むことも許しています。この情報はどんな API があるかの一覧などで、後半で呼ぶ /api もその 1 つと考えられます。公式ドキュメントによると、RBAC が有効なら、各 Namespace の default の ServiceAccount は、既定ではこの API の情報を読む権限(API discovery)しか持ちません。既定の RBAC の決まりは kube-system 以外の Namespace の ServiceAccount にも、これ以外の権限を与えません。

参考:Using RBAC Authorization | Kubernetes、Using RBAC Authorization | Kubernetes、Service Accounts | Kubernetes、Authenticating | Kubernetes

kind が書き込んだ kubeconfig の利用者(第3回)がどんな権限を持つかは公式ドキュメントには書かれていないので、ここで述べるのは推測です。kind は、kubeadm がコントロールプレーンのノードに作る管理者用の kubeconfig(admin.conf)を、手元の kubeconfig に書き込んでいると読めます。v1.37 時点の kubeadm の公式ドキュメントによると、admin.conf の証明書の O は kubeadm:cluster-admins で、このグループは cluster-admin に結び付けられます。そのため kind の利用者は cluster-admin の権限を持ち、クラスタのどの操作もできると考えられます。後半の手順で確かめます。

参考:kind のソースコード(kubeconfig)、Implementation details | Kubernetes

RBAC の推奨のページは、管理者は特に必要な場合を除いて cluster-admin のアカウントを使うべきではない(should not)としています。手元の学習用のクラスタでは気にしなくてかまいませんが、仕事のクラスタでは利用者ごとに権限を分けることになると考えられます。cluster-admin に既定で結び付けられている system:masters のグループには、特別な扱いがあります。同じページによると、このグループの利用者は RBAC のすべての確認を受けずに、いつでも制限の無いスーパーユーザーの権限を持ちます。この権限は RoleBinding や ClusterRoleBinding を消しても取り消せません。そのため、このページは利用者をこのグループに足すことを避けるよう(Avoid)勧めています。

参考:Role Based Access Control Good Practices | Kubernetes

API のリクエストが許されるか断られるか

以下の図は、Pod の中のプログラムが ServiceAccount のトークンで API を呼び、kube-apiserver が許すか断るかを決める流れを表しています。

Pod の中のプログラムが API を呼んだときの、認証と認可の流れ

戻る 2 本の線のうち、赤の実線は拒否の道です。灰色の破線はこのシリーズの図で応答に使う線で、許可されて結果が返る道を表します。図は Pod の中から呼ぶ道だけを描いていて、後半で使う kubectl の --as の道はありません。

kube-apiserver はまず Authorization: Bearer のヘッダーで受け取ったトークンを確かめます。公式ドキュメントによると、署名、期限、トークンの中のオブジェクトへの参照などを順に確かめます。トークンが正しければ、リクエストは system:serviceaccount:rbac-demo:demo-sa の利用者からのものとして扱われます。図では認証の細部を省いています。

参考:Service Accounts | Kubernetes、Authenticating | Kubernetes

次に認可です。公式ドキュメントによると、認可は kube-apiserver の中で行われます。kube-apiserver は、利用者、グループ、操作(verb)、リソース、Namespace、API グループなどのリクエストの属性を、決まりと照らし合わせます。いくつもの認可モードがあるときは順に調べ、どれかが許すか拒否すれば、その結果がすぐに返ります。すべてのモードが「意見なし」なら、リクエストは拒否されます。全体として拒否になったとき、kube-apiserver は HTTP の 403(Forbidden)で応答します。RBAC は利用者やグループに結び付けられたロールに、その操作を許す規則があるかを調べると読めます。後半の手順のように RoleBinding がまだ無く、ほかのモードも許さなければ、403 で断られると考えられます。許されたリクエストがその先でどう処理されるか(アドミッションなど)は、第40回で扱います。

参考:Authorization | Kubernetes、Authorization | Kubernetes、Using RBAC Authorization | Kubernetes

権限を確かめるだけなら、実際に API を呼ばなくてもかまいません。kubectl auth can-i は、ある操作が許されるかを認可の仕組みに問い合わせるコマンドです。公式ドキュメントによると、SelfSubjectAccessReview の API を使い、今の利用者がその操作をできるかを調べます。どの認可モードを使っていても動きます。

参考:Authorization | Kubernetes、kubectl auth can-i | Kubernetes

ほかの利用者の権限を調べるには、なりすまし(impersonation)と組み合わせます。なりすましは不正な行為ではなく正規の機能です。たとえば管理者がほかの利用者として一時的にリクエストを送り、断られるかを見て認可の決まりを調べるときに使います。公式ドキュメントによると、なりすましのリクエストはまず送った人の認証情報で認証されます。次に kube-apiserver はその人になりすましの権限があるかを確かめ、利用者の情報をなりすます先のものに置き換えます。認可はなりすました利用者の情報で行われます。kubectl の --as はなりすます利用者名を Impersonate-User のヘッダーで送る指定です。リファレンスによると、--as には普通の利用者のほか、Namespace の ServiceAccount も指定できます。なりすましには、なりすましを許す権限が要ります。従来の impersonate の操作の権限のほか、v1.37 時点では制約付きのなりすまし(Constrained Impersonation)の権限でもかまいません。制約付きのなりすましは v1.36 から Beta で既定で有効で、フィーチャーゲートは ConstrainedImpersonation です。kube-apiserver は先に制約付きのなりすましの権限を確かめ、それで許されなければ impersonate の権限を確かめます。kubectl auth can-i のリファレンスの例も --as を使うにはなりすましを許されていなければならない(must)と書いています。

参考:User Impersonation | Kubernetes、User Impersonation | Kubernetes、User Impersonation | Kubernetes、kubectl auth can-i | Kubernetes

手元で試す

ここまでの内容を手元の kind のクラスタで試す手順を示します。まず自分の利用者の権限を確かめ、Namespace と ServiceAccount を作ります。権限の無い ServiceAccount で操作が断られるのを見てから、最後に Role と RoleBinding を足して、同じ操作が許されることを確かめます。

断られることは 3 つの方法で確かめます。3 つの方法の違いを次の表にまとめます。

確かめ方どこで動くか認証と認可わかること
kubectl auth can-i --as手元の kubectlkind の利用者で認証し、demo-sa として許されるかを問い合わせる許されるかどうか(操作はしない)
kubectl get --as手元の kubectlkind の利用者で認証し、demo-sa として認可される実際の操作が断られるか
Pod の中の curlPod の中demo-sa のトークンで認証し、demo-sa として認可される認証から認可までの本物の流れ(図の道)

参考:User Impersonation | Kubernetes、Authorization | Kubernetes

この節で引用する出力の例は、公式ドキュメントに載っている、ほかのクラスタで取ったものです。名前や時間などの値は手元とは違います。各段に書く成功の目安は公式ドキュメントから予想される結果で、手元で実行して確かめたものではありません。

コマンドは macOS や Linux のシェル(bash や zsh)で実行する前提で書いています。Windows では WSL(Windows の中で Linux を動かす仕組み)などを使ってください。

試す環境は次のとおりです。

項目内容
kindv0.33.0
Kubernetesv1.37.0(kind v0.33.0 の既定のノードイメージ)
ノード構成シングルノード(クラスタ名 kind):コントロールプレーン 1
必要なものDocker、kubectl(v1.36〜v1.38)、テキストエディタ、端末(ターミナル)2 つ

参考:Release v0.33.0 · kubernetes-sigs/kind、Version Skew Policy | Kubernetes

kind のクラスタで RBAC が有効になっているかは、kind の公式ドキュメントには書かれていません。kind は各ノードを kubeadm で初期化し(第2回)、kubeadm の設計の原則には「RBAC を強制する」があります。そのため、kind のクラスタでも RBAC は有効だと考えられます。

参考:Implementation details | Kubernetes

この回では第37回と同じく、シングルノードのクラスタを使います。権限を確かめるのは kube-apiserver の仕事で、ノードが 1 つでも確かめられるからです。kind でクラスタを作る手順は、以下の記事で解説しています。

基本仕様 ・ 第2回 kind で検証用のクラスタを作るDocker のコンテナをノードにして、手元のマシンに Kubernetes のクラスタを作るツール kind の仕組みを図で説明します。kind create cluster で何が起きるかを押さえ、シングルノードとマルチノードのクラスタを作って、ノードの正体が Docker のコンテナであることを確かめる手順を紹介します。

この回では端末を 2 つ使います。これまでの回と同じく、1 つ目の端末は変化の監視(この回では Pod)に取っておきます。クラスタの準備も含めて、操作は 2 つ目の端末で行います。2 つ目の端末を開いたら、cd でマニフェストを保存するディレクトリに移動しておきます。

シングルノードのクラスタを用意する

2 つ目の端末で、前の回のクラスタが残っていないかを確かめます。

console
$ kind get clusters

kind が表示されたら、作り直さずにそのまま使えるので、クラスタを作る次のコマンドは飛ばします。表示されなければ、クラスタを作ってから、もう一度一覧します。前の回までの multi-node が表示されても、この回では使いません。手元のマシンの資源(メモリなど)に余裕がなければ、kind delete cluster --name multi-node で消してから進めてください。

console
$ kind create cluster
$ kind get clusters

kind get clusters に kind が表示されれば、クラスタができています。第3回で決めたとおり、ここからのコマンドには --context kind-kind を付けて、操作するクラスタをはっきりさせます。続けて、ノードを一覧します。

console
$ kubectl get nodes --context kind-kind

ノードが 1 つ表示され、STATUS 列が Ready になっていれば準備ができています。Ready になっていなければ、少し待ってからもう一度実行します。

参考:Quick Start | kind、kubectl get | Kubernetes

前の回のクラスタを使い続けている場合は、この回で使う rbac-demo の Namespace が無いことも確かめます。

console
$ kubectl get namespace rbac-demo --context kind-kind

見つからないことを知らせる 1 行が出れば、この回の手順をそのまま進められます。表示されたら、自分で作ったものかを確かめます。消すと中のリソースも一緒に消えるので(第37回)、残したいものが無いかも先に確かめてください。そのうえで kubectl delete namespace rbac-demo --context kind-kind で消します。

自分の権限を確かめる

2 つ目の端末で、kind の利用者がすべての操作をできるかを確かめます。公式ドキュメントの例のとおり、'*' '*' は「今の Namespace で、すべてのリソースにすべての操作をできるか」を表します。* を ' で囲むのは、一般にシェルが * をファイル名の一覧に置き換えてしまうからです。今の Namespace は、コンテキストに Namespace が無ければ default だと考えられます(第37回)。

console
$ kubectl auth can-i '*' '*' --context kind-kind

参考:kubectl auth can-i | Kubernetes

公式ドキュメントの例(kubectl auth can-i create deployments --namespace dev)では、許されるときの出力は次の 1 行です。

yes

参考:Authorization | Kubernetes

yes が表示されれば、「既定の ClusterRole」の節の推測(kind の利用者は cluster-admin の権限を持つ)に合います。cluster-admin ならどの操作もできるので、このあと --as で使う impersonate の権限も持っているはずです。no が表示されたら、まず --context kind-kind を付け忘れて、別のクラスタに問い合わせていないかを見直します。付けていても no なら推測が外れていて、--as を使う段はこの記事のとおりには進まないと考えられます。

Namespace と ServiceAccount を作る

以下はこの回で使う Namespace rbac-demo と、その中の ServiceAccount demo-sa のマニフェストです。ServiceAccount の書き方は公式ドキュメントの例をもとにし、名前と Namespace をこの回のものにしました。rbac-demo.yaml という名前で保存します。

yaml
apiVersion: v1
kind: Namespace
metadata:
  name: rbac-demo
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: demo-sa
  namespace: rbac-demo

参考:Configure Service Accounts for Pods | Kubernetes、ServiceAccount | Kubernetes API Reference

2 つ目の端末で、マニフェストを渡します。

console
$ kubectl apply -f rbac-demo.yaml --context kind-kind

namespace/rbac-demo と serviceaccount/demo-sa が作られたことを知らせる 2 行が出るはずです。続けて、rbac-demo の ServiceAccount を一覧します。

console
$ kubectl get serviceaccounts -n rbac-demo --context kind-kind

公式ドキュメントの例では次のように表示されています。

NAME      SECRETS    AGE
default   1          1d

参考:Configure Service Accounts for Pods | Kubernetes

手元の表示は、例と次の点が違うと考えられます。

違い手元の表示
並ぶ ServiceAccountdefault に加えて demo-sa の行もある。default はマニフェストに書いていないが、ServiceAccount のコントローラーが作る
SECRETS 列0 になると考えられる。トークンの Secret が ServiceAccount ごとに自動で作られていたのは、v1.24 より前のため
AGE 列作ってからの時間なので、数秒から数分の短い値

参考:Managing Service Accounts | Kubernetes、Service Accounts | Kubernetes

default はコントローラーが非同期に作ると考えられるので、Namespace を作った直後は、まだ並ばないこともあると考えられます。そのときは少し待ってから、もう一度一覧します。この段では、default と demo-sa の 2 つの行が表示されれば成功です。

Pod を監視する

1 つ目の端末で、rbac-demo の Pod を監視します。この監視は片付けの前まで止めずに続けます。

console
$ kubectl get pods -n rbac-demo --watch --context kind-kind

参考:kubectl get | Kubernetes

まだ Pod が無いので、何も表示されないか、Pod が無いことを知らせる 1 行が出て、止まったように見えると考えられます。Pod を作ると、この端末に行が書き足されます。

権限の無い ServiceAccount で確かめる

2 つ目の端末で、demo-sa が rbac-demo の Pod を一覧できるかを確かめます。--as の値は ServiceAccount の利用者名の形(system:serviceaccount:<Namespace>:<名前>)で書きます。

console
$ kubectl auth can-i list pods -n rbac-demo --as=system:serviceaccount:rbac-demo:demo-sa --context kind-kind

参考:kubectl auth can-i | Kubernetes、Authorization | Kubernetes

公式ドキュメントの例(kubectl auth can-i create deployments --namespace prod)では、許されないときの出力は次の 1 行です。

no

参考:Authorization | Kubernetes

手元でも no が表示されるはずです。demo-sa にはまだ何のロールも結び付けていないので、API の情報を読む権限しか持たないからです。

今度は問い合わせるだけでなく、実際に操作させます。--as は kubectl auth can-i 以外のコマンドにも付けられます。手元の kubectl は kind の利用者の証明書で認証し、認可は demo-sa として行われます。demo-sa になりすまして、rbac-demo の Pod を一覧します。

console
$ kubectl get pods -n rbac-demo --as=system:serviceaccount:rbac-demo:demo-sa --context kind-kind

参考:User Impersonation | Kubernetes、kubectl get | Kubernetes

なりすましのページには、権限の無い利用者のコマンドが断られた例があります。利用者 clark が kubectl drain mynode を実行したときの出力は、次のとおりです。

Error from server (Forbidden): User "clark" cannot get nodes at the cluster scope. (get nodes mynode)

参考:User Impersonation | Kubernetes

手元でも、例と同じく Error from server (Forbidden): で始まる 1 行が出ると考えられます。見るのは Forbidden の語と、例の User "clark" にあたる部分です。手元ではそこに、なりすました利用者名 system:serviceaccount:rbac-demo:demo-sa が入ると考えられます。続く文面は対象のリソースも例と違うので、例のとおりにはなりません。1 つ目の端末には変化が無いはずです。

この段では、kubectl auth can-i が no を返し、kubectl get pods が Forbidden で断られれば成功です。

ServiceAccount を使う Pod を作る

次は demo-sa の本物のトークンで API を呼びます。公式ドキュメントのシェルのスクリプトは curl を使うので、curl の入ったイメージ curlimages/curl を使います。このイメージは起動すると curl を 1 回動かして終わると考えられます(Docker Hub のページの例では、イメージに curl の引数をそのまま渡しています)。そこで command を sleep 3600 に替えて、Pod を動かし続けます。以下は demo-sa を使う Pod のマニフェストで、api-client.yaml という名前で保存します。

yaml
apiVersion: v1
kind: Pod
metadata:
  name: api-client
  namespace: rbac-demo
spec:
  serviceAccountName: demo-sa
  containers:
  - name: curl
    image: curlimages/curl:8.23.0
    command: ["sleep", "3600"]

参考:Pod | Kubernetes API Reference、curlimages/curl | Docker Hub

sleep 3600 は 1 時間で終わります。途中で長く休むとコンテナが終わって再起動され(第9回の restartPolicy の既定は Always)、RESTARTS 列が増えることがありますが、手順はそのまま続けられると考えられます。

イメージに sleep やシェル(sh)があるかは Docker Hub のページに書かれていませんが、リリースノートから alpine(小さな Linux のディストリビューション)をもとにしたイメージと読めるので、あると考えられます。もし無ければ、Pod が Running にならないか、あとの kubectl exec でシェルが見つからないことを知らせるエラーが出ると考えられます。

2 つ目の端末で、Pod を作ります。

console
$ kubectl apply -f api-client.yaml --context kind-kind

pod/api-client が作られたことを知らせる 1 行が出るはずです。1 つ目の端末に api-client の行が現れ、STATUS 列が Running になるまで待ちます。初めて使うイメージなので、取得に時間がかかることがあり、そのあいだは STATUS 列に ContainerCreating のような値が出ると考えられます(第9回)。かかる時間はネットワークによって違います。ErrImagePull や ImagePullBackOff が出たり、なかなか Running にならなかったりしたら、第41回のとおり kubectl describe pod api-client -n rbac-demo --context kind-kind の Events: で理由を見ます。

Running になったら、Pod の定義を YAML で表示します。

console
$ kubectl get pod api-client -n rbac-demo -o yaml --context kind-kind

spec の中で次の 3 か所を探します。マニフェストには書いていない 2 つ目と 3 つ目は、ServiceAccount のアドミッションコントローラーが足したものです。

  • serviceAccountName: demo-sa
  • volumes の、名前が kube-api-access- で始まり、projected の sources に 3 つの材料が並ぶボリューム
  • コンテナ curl の volumeMounts の、mountPath: /var/run/secrets/kubernetes.io/serviceaccount

参考:Managing Service Accounts | Kubernetes、Managing Service Accounts | Kubernetes

ボリュームの名前の kube-api-access- に続く部分は、手元ではランダムな文字列になります。同じページの別の例(TokenRequest API の節)には expirationSeconds: 3607 や defaultMode: 420 が並んでいるので、手元でもこうした項目が並ぶと考えられます。

参考:Managing Service Accounts | Kubernetes

この段では、1 つ目の端末で api-client が Running になり、YAML に上の 3 か所が見つかれば成功です。

Pod の中から API を呼ぶ

2 つ目の端末で、Pod のコンテナの中でシェルを動かします。kubectl exec は第10回で使った、コンテナの中でコマンドを動かすコマンドです。-it は、端末からコマンドを打てるようにする指定です。

console
$ kubectl exec -it api-client -n rbac-demo --context kind-kind -- sh

参考:kubectl exec | Kubernetes

ここから 2 つ目の端末で打つコマンドは、手元ではなく Pod api-client の curl コンテナの中で動きます。プロンプトの形はイメージによって違うので、コンテナの中で打つコマンドは行頭に $ を付けずに書きます。exit でシェルを抜けると、手元のシェルのプロンプト($)に戻ります。

まず、トークンがマウントされているディレクトリの中身を一覧します。

shell
ls /var/run/secrets/kubernetes.io/serviceaccount

ca.crt、namespace、token の 3 つが並ぶはずです。「トークンが Pod に届くまで」で見た、3 つの材料のファイルです。

続けて、公式ドキュメントのスクリプトをそのまま打ちます。このスクリプトは接続先、トークン、Namespace、認証局の証明書を変数に入れ、トークンを付けて /api を呼びます。kubernetes.default.svc はクラスタの中の DNS 名(第23回)なので、手元の端末で打ってもつながらないはずです。

shell
# Point to the internal API server hostname
APISERVER=https://kubernetes.default.svc

# Path to ServiceAccount token
SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount

# Read this Pod's namespace
NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)

# Read the ServiceAccount bearer token
TOKEN=$(cat ${SERVICEACCOUNT}/token)

# Reference the internal certificate authority (CA)
CACERT=${SERVICEACCOUNT}/ca.crt

# Explore the API with TOKEN
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api

公式ドキュメントの例では、出力は次のとおりです。

json
{
  "kind": "APIVersions",
  "versions": ["v1"],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

参考:Accessing the Kubernetes API from a Pod | Kubernetes

"kind": "APIVersions" の JSON が返れば、トークンでの認証も認可も通っています。図の、許可されて結果が返る道を通る例です。/api は「既定の ClusterRole」の節で見た API の情報の 1 つで、demo-sa でも読めると考えられます。serverAddress の値は、手元のクラスタの kube-apiserver のアドレスになるはずです。

公式ドキュメントは kubernetes.default.svc の名前に正しい証明書があることは保証しないと注意しています。kubeadm が作る kube-apiserver の証明書にはこの名前が入るので、kind のクラスタではつながると考えられます。もし curl 自身が証明書のエラーを出したら、APISERVER を https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT_HTTPS} に替えて試します。KUBERNETES_SERVICE_HOST が表すアドレスには、正しい証明書が出されるはずだとされているからです。curl の公式の文書では、証明書を確かめられなかったことを表すエラーの番号は 60 です。curl がどんな形でエラーを表示するかは文書で確かめられなかったので、表示に 60 の番号が含まれるかを目安にします。

参考:Accessing the Kubernetes API from a Pod | Kubernetes、Implementation details | Kubernetes、libcurl - Error Codes、curl man page

次に、同じトークンで rbac-demo の Pod の一覧を求めます。パスは第5回で見た、コアのグループの Namespace に属するリソースの形です。

shell
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/namespaces/${NAMESPACE}/pods

今度は、トークンでの認証は通ったうえで、認可で断られるはずです。「API のリクエストが許されるか断られるか」で見たとおり、kube-apiserver は 403(Forbidden)で応答します。このときの応答の本文の例は、このメディアで確かめた範囲の公式ドキュメントにはありません。返ってきた応答の中に 403 や Forbidden の語があるかを探します(あると考えられます)。応答が前の段で見た curl 自身の証明書のエラー(番号 60)ではなく、Pod の一覧(api-client の情報)も含まれていなければ、拒否されたと見てかまいません。

参考:Authorization | Kubernetes

確かめたら、exit を入力してシェルを抜けます。シェルを抜けると、さきほどの変数は消えます。

shell
exit

この段では、/api の呼び出しで APIVersions の JSON が返り、Pod の一覧の呼び出しが拒否されれば成功です。

Role と RoleBinding を足す

以下は rbac-demo の Pod を読む権限の Role と、それを demo-sa に結び付ける RoleBinding のマニフェストです。どちらも公式ドキュメントの Role と RoleBinding の例をもとにし、Namespace と subjects をこの回のものにしました。pod-reader.yaml という名前で保存します。

yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: rbac-demo
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: rbac-demo
subjects:
- kind: ServiceAccount
  name: demo-sa
  namespace: rbac-demo
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

参考:Using RBAC Authorization | Kubernetes、Using RBAC Authorization | Kubernetes、Role | Kubernetes API Reference、RoleBinding | Kubernetes API Reference、Subject | Kubernetes API Reference

subjects の ServiceAccount には apiGroup を書いていません。API リファレンスによると、ServiceAccount の場合の既定は "" だからです。

2 つ目の端末で、マニフェストを渡します。Pod やトークンは作り直さなくてよいはずです。

console
$ kubectl apply -f pod-reader.yaml --context kind-kind

Role の pod-reader と RoleBinding の read-pods が作られたことを知らせる 2 行が出るはずです。Role や RoleBinding を作っても Pod は変わらないので、1 つ目の端末には変化が無いはずです。

もう一度、demo-sa が rbac-demo の Pod を一覧できるかを確かめます。

console
$ kubectl auth can-i list pods -n rbac-demo --as=system:serviceaccount:rbac-demo:demo-sa --context kind-kind

今度は yes が表示されるはずです。公式ドキュメントにも、ServiceAccount dev-sa について同じ形のコマンドを実行して yes が返る例があります。続けて、許していない操作と、別の Namespace を確かめます。

console
$ kubectl auth can-i delete pods -n rbac-demo --as=system:serviceaccount:rbac-demo:demo-sa --context kind-kind
$ kubectl auth can-i list pods -n default --as=system:serviceaccount:rbac-demo:demo-sa --context kind-kind

参考:Authorization | Kubernetes

どちらも no が表示されるはずです。1 つ目は、Role の verbs に delete が無いからです。2 つ目は、RoleBinding が rbac-demo の Namespace のものなので、権限が default には効かないからです。権限は書いたものだけが足され、書いていない操作は許されません。

さきほど断られた kubectl get pods -n rbac-demo --as=system:serviceaccount:rbac-demo:demo-sa --context kind-kind も、今度は断られずに api-client の行が表示されるはずです。

最後に、Pod の中からもう一度呼びます。2 つ目の端末でシェルに入り直します。

console
$ kubectl exec -it api-client -n rbac-demo --context kind-kind -- sh

シェルを抜けたときに変数は消えているので、コンテナの中で変数の 5 行を打ち直してから、Pod の一覧を求めます。変数の行はさきほどの公式ドキュメントのスクリプトからコメントを除いたものです。

shell
APISERVER=https://kubernetes.default.svc
SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
TOKEN=$(cat ${SERVICEACCOUNT}/token)
CACERT=${SERVICEACCOUNT}/ca.crt
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/namespaces/${NAMESPACE}/pods

今度は Pod の一覧が JSON で返るはずです。長い JSON が流れるので、その中に api-client の文字があるかを探します。トークンは同じで、変わったのは RoleBinding が加わったことだけです。確かめたら exit でシェルを抜けます。

参考:Accessing the Kubernetes API from a Pod | Kubernetes、Using RBAC Authorization | Kubernetes

この段では、次の 3 点が合っていれば成功です。

  • kubectl auth can-i list pods が yes に変わり、delete と default の Namespace は no のまま
  • --as を付けた kubectl get pods で api-client の行が表示される
  • Pod の中からの呼び出しで、Pod の一覧が返る

片付け

1 つ目の端末で Ctrl+C を押して、Pod の監視を止めます。

2 つ目の端末で、rbac-demo の Namespace を消します。第37回で見たとおり、中の Pod、ServiceAccount、Role、RoleBinding も一緒に消えます。

console
$ kubectl delete namespace rbac-demo --context kind-kind

中の Pod が止まるのを待つと考えられるので、コマンドが終わるまでに少し時間がかかることがあります。コマンドが終わったら、残っていないかを確かめます。

console
$ kubectl get namespace rbac-demo --context kind-kind

見つからないことを知らせる 1 行が出れば、削除できています。STATUS 列が Terminating で表示されたら、少し待ってからもう一度実行します。

参考:Share a Cluster with Namespaces | Kubernetes

最後にクラスタを削除し、消えたことを確かめます。続けて次の回を試す場合は、クラスタを残しておいてもかまいません。

console
$ kind delete cluster
$ kind get clusters

kind delete cluster は存在しないクラスタを指定してもエラーを返しません。kind get clusters に kind が表示されなくなったことを確かめます。kubeconfig に kind-kind のコンテキストが残っていたら、第3回の片付けと同じく kubectl config delete-context kind-kind で消します。

参考:Quick Start | kind、kubectl config | Kubernetes

注意点

ServiceAccount と RBAC でつまずきやすい点と、後から変えられない設定を、次の表にまとめます。

項目内容
serviceAccountName を書かなければ defaultdefault に与えた権限は、serviceAccountName を書かないその Namespace のすべての Pod が使える
ServiceAccount は Pod より先に作る指定した ServiceAccount が無いと、ServiceAccount のアドミッションコントローラーが Pod を拒否する
Pod の ServiceAccount は後から変えられないすでにある Pod の spec.serviceAccountName は変えられない
トークンは期限付きで入れ替わる既定では 1 時間で、kubelet が取り直す。入れ替わったトークンを読み直すのはアプリケーションの責任とされる
API を使わない Pod にもトークンが付くautomountServiceAccountToken: false で避けられる。Pod の値が ServiceAccount の値より優先される
権限は足し算だけ拒否の規則は無い。既定ではアクセスは拒否され、ロールに書いて結び付けた操作だけが許される
roleRef は後から変えられない変えるには結び付けを消して作り直す(kubectl auth reconcile も使える)
ワイルドカード("*")は広すぎることがある新しく加わるリソースや操作も自動で許す。具体的なリソースと操作を書くべき(should)とされる
ワークロードを作れる人は ServiceAccount の権限も使えるPod はその Namespace のどの ServiceAccount としても動かせる。edit は Secret も読める
system:masters は RBAC の確認を受けない結び付けを消しても権限を取り消せない。利用者をこのグループに足すことは避ける(Avoid)よう勧められている
古い Secret のトークンv1.24 より前は自動で作られていた。今でも手で作れるが、TokenRequest で期限の短いトークンを取得する方法が勧められている

参考:Configure Service Accounts for Pods | Kubernetes、Managing Service Accounts | Kubernetes、Using RBAC Authorization | Kubernetes、Role Based Access Control Good Practices | Kubernetes、Role Based Access Control Good Practices | Kubernetes

関連記事

次の回では Pod に求めるセキュリティの基準を Namespace ごとに決める Pod Security Admission を扱います。

基本仕様 ・ 第39回 Pod Security Admission を動かして理解するPod に求めるセキュリティの基準を Namespace ごとに守らせる Pod Security Admission の仕組みを図で説明します。SecurityContext の主な項目、Pod Security Standards の 3 つの水準、enforce・audit・warn の違いを押さえ、root で動く Pod が水準によって拒否される様子を kind のクラスタで確かめる手順を紹介します。

Secret の get・list・watch を絞る理由と、古い ServiceAccount のトークンの Secret については、以下の記事で解説しています。

基本仕様 ・ 第18回 Secret の仕組みと安全な扱い方パスワードやトークンなどの機密のデータを Pod に渡す Secret の仕組みを図で説明します。ConfigMap との違い、base64 はエンコードであって暗号化ではないこと、etcd での保存時の暗号化や RBAC による読み取りの制限などの安全な扱い方を押さえ、kind のクラスタで Secret の値をデコードして確かめる手順を紹介します。

Role と RoleBinding が働く範囲になる Namespace については、以下の記事で解説しています。

基本仕様 ・ 第37回 Namespace で分けられるもの、分けられないもの1 つのクラスタの中でリソースをグループに分ける Namespace の仕組みを図で説明します。名前の範囲になること、Namespace に属さないクラスタ全体のリソース、既定では Namespace をまたいで届く通信、Namespace を消すと中身も消える流れを押さえ、kind のクラスタで確かめる手順を紹介します。

Kubernetes 基本仕様の目次へ