Kubernetesのユーザー管理と認証・権限確認機構を理解しよう

Kubernetesはさまざまな環境で利用され、かつ不特定多数がクラスタにアクセスできることを前提に構築されており、そのため非常に柔軟なユーザー認証機構やユーザーの権限を管理する機能が組み込まれている。本記事ではこれらの認証や権限確認機構がどのように働くのかや、その仕組みについて解説する。

Kubernetesにおける認証の必要性

Kubernetesはさまざまな企業・組織が参加するオープンな組織「Cloud Native Computing Foundation(CNCF)」によって開発が進められているが、元々はGoogleによってその開発がスタートしたプロジェクトであり、同社の持つコンテナクラスタ管理技術を元にしている。そのため、Kubernetesは当初から不特定多数がアクセスできるパブリッククラウドでの利用が想定されており、そういった環境でもセキュアかつ柔軟に利用できるよう設計されている。そのための機能の1つが、クラウドにアクセスするユーザー単位で利用できる機能を制限できる認証・権限確認機構だ。

Kubernetesでは、APIサーバー(api server)と呼ばれるコンポーネントに対しHTTP/HTTPSでリクエストを送信することで、クラスタに関する各種操作を行うする仕組みになっている。たとえばKubernetesではクラスタを操作するためのコマンドラインインターフェイスである「kubectl」が用意されているが、kubectlはコマンドラインでユーザーから指定されたコマンドに応じてAPIサーバーにリクエストを送信し、その結果を受信して画面に表示するという処理を行う。

パブリッククラウドにおいては、APIサーバーは外部(インターネット)からアクセスできるようになっており、またその仕様も公開されている。そのため、そのホスト名やIPアドレスさえ分かれば、誰もがAPIサーバーに対しリクエストを送信することができてしまう。そこで、クラスタの管理者とは無関係な第三者がAPIサーバーに対しリクエストを送信した場合、そのリクエストを不正なものと判断して却下する仕組みがAPIサーバーには設けられている。

具体的には、APIサーバーにアクセスするクライアントはリクエストの送信時にその送信者を確認するための情報(ユーザー名や認証トークン)などをリクエスト内容と一緒に送信する。APIサーバーはこれらの情報を照合することでリクエスト者の正当性を確認する仕組みになっている。

さらに、Kubernetesではユーザーごとに実行できる処理を制限できる機構も用意されている。これによって、たとえばクラスタの管理者が複数いる場合、ある管理者にはコンテナの稼働状況に関する情報取得は許可するが、コンテナの作成や削除といった処理は禁止したい、といったような需要に対応できる。

authenticationとauthorization、admission control

Kubernetesでは、リクエストを送信したユーザーがクラスタの正当な管理者であるかを確認する処理を「authentication(認証)」と呼ぶ。一般的なWebサービスなどではユーザー名とパスワードによる認証が一般的だが、Kubernetesではさまざまな認証方法が利用できるよう、この認証を行う部分自体をモジュール化し、目的やユースケースに応じて異なる認証手法を選択できるようになっている。標準でいくつかの認証モジュールが提供されているほか、認証を行うための独自のモジュールを実装したり、外部サービスと連携したりして認証を行うことも可能だ。

APIサーバーはリクエストを受信した際に、まず認証を行ってユーザーの正当性を確認する。認証が成功した場合、続いてそのユーザーに対しリクエストされた処理を実行する権限があるかどうかを確認する処理が行われる。この処理は、「authorization(認可)」と呼ばれている。authorizationについてもauthenticationと同様にモジュール化されており、利用者が必要に応じてその手法を選択できるようになっている。

さて、認証および認可ではリクエストを行ったユーザーの正当性と、そのリクエストを行う権限を確認するわけだが、この2つの処理に成功したからといって、そのリクエストが適切であるとは保証できない。誤ってKubernetesやクラスタの仕様・設定に反するリクエストを行ってしまう可能性もある。そのためAPIサーバーでは、認証および認可の後に「admission control(入力制御)」という処理が実行されるようになっている。admission controlはリクエストの内容をチェックする仕組みで、ここではあらかじめ指定していた条件に合致しないリクエストをブロックしたり、指定された条件に応じてリクエスト内容の修正をおこなったりする。

これらのauthenticationとauthorization、admission controlの流れをまとめたものが次の図1だ。

図1 APIサーバーによるリクエスト処理の流れ
図1 APIサーバーによるリクエスト処理の流れ

APIサーバーはリクエストを受信すると、この3つの処理を順に実行し、すべてにパスした場合のみ指定された処理が実行される仕組みになっている。

ちなみに、APIサーバーは暗号化された接続のみを受け付けるポート(Secure Port)と、暗号化されていない接続を受け付けるポート(Localhost Port)の2つを利用する。前者のデフォルトは6443番ポート、後者のデフォルトは8080番ポートだ。

Localhost PortはAPIサーバーと同じホスト上で動作するKubernetesの各種コンポーネントがAPIサーバーと通信するために使用されるもので、このポートに送信されたリクエストに対しては認証および認可処理がバイパスされ、admission controlのみが適用される。ほかのサービスやコンポーネント、ユーザーがこのポートにリクエストを送信することは可能ではあるものの、テストやクラウド環境の構築時の利用に限っての利用が推奨されている。

Kubernetesのユーザーとサービスアカウント

前述のとおり、Kubernetesクラスタに対して何らかの操作を行いたい場合にはAPIサーバーに対してリクエストを送信することになるのだが、APIサーバーにリクエストを送信するのは人間のユーザーだけではない。たとえばKubernetesクラスタ内で実行されるDNSサーバーであるCoreDNSは、APIサーバーにアクセスしてクラスタの情報を取得し、それに応じて設定やDNS情報を自動的に取得するようになっている。

こういったアプリケーションによるリクエストについても、不正なリクエストが通らないよう認証や認可、admission controlなどの処理を実行する必要がある。Kubernetesではこういったアプリケーションからのリクエストで使用することを想定した「service account(サービスアカウント)」と呼ばれる限定されたユーザーを用意する仕組みが用意されている。

サービスアカウントは「ServiceAccount」(sa)というリソースに紐付けられており、名前空間毎に独立しているという特徴がある。作成されているサービスアカウントは、「kubectl get sa」コマンドを用いて確認できる。たとえば次の例は、「kube-system」名前空間で作成されているサービスアカウント一覧を取得するものだ。

$ kubectl -n kube-system get sa
NAME                                 SECRETS   AGE
attachdetach-controller              1         19d
bootstrap-signer                     1         19d
certificate-controller               1         19d
clusterrole-aggregation-controller   1         19d
coredns                              1         19d
cronjob-controller                   1         19d
daemon-set-controller                1         19d
default                              1         19d
deployment-controller                1         19d
disruption-controller                1         19d
endpoint-controller                  1         19d
expand-controller                    1         19d
flannel                              1         19d
generic-garbage-collector            1         19d
horizontal-pod-autoscaler            1         19d
job-controller                       1         19d
kube-proxy                           1         19d
namespace-controller                 1         19d
node-controller                      1         19d
persistent-volume-binder             1         19d
pod-garbage-collector                1         19d
pv-protection-controller             1         19d
pvc-protection-controller            1         19d
replicaset-controller                1         19d
replication-controller               1         19d
resourcequota-controller             1         19d
service-account-controller           1         19d
service-controller                   1         19d
statefulset-controller               1         19d
token-cleaner                        1         19d
ttl-controller                       1         19d

サービスアカウントは名前空間毎に自動的にデフォルトのもの(名前はサービスアカウント名は「default」)が作成されるほか、「kubectl create sa」コマンドで手動で作成することもできる。サービスアカウントはトークンベースでの認証が行われるようになっており、その認証処理は後述する「Service Account Tokens」モジュールで実装されている。

なお、サービスアカウント以外の一般ユーザーについては、基本的にはKubernetes本体では管理は行われず、各認証モジュールが独立して管理するようになっている。そのためkubectlなどのインターフェイス経由でユーザー情報を取得することは基本的には行えない。

kubectlの「context」、「user」、「cluster」の設定

Kubernetesにアクセスするためのコマンドラインインターフェイスであるkubectlコマンドでは、「~/.kube/config」という設定ファイル内にKubernetesへアクセスする際に利用するユーザーの認証情報を保存する仕組みになっている。これによってkubectlコマンドの実行時に毎回ユーザー情報を入力せずとも各種操作を実行できる。また複数の異なるクラスタ、ユーザーを管理できるよう、接続先や使用するユーザーを切り替えるための「context」という仕組みも用意されている。このcontextという概念は厳密にはKubernetesの認証とは直接は関係ないのだが、混乱しやすいのでこちらについてもここで説明しておこう。

kubectlの設定ファイル(~/.kube/config)は以下のようなフォーマットとなっている。

apiVersion: v1
kind: Config
preferences: <各種設定>
users:
- name: <ユーザー名>
  user:
    client-certificate-data: <認証のためのデータ>
    client-key-data: <クライアント鍵データ>
clusters:
- name: <クラスタ名>
  cluster:
    certificate-authority-data: <クラスタ認証のための鍵データ>
    server: https://<APIサーバーのIPアドレス>:<ポート>
contexts:
- name: <context名>
  context:
    cluster: <contextに紐付けるクラスタ名>
    user: <contextに紐付けるユーザー名>
current-context: <現在のcontext>

ここで「users:」以下にはKubernetesで使用するユーザーの情報を、「clusters:」以下には接続先となるKubernetesクラスタのAPIサーバーの情報を記述する。この例ではどちらも1つずつしか記述していないが、複数のユーザーやクラスタを「users:」や「clusters:」以下に記述することも可能だ。そして、「users:」や「clusters:」で定義されたユーザーとKubernetesクラスタの情報を組み合わせたものがcontextとなる。

たとえば次の例では、「kubernetes-admin」という名前のユーザーと「kubernetes」という名前のクラスタ、そしてそれらを紐付けた「kubernetes-admin@kubernetes」という名前のcontextが定義されている。使用するcontextを指定する「current-context」でこの「kubernetes-admin@kubernetes」が指定されているため、kubectlコマンドはデフォルトでは「kubernetes」クラスタ(https://192.168.1.100:6443)に対し、「kubernetes-admin」というユーザー名でアクセスすることになる。

apiVersion: v1
kind: Config
preferences: {}
users:
- name: kubernetes-admin
  user:
    client-certificate-data: <認証のためのデータ>
    client-key-data: <クライアント鍵データ>
clusters:
- name: kubernetes
  cluster:
    certificate-authority-data: <クラスタ認証のための鍵データ>
    server: https://192.168.1.100:6443
contexts:
- name: kubernetes-admin@kubernetes
  context:
    cluster: kubernetes
    user: kubernetes-admin
current-context: kubernetes-admin@kubernetes

ちなみに、現在のcontextは「kubectl config current-context」コマンドで確認できる。

$ kubectl config current-context
kubernetes-admin@kubernetes

また、コンテキストの切り替えは「kubectl config use-context」コマンドで実行できる。詳しくは、「kubectl config --help」コマンドで表示されるオンラインヘルプを確認して欲しい。

Kubernetes標準のauthenticationモジュール

それでは、Kubernetesにおける認証の核となるauthenticationモジュールや、kubeadmで構築したクラスタで有効になっている認証方法について紹介していこう。authenticationモジュールは、APIサーバーへのリクエストに含まれていた認証情報を受け取り、その情報を照合してそのユーザーが適切なものかどうかを判断するという処理を行う。もしユーザーが適切なものだった場合、authenticationモジュールは次のような情報をユーザー情報として返す。

  • ユーザー名
  • UID(ユーザーを識別するためのユニークなID)
  • グループ
  • Extraフィールド

Extraフィールドは文字列をキーとしたマップ型オブジェクトで、任意のデータを格納できる。また、各ユーザーは複数のグループに所属できる。

ユーザー名やグループについては各モジュールが任意のものを提供できるようになっているが、Kubernetes共通で定められている特別なグループも存在する。たとえば、認証済みのユーザーは必ず「system:authenticated」というグループに所属するように定義されている。

また、一部のリクエストはユーザー認証なしの匿名ユーザーでも実行が可能だ。この場合、便宜的にユーザー名は「system:anonymous」、所属グループは「system:unauthenticated」に設定される。

Kubernetes(v1.13)では、以下のauthenticationモジュールが標準で提供されている。これらモジュールを有効にするには、APIサーバーの起動オプションで対応するものを指定すれば良い(表1)。

表1 Kubernetes v1.13にて標準で提供されているauthenticationモジュール
モジュール名 説明 設定オプション
X509 Client Certs 公開鍵証明書認証を利用して認証を行う --client-ca-file=<CAファイル>
Static Token File ユーザー名やトークンとなどを列挙したトークンファイルでユーザーを管理する --token-auth-file=<トークンファイル>
Bootstrap Tokens ベータ機能。クラスタ構築時、まだ認証設定が行われていない状態で使われる。ユーザー情報は「kube-system」名前空間にSecretsとして格納される。ユーザーは有効期限付きで、一定期間で削除される --enable-bootstrap-token-auth
Static Password File ユーザー名とパスワードなどを列挙したパスワードファイルでユーザーを管理する --basic-auth-file=<パスワードファイル>
Service Account Tokens トークンを使って認証する、サービスアカウントの認証専用 --service-account-key-file <PEM形式鍵ファイル>
OpenID Connect Tokens OAuth 2を使った認証 --oidc-issuer-url=<認証URL>など
Webhook Token Authentication Webhookを使ってトークンを認証する --authentication-token-webhook-config-file=<設定ファイル>
Authenticating Proxy 認証機能を持つプロクシなどが、「X-Remote-User」や「X-Remote-Group」といったHTTPヘッダを使って認証情報を付与する --requestheader-username-headers=<使用するヘッダ名>

たとえば、kubeadmで構築したKubernetesクラスタの初期状態ではAPIサーバー(kube-apiserver)プロセスの起動オプションとして次のものが指定されている。

--client-ca-file=/etc/kubernetes/pki/ca.crt
--enable-bootstrap-token-auth=true
--requestheader-allowed-names=front-proxy-client
--requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
--requestheader-extra-headers-prefix=X-Remote-Extra-
--requestheader-group-headers=X-Remote-Group
--requestheader-username-headers=X-Remote-User
--service-account-key-file=/etc/kubernetes/pki/sa.pub

これにより、「X509 Client Certs」および「Bootstrap Tokens」、「Service Account Tokens」、「Authenticating Proxy」認証が有効になっていることが分かる。

まずX509 Client Certsだが、これはSSL/TLS等で使われるX.509公開鍵認証を使ってユーザーの認証を行うモジュールだ。このモジュールを使った認証では、各ユーザーはリクエスト時にクライアント証明書を送信し、APIサーバーは受け取った証明書を「--client-ca-file」オプションで指定したCAファイル(認証局情報ファイル)を使用してユーザーの正当性を検証する。証明書が正当なものであると判断された場合、証明書のcommon name(CN)フィールドで指定された文字列がユーザー名に、証明書のorganization(O)フィールドで指定された文字列が所属グループとして使用される。複数のorganizationフィールドを指定することで、複数のグループにユーザーを所属させることもできる。

たとえばkubeadmでクラスタを構築した場合、kubectl用の設定ファイルとして/etc/kubernetes/admin.confとして作成され、その中の「user:」以下の「client-certificate-data」にBASE64でエンコードされた証明書が格納される。この証明書の情報は、次のようにして確認できる。

#  python -c 'import yaml, sys; print(yaml.load(sys.stdin)["users"][0]["user"]["client-certificate-data"
])' < /etc/kubernetes/admin.conf  | base64 -d | openssl x509 -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 2279747390005043816 (0x1fa34a2c2edfe268)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=kubernetes
        Validity
            Not Before: Feb 21 14:16:37 2019 GMT
            Not After : Feb 21 14:16:41 2020 GMT
        Subject: O=system:masters, CN=kubernetes-admin
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:a1:90:40:76:52:97:f0:30:18:d0:68:0f:2f:7c:
                    1f:97:a8:9d:13:6b:fd:2f:8c:01:96:38:29:bb:83:
                    2e:d9:73:c3:9c:ae:63:aa:83:35:00:b1:04:1e:8a:
                    08:73:94:94:1e:eb:cf:88:fa:05:df:8b:a7:a9:8d:
                    0f:0b:88:0b:d1:3b:e2:40:b6:3b:7a:ec:62:58:d9:
                    ff:8e:d1:29:e2:8f:78:a6:78:41:d9:d2:aa:b5:54:
                    a6:70:a7:49:a7:ec:22:cc:f2:e0:95:f4:4f:fc:85:
                    17:d4:52:5f:1a:fa:5a:f7:b6:47:62:8b:25:ad:23:
                    36:81:b4:7c:8e:bc:4b:69:81:43:8f:ea:10:48:52:
                    36:4e:e6:df:6a:1f:2a:8f:04:1c:1e:1e:65:4f:5f:
                    ae:50:6a:2a:83:42:e0:21:f1:17:7a:1d:7a:6f:56:
                    43:cf:dc:7f:39:c0:f1:14:ea:02:1c:a4:a7:61:49:
                    c6:f4:5b:8a:ad:a4:92:1b:ff:d1:49:0d:b7:4f:cb:
                    67:87:23:10:05:4b:79:b3:c1:df:50:b2:fb:a6:4f:
                    cb:68:a6:4a:2a:c7:db:00:a5:4b:dc:e9:d7:8c:26:
                    06:f1:dd:49:9d:ef:9d:7d:6a:a4:1b:ae:c4:f5:b7:
                    8c:a1:f4:f9:24:cd:c9:f7:f1:93:4e:47:d8:5d:d1:
                    6b:ab
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Client Authentication
    Signature Algorithm: sha256WithRSAEncryption
         8f:34:f4:94:6a:e8:9f:f8:d8:c9:f3:8e:2d:0f:de:4a:0f:39:
         4b:55:50:03:3d:d4:35:94:7f:53:4b:b9:1f:5f:5e:5f:35:43:
         32:4e:ca:b7:08:aa:d8:62:97:73:0d:c5:e9:71:73:18:90:69:
         ec:78:4c:fd:d3:34:07:f8:66:18:c9:4a:0e:82:5e:d3:d9:d6:
         2e:ee:76:bd:04:c4:de:90:71:15:a6:38:d7:a4:c0:1a:49:66:
         a4:cd:f6:b6:bd:6f:9d:3e:e0:6f:f9:4b:89:97:5f:ad:4b:dd:
         35:be:d5:a2:62:8d:4e:06:84:3d:a8:3e:53:34:59:2b:db:21:
         e6:a8:81:76:1e:8e:46:d6:c0:be:7c:11:7f:db:e0:6f:78:38:
         6f:1f:b7:b5:57:ef:2c:20:d1:bb:78:c1:aa:af:30:34:6c:5a:
         08:79:14:01:24:32:6d:9a:7e:96:26:1b:28:21:60:6f:ec:f7:
         35:93:c5:5c:7c:fb:f7:7c:11:a9:67:82:a7:0e:10:85:00:43:
         fe:94:4e:c6:d9:cc:75:d3:f9:86:5c:17:c0:5f:e8:36:5f:8d:
         3d:2e:b8:e9:0d:11:3f:cc:fa:bb:de:59:9d:98:ee:76:10:f6:
         4b:92:1d:7a:29:d6:bf:58:76:22:99:ea:1e:29:81:69:2b:4c:
         4d:e8:26:a9

これを見ると、common nameは「kubernetes-admin」、organizationは「system:masters」となっており、この証明書で認証されたユーザーはユーザー名が「kubernetes-admin」、所属グループが「system:masters」になることが分かる。

また、ユーザーを追加するにはAPIサーバーの--client-ca-fileオプションで指定されているCAファイルを使って証明書を作成し、その情報をkubectlの設定ファイルに追加すれば良い。CAファイルが/etc/kubernetes/pki/ca.crtである場合、これは次のようにして行える。

まず、秘密鍵ファイル(以下では「user.key」)を作成する。

# openssl genrsa -out user.key 2018
Generating RSA private key, 2018 bit long modulus
.............................................+++
.....................+++
e is 65537 (0x10001)

続いて作成した秘密鍵ファイルを元に署名リクエストファイル(CSRファイル、以下では「user.csr」)を作成する。ここでは国名や県名などを入力できるが、Kubernetsで必要となるのは前述のとおりOrganizationとCommon Nameだけなので、それ以外は空のままで構わない。

# openssl req -new -key user.key -out user.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:test  ←グループ名を入力
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:testuser  ←ユーザー名を入力
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

最後にCA証明書とCA鍵ファイルを使ってCSRファイルから証明書ファイル(ここでは「user.crt」)を作成する。

# openssl x509 -req -in user.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial
 -out user.crt -days 10000
Signature ok
subject=/C=XX/L=Default City/O=test/CN=testuser
Getting CA Private Key

作成した証明書の内容は、次のようにして確認できる。

# openssl x509  -noout -text -in user.crt
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number:
            ad:09:56:44:1c:12:dc:20
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=kubernetes
        Validity
            Not Before: Mar 14 11:49:48 2019 GMT
            Not After : Jul 30 11:49:48 2046 GMT
        Subject: C=XX, L=Default City, O=test, CN=testuser
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2018 bit)
                Modulus:
                    02:ea:f1:23:98:79:53:f3:ed:d4:3a:5b:8c:b6:03:
                    1b:6d:bf:df:27:2c:2d:c7:f2:73:b0:13:86:dd:78:
                    bb:f6:c3:0b:04:be:ec:1c:39:d7:c5:c2:a8:bc:e0:
                    23:51:68:21:6b:3b:82:f4:50:aa:64:8e:5e:c2:1d:
                    95:c7:c4:81:d0:76:b1:2b:f4:4c:b7:00:1a:6d:44:
                    4b:d6:f8:b2:e9:82:75:dd:c3:b4:d0:68:6f:f7:82:
                    d4:65:64:4e:6e:b7:f5:6f:fe:cb:34:cc:a6:d5:dd:
                    b5:23:9e:2e:a3:3d:b9:8b:74:67:b5:e3:b8:3e:34:
                    f2:f2:0f:b2:e5:69:02:d2:fc:3d:a3:f0:9b:86:6e:
                    7d:ad:c1:86:f0:1c:42:c0:8f:73:da:21:c2:d9:de:
                    30:86:92:05:0f:0a:4a:0e:49:22:ab:01:4b:e2:95:
                    a7:3e:e7:ee:97:22:f7:0b:95:90:65:7b:c9:a9:29:
                    07:61:48:30:42:85:38:8a:cc:be:ec:89:35:c6:d3:
                    99:53:ab:66:d1:cb:59:62:a7:42:2b:39:03:65:6f:
                    12:63:d7:12:3a:9f:56:11:7a:c0:8e:de:82:bd:65:
                    16:5a:ba:67:74:33:66:ea:83:41:ca:90:d2:b5:c0:
                    26:e2:fe:b7:a8:61:72:23:1b:f5:ea:45:55
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha256WithRSAEncryption
         1e:f7:e0:6d:01:0d:ac:5a:ad:89:e1:d6:d3:f1:78:33:96:52:
         10:02:33:e2:d3:77:8d:25:8a:f6:de:14:62:e6:bd:93:be:72:
         d8:0f:f8:a6:5b:08:37:ed:73:53:14:83:7b:04:0e:0f:53:f7:
         cd:15:ad:fc:93:3d:6b:8d:da:37:9a:76:ed:e7:7d:a1:e3:3b:
         3d:db:b0:83:15:17:e9:d8:47:25:f6:ac:d0:8e:9a:54:07:88:
         bc:7d:7d:49:77:97:71:c2:4e:ab:21:f6:5f:b7:74:7d:a3:2f:
         32:72:91:78:fe:f8:b2:3b:cc:83:51:8a:46:8b:14:0a:57:4f:
         3a:a3:c3:b3:52:25:ec:fa:54:2e:05:a5:e7:8d:ad:22:d9:c1:
         cc:77:13:2b:22:f8:b3:07:64:cb:1d:12:c9:36:07:0d:45:3d:
         4d:b8:43:c1:37:bf:22:6c:37:9e:f8:74:b3:6f:d5:a5:f4:4e:
         35:fb:e1:65:15:4d:46:c9:ae:90:f2:10:d8:c5:11:30:77:5d:
         b6:22:ad:02:95:26:4e:d8:fe:f0:d0:1c:2a:e1:24:44:88:bf:
         6c:f4:d8:f4:0d:cc:7d:51:ab:0d:67:68:8d:bf:2b:0b:20:19:
         73:00:c8:07:0c:38:58:71:0a:63:fb:e0:b4:9e:27:40:bf:30:
         19:39:20:2d

作成した証明書をAPIサーバーとの認証に利用するには、「kubectl config set-credentials <ユーザー名>」コマンドで設定ファイルに使用する証明書情報を追加すれば良い。たとえば「testuser」というユーザー名で追加するには、次のように実行する。

$ kubectl config set-credentials testuser --client-certificate=user.crt --client-key=user.key --embed-certs=true
User "test" set.

さて、それでは作成したこのユーザーでKubernetesクラスタにアクセスしてみよう。前述のとおり、kubectlコマンドではcontextという単位で使用するユーザーとクラスタの情報を管理している。そこで、まず作成したtestuserを使用するcontextを次のようにして作成し、それを使用するcontextとして指定する。

↓設定されているクラスタ名を確認する
$ kubectl config get-clusters
NAME
kubernetes

↓「kubernetes」クラスタと「testuser」ユーザーを紐付けた「test」contextを作成する
$ kubectl config set-context test --user=testuser --cluster=kubernetes
Context "test" created.

↓使用するcontextを「test」に切り替える
$ kubectl config use-context test
Switched to context "test".

↓contextの情報を確認する
$ kubectl config get-contexts
CURRENT   NAME                          CLUSTER      AUTHINFO           NAMESPACE
          kubernetes-admin@kubernetes   kubernetes   kubernetes-admin
*         test                          kubernetes   testuser

これで、「testuser」ユーザーでクラスタにアクセスできるようになった。ただし、現時点ではこのユーザーには権限が設定されていないため、クラスタの操作を行うことはできない。たとえば、「kubectl get pod」コマンドで稼動中のPodの情報を取得しようとすると次のようにエラーとなり、「testuser」には指定した処理を実行する権限がないという旨が表示される。

$ kubectl get pod
Error from server (Forbidden): pods is forbidden: User "testuser" cannot list resource "pods" in API group "" in the namespace "default"

なお、この状態ではクラスタに対し何も操作が行えないので、ユーザー認証に成功したことを確認したら元のユーザーに戻しておこう。

$ kubectl config use-context kubernetes-admin@kubernetes
Switched to context "kubernetes-admin@kubernetes".

サービスアカウントトークンの管理

このようにX509 Client Certs認証では証明書を使って認証を行う仕組みになっているが、別の認証モジュールではまったく異なる方法で認証が行われる。たとえばService Account Tokensモジュールでは、サービスアカウント毎に用意されたトークンを使って認証が行われる。具体的には、リクエスト時に「Authorization: Bearer <トークン>」HTTPヘッダとしてトークンを送信し、そのトークンが登録されているものと一致するかどうかで認証を行う仕組みだ。

前述のとおり、作成されているサービスアカウント一覧は「kubectl get sa」コマンドで確認できる。

$ kubectl get sa --all-namespaces=true
NAMESPACE     NAME                                 SECRETS   AGE
default       default                              1         20d
kube-public   default                              1         20d
kube-system   attachdetach-controller              1         20d
kube-system   bootstrap-signer                     1         20d
kube-system   certificate-controller               1         20d
kube-system   clusterrole-aggregation-controller   1         20d
kube-system   coredns                              1         20d
kube-system   cronjob-controller                   1         20d
kube-system   daemon-set-controller                1         20d
kube-system   default                              1         20d
kube-system   deployment-controller                1         20d
kube-system   disruption-controller                1         20d
kube-system   endpoint-controller                  1         20d
kube-system   expand-controller                    1         20d
kube-system   flannel                              1         20d
kube-system   generic-garbage-collector            1         20d
kube-system   horizontal-pod-autoscaler            1         20d
kube-system   job-controller                       1         20d
kube-system   kube-proxy                           1         20d
kube-system   namespace-controller                 1         20d
kube-system   node-controller                      1         20d
kube-system   persistent-volume-binder             1         20d
kube-system   pod-garbage-collector                1         20d
kube-system   pv-protection-controller             1         20d
kube-system   pvc-protection-controller            1         20d
kube-system   replicaset-controller                1         20d
kube-system   replication-controller               1         20d
kube-system   resourcequota-controller             1         20d
kube-system   service-account-controller           1         20d
kube-system   service-controller                   1         20d
kube-system   statefulset-controller               1         20d
kube-system   token-cleaner                        1         20d
kube-system   ttl-controller                       1         20d

また、個々のサービスアカウントの詳細は「kubectl describe sa」コマンドで確認できる。

$ kubectl describe sa default
Name:                default
Namespace:           default
Labels:              <none>
Annotations:         <none>
Image pull secrets:  <none>
Mountable secrets:   default-token-tgwlm
Tokens:              default-token-tgwlm
Events:              <none>

認証に使用するトークン本体は「Secret」というリソースに格納されており、そこで「Tokens:」に紐付けられている文字列がその名前となる。上記の場合、secret名は「default-token-tgwlm」になるので、次のように実行することでトークン文字列を確認できる。

$ kubectl describe  secret default-token-tgwlm
Name:         default-token-tgwlm
Namespace:    default
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: default
              kubernetes.io/service-account.uid: 65925a6d-35e3-11e9-892f-9ca3ba2d9e1b

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1025 bytes
namespace:  7 bytes
token:      eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tdGd3bG0iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjY1OTI1YTZkLTM1ZTMtMTFlOS04OTJmLTljYTNiYTJkOWUxYiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.AqzEjdGceWdssZpQgvorwiqx7rhZeoP3DLfJ58OM0oHRGfqDNbVMDCnm0-8t0aj7zMVaBa281HfacRLl0VESaWrcXRdarJCRJ5l0VO2TIhoV0TCXzblGEu1oX-qJipIS8Hr_2Ne2JOJ49tvUwwfCnfSyAe4yBJC4N6TDgy8x35Rqj92bL8NSV915nYTRYpEZA8E8r88Y3cMqeXfd7yTDIqCzhoPzXzDTh9wgyKMw5ACDAKEDrdyRByQ4frbbgS19E8OnlGML1IEtm8zj6L5Zryv0ndf4-M_PNncXI0oxMgi_pgel_sZGAnBAZhlpaOnrTbSsOcU-uvDiZ7jxaBmXYQ

クライアントは、APIサーバーへのリクエスト時に「Authorization: Bearer <トークン文字列>」というHTTPヘッダを使ってこのトークンを送信することで認証を受けることができる。たとえばcurlコマンドでは、次のようにしてこのトークンを使ってリクエストを送信できる。

$ curl -k https://<APIサーバーのホスト名/IPアドレス>:<ポート>/api/v1/namespaces/default/pods -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tdGd3bG0iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjY1OTI1YTZkLTM1ZTMtMTFlOS04OTJmLTljYTNiYTJkOWUxYiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.AqzEjdGceWdssZpQgvorwiqx7rhZeoP3DLfJ58OM0oHRGfqDNbVMDCnm0-8t0aj7zMVaBa281HfacRLl0VESaWrcXRdarJCRJ5l0VO2TIhoV0TCXzblGEu1oX-qJipIS8Hr_2Ne2JOJ49tvUwwfCnfSyAe4yBJC4N6TDgy8x35Rqj92bL8NSV915nYTRYpEZA8E8r88Y3cMqeXfd7yTDIqCzhoPzXzDTh9wgyKMw5ACDAKEDrdyRByQ4frbbgS19E8OnlGML1IEtm8zj6L5Zryv0ndf4-M_PNncXI0oxMgi_pgel_sZGAnBAZhlpaOnrTbSsOcU-uvDiZ7jxaBmXYQ"
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {

  },
  "status": "Failure",
  "message": "pods is forbidden: User \"system:serviceaccount:default:default\" cannot list resource \"pods\" in API group \"\" in the namespace \"default\"",
  "reason": "Forbidden",
  "details": {
    "kind": "pods"
  },

Kubernetes内ではサービスアカウントのユーザー名は「system:serviceaccount:<名前空間名>:<サービスアカウント名>」となり、「system:serviceaccounts」および「system:serviceaccounts:<名前空間名>」グループに所属する。ここでは適切な権限設定を行っていないためリクエストは失敗しているが、リクエストを行ったユーザーが「system:serviceaccount:default:default」であることは認識されていることが確認できる。

なお、Podの作成時にサービスアカウントに関する情報はコンテナ内の/var/run/secrets/kubernetes.io/serviceaccountディレクトリに自動的にマウントされ、ファイルとしてその情報にアクセスできる。このディレクトリ内の「ca.crt」ファイルにサービスアカウントのトークン作成に使われるCA証明書が、「namespace」ファイルにPodが稼動している名前空間が、「token」ファイルにトークンが格納されており、コンテナ内からは次のように自由にアクセスできる。

# cat /var/run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tdGd3bG0iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjY1OTI1YTZkLTM1ZTMtMTFlOS04OTJmLTljYTNiYTJkOWUxYiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.AqzEjdGceWdssZpQgvorwiqx7rhZeoP3DLfJ58OM0oHRGfqDNbVMDCnm0-8t0aj7zMVaBa281HfacRLl0VESaWrcXRdarJCRJ5l0VO2TIhoV0TCXzblGEu1oX-qJipIS8Hr_2Ne2JOJ49tvUwwfCnfSyAe4yBJC4N6TDgy8x35Rqj92bL8NSV915nYTRYpEZA8E8r88Y3cMqeXfd7yTDIqCzhoPzXzDTh9wgyKMw5ACDAKEDrdyRByQ4frbbgS19E8OnlGML1IEtm8zj6L5Zryv0ndf4-M_PNncXI0oxMgi_pgel_sZGAnBAZhlpaOnrTbSsOcU-uvDiZ7jxaBmXYQ

ここでマウントされるサービスアカウント情報は基本的には各名前空間内の「default」サービスアカウントのものだが、PodのSpecの「seviceAccountName」項目で紐付けるサービスアカウント名を明示的に指定することも可能だ。たとえば次のように指定した場合、「foobar」というサービスアカウントの情報がコンテナ内にマウントされる。

    spec:
      serviceAccountName: foobar
      containers:
      - name: httpd
        image: httpd

なお、サービスアカウントの認証トークンは上記のようにSecretリソースとして管理されるため、Secretリソースにアクセスできるユーザーは誰でもこのアカウントを利用できてしまう点には注意したい。

権限認証の流れ

続いて、authorization(認可)モジュールとその動作について見ていこう。authorizationモジュールはauthenticationモジュールによってユーザーの正当性が確認された後に、そのユーザーがリクエストした処理の権限を持っているかどうかを判断し、権限が無ければリクエストを却下するという処理を行う。

ユーザーが権限を持っているかどうかはユーザー名および所属グループといったauthorizationモジュールが付与した属性と、リクエストされたAPIのパスや処理内容、対象リソース、名前空間といった処理内容によって判断される。

ユーザーが所有している権限一覧を取得するような機能は用意されていないが、ユーザーに指定した操作を実行する権限が与えられているかどうかは「kubectl auth can-i」コマンドで確認できる。たとえばPodの情報を取得する権限があるかどうかは、次のようにして確認できる。

$ kubectl auth can-i get pod
yes

権限がある場合は、このように「yes」が返される。一方権限が無い場合は「no」という結果となる。

authorizationモジュールはauthenticationモジュールと同様にモジュール化されており、APIサーバーの起動時にどのモジュールを利用するかを「--authorization-mode=<モジュール名>」コマンドラインオプションで指定する。Kubernetes v1.13にて標準で利用できるauthorizationモジュールは次の表2のとおりだ。

表2 Kubernetes 1.13にて標準で利用できるauthorizationモジュール
モジュール名 説明
Node 各クラスタノードで動作するコンポーネントであるkubeletからのリクエストを認可するためのモジュール
ABAC 「Attribute-Based Access Control」の略。APIサーバーの「--authorization-policy-file=<ポリシーファイル>」オプションで指定した設定ファイルでユーザーやユーザーの属性毎に許可するリクエストを定義する
RBAC 「Role-Based Access Control」の略。「Role」や「ClusterRole」、「RoleBinding」、「ClusterRoleBinding」といったリソースを作成して認可ルールを定義する
Webhook Webhookを利用して外部サービスに問い合わせを行い、その結果を元にして権限認可を行う仕組み
AlwaysDeny テスト用モジュール。常にすべてのリクエストを拒否する
AlwaysAllow テスト用モジュール。常にすべてのリクエストを認可する

使用するauthenticationモジュールは複数を同時に指定でき、その場合先に指定したほうが優先される。たとえばkubeadmで構築したクラスタの場合、デフォルト設定ではAPIサーバーの起動時に「--authorization-mode=Node,RBAC」オプションが指定されていた。この場合、まずNodeモジュールでの認可が試みられ、続いてRBACモジュールでの認可が試みられるという流れになる。

デフォルトで使われている「RBAC」

kubeadmで構築したクラスタにてデフォルトで使われている「RBAC」は、Kubernetes v1.8より安定扱いになったauthorizationモジュールだ。RBACでは「Role」リソースと「ClusterRole」リソースでルールを作成し、「RoleBinding」および「ClusterRoleBinding」リソースでルールをユーザーやグループに紐付けることで、各ユーザーが利用できるリクエストを制限する仕組みとなっている。

RoleリソースとClusterRoleリソースの違いだが、Roleリソースは指定した名前空間内でのみ有効なルールで、ClusterRoleはクラスタ全体で有効となるという点が異なる。たとえばkubeadmで構築したクラスタでは、次のようなRoleおよびClusterRoleリソースがデフォルトで作成されている。

$ kubectl get role --all-namespaces=true
NAMESPACE     NAME                                             AGE
kube-public   kubeadm:bootstrap-signer-clusterinfo             21d
kube-public   system:controller:bootstrap-signer               21d
kube-system   extension-apiserver-authentication-reader        21d
kube-system   kube-proxy                                       21d
kube-system   kubeadm:kubelet-config-1.13                      21d
kube-system   kubeadm:nodes-kubeadm-config                     21d
kube-system   system::leader-locking-kube-controller-manager   21d
kube-system   system::leader-locking-kube-scheduler            21d
kube-system   system:controller:bootstrap-signer               21d
kube-system   system:controller:cloud-provider                 21d
kube-system   system:controller:token-cleaner                  21d
$ kubectl get clusterrole --all-namespaces=true
NAME                                                                   AGE
admin                                                                  21d
cluster-admin                                                          21d
edit                                                                   21d
flannel                                                                21d
system:aggregate-to-admin                                              21d
system:aggregate-to-edit                                               21d
system:aggregate-to-view                                               21d
system:auth-delegator                                                  21d
system:aws-cloud-provider                                              21d
system:basic-user                                                      21d
system:certificates.k8s.io:certificatesigningrequests:nodeclient       21d
system:certificates.k8s.io:certificatesigningrequests:selfnodeclient   21d
system:controller:attachdetach-controller                              21d
system:controller:certificate-controller                               21d
system:controller:clusterrole-aggregation-controller                   21d
system:controller:cronjob-controller                                   21d
system:controller:daemon-set-controller                                21d
system:controller:deployment-controller                                21d
system:controller:disruption-controller                                21d
system:controller:endpoint-controller                                  21d
system:controller:expand-controller                                    21d
system:controller:generic-garbage-collector                            21d
system:controller:horizontal-pod-autoscaler                            21d
system:controller:job-controller                                       21d
system:controller:namespace-controller                                 21d
system:controller:node-controller                                      21d
system:controller:persistent-volume-binder                             21d
system:controller:pod-garbage-collector                                21d
system:controller:pv-protection-controller                             21d
system:controller:pvc-protection-controller                            21d
system:controller:replicaset-controller                                21d
system:controller:replication-controller                               21d
system:controller:resourcequota-controller                             21d
system:controller:route-controller                                     21d
system:controller:service-account-controller                           21d
system:controller:service-controller                                   21d
system:controller:statefulset-controller                               21d
system:controller:ttl-controller                                       21d
system:coredns                                                         21d
system:csi-external-attacher                                           21d
system:csi-external-provisioner                                        21d
system:discovery                                                       21d
system:heapster                                                        21d
system:kube-aggregator                                                 21d
system:kube-controller-manager                                         21d
system:kube-dns                                                        21d
system:kube-scheduler                                                  21d
system:kubelet-api-admin                                               21d
system:node                                                            21d
system:node-bootstrapper                                               21d
system:node-problem-detector                                           21d
system:node-proxier                                                    21d
system:persistent-volume-provisioner                                   21d
system:volume-scheduler                                                21d
view                                                                   21d

これらRoleおよびClusterRoleリソースで定義された認可ルールをどのユーザーに対し紐付けるかを定義するのがRoleBindingおよびClusterRoleBindingリソースで、kubeadmで構築したクラスタでは以下のようなRoleBindingおよびClusterRoleBindingが定義されていた。

$ kubectl get rolebinding --all-namespaces=true
NAMESPACE     NAME                                             AGE
kube-public   kubeadm:bootstrap-signer-clusterinfo             21d
kube-public   system:controller:bootstrap-signer               21d
kube-system   kube-proxy                                       21d
kube-system   kubeadm:kubelet-config-1.13                      21d
kube-system   kubeadm:nodes-kubeadm-config                     21d
kube-system   system::leader-locking-kube-controller-manager   21d
kube-system   system::leader-locking-kube-scheduler            21d
kube-system   system:controller:bootstrap-signer               21d
kube-system   system:controller:cloud-provider                 21d
kube-system   system:controller:token-cleaner                  21d
$ kubectl get clusterrolebinding --all-namespaces=true
NAME                                                   AGE
cluster-admin                                          21d
flannel                                                21d
kubeadm:kubelet-bootstrap                              21d
kubeadm:node-autoapprove-bootstrap                     21d
kubeadm:node-autoapprove-certificate-rotation          21d
kubeadm:node-proxier                                   21d
system:aws-cloud-provider                              21d
system:basic-user                                      21d
system:controller:attachdetach-controller              21d
system:controller:certificate-controller               21d
system:controller:clusterrole-aggregation-controller   21d
system:controller:cronjob-controller                   21d
system:controller:daemon-set-controller                21d
system:controller:deployment-controller                21d
system:controller:disruption-controller                21d
system:controller:endpoint-controller                  21d
system:controller:expand-controller                    21d
system:controller:generic-garbage-collector            21d
system:controller:horizontal-pod-autoscaler            21d
system:controller:job-controller                       21d
system:controller:namespace-controller                 21d
system:controller:node-controller                      21d
system:controller:persistent-volume-binder             21d
system:controller:pod-garbage-collector                21d
system:controller:pv-protection-controller             21d
system:controller:pvc-protection-controller            21d
system:controller:replicaset-controller                21d
system:controller:replication-controller               21d
system:controller:resourcequota-controller             21d
system:controller:route-controller                     21d
system:controller:service-account-controller           21d
system:controller:service-controller                   21d
system:controller:statefulset-controller               21d
system:controller:ttl-controller                       21d
system:coredns                                         21d
system:discovery                                       21d
system:kube-controller-manager                         21d
system:kube-dns                                        21d
system:kube-scheduler                                  21d
system:node                                            21d
system:node-proxier                                    21d
system:volume-scheduler                                21d

たとえば、「cluster-admin」というClusterRoleBindingリソースは次のようになっている。

$ kubectl describe clusterrolebinding cluster-admin
Name:         cluster-admin
Labels:       kubernetes.io/bootstrapping=rbac-defaults
Annotations:  rbac.authorization.kubernetes.io/autoupdate: true
Role:
  Kind:  ClusterRole
  Name:  cluster-admin
Subjects:
  Kind   Name            Namespace
  ----   ----            ---------
  Group  system:masters

この設定は「system:masters」というグループに所属しているユーザーに対し、「cluster-admin」というClusterRoleで定義されている権限を付与するという設定だ。また、「cluster-admin」ClusterRoleは次のように定義されている。

$ kubectl describe clusterrole cluster-admin
Name:         cluster-admin
Labels:       kubernetes.io/bootstrapping=rbac-defaults
Annotations:  rbac.authorization.kubernetes.io/autoupdate: true
PolicyRule:
  Resources  Non-Resource URLs  Resource Names  Verbs
  ---------  -----------------  --------------  -----
  *.*        []                 []              [*]
             [*]                []              [*]

これは、すべてのリソースに対しすべての処理を許可するという内容になっている。これにより、「system:masters」グループに所属するユーザーはすべてのリクエストが認可されるようになっている。

ユーザーに対し権限を追加する

さて、新規に定義したユーザーに対して権限を与えるには、まず許可する権限を定義したRoleもしくはClusterRoleリソースを作成し、それらをユーザーもしくはグループに紐付けるRoleBindingもしくはClusterRoleBindingリソースを作成すれば良い。たとえば次の設定例は、すべての「get」および「list」、「watch」操作を許可する「readonly-for-all」というClusterRoleリソースと、それを先に作成しておいた「testuser」というユーザーに紐付けるClusterRoleBindingを定義するものだ。

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: readonly-for-all
rules:
- apiGroups: ["*"]
  resources: ["*"]
  verbs: ["get", "list", "watch"]
- nonResourceURLs: ["*"]
  verbs: ["get", "list", "watch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: readonly-for-test
subjects:
- kind: User
  name: testuser
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: readonly-for-all
  apiGroup: rbac.authorization.k8s.io

これを「readonly-for-all.yaml」というファイルに保存し、「kubectl apply」コマンドで適用する。

$ kubectl apply -f readonly-for-all.yaml
clusterrole.rbac.authorization.k8s.io/readonly-for-all unchanged
clusterrolebinding.rbac.authorization.k8s.io/readonly-for-test created

↓「readonly-for-all」ClusterRoleの内容を確認
$ kubectl describe clusterrole readonly-for-all
Name:         readonly-for-all
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRole","metadata":{"annotations":{},"name":"readonly-for-all"},"rules":[{"apiGr...
PolicyRule:
  Resources  Non-Resource URLs  Resource Names  Verbs
  ---------  -----------------  --------------  -----
  *.*        []                 []              [get list watch]
             [*]                []              [get]
             [*]                []              [list]
             [*]                []              [watch]

↓「readonly-for-test」ClusterRoleBingingの内容を確認
$ kubectl describe clusterrolebinding readonly-for-test
Name:         readonly-for-test
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRoleBinding","metadata":{"annotations":{},"name":"readonly-for-test"},"roleRef...
Role:
  Kind:  ClusterRole
  Name:  readonly-for-all
Subjects:
  Kind  Name      Namespace
  ----  ----

これでtestuserユーザーに権限が設定され、先ほどは実行できなかったget系の操作が行えるようになる。

↓使用するcontextを「test」に切り替える
$ kubectl config use-context test
Switched to context "test".

$ kubectl get pod
NAME      READY   STATUS    RESTARTS   AGE
busybox   1/1     Running   1          21d

一方で書き込み系の操作については権限が与えられていないので、たとえばPodを作成するような動作についてはエラーとなる。

$ kubectl apply -f httpd.yaml
Error from server (Forbidden): error when creating "httpd.yaml": pods is forbidden: User "testuser" cannot create resource "pods" in API group "" in the namespace "default"

admission control

authenticationおよびauthorizationによってリクエストが許可された後、最後に実行されるのがadmission control処理だ。この処理はリソースの作成時、もしくは既存リソースの変更時にのみ実行されるもので、前述のとおりリクエストの内容が適切なものであるかを検証したり(validating)、あらかじめ指定された条件に応じてリクエスト内容を変更したりする処理(mutating)が行われる。

admission controlもauthenticationやauthorizationと同様にモジュール化されており、APIサーバーの起動オプションで有効/無効にするモジュール(admission controller)を指定できるようになっている。具体的には、「 --enable-admission-plugins=<プラグイン>」オプションで有効にするadmission controlモジュールを、「--disable-admission-plugins=<プラグイン>」で無効にするモジュールを指定するようになっている。

ちなみに、Kubernetes v1.13では表3のadmission controllerがデフォルトとなっている。

表3 Kubernetes v1.13でデフォルトで有効化されているadmission controller
名称 説明
NamespaceLifecycle リクエスト時に指定された名前空間の存在を検証したり、システムで予約されている名前空間を削除しないようチェックしたりする
LimitRanger リクエストが名前空間に設定された「LimitRange」制限に引っかからないかをチェックする
ServiceAccount サービスアカウント関連の処理を自動化する
TaintNodesByCondition Podが不適切なノードで実行されないようチェックする(ベータ機能)
Priority priorityClassNameフィールドの設定を行う
DefaultTolerationSeconds Podリソースの作成時、Podが利用可能になるまでの最大待機時間を5分に設定する
DefaultStorageClass 「PersistentVolumeClaim」リソースの作成時に自動的にデフォルトのストレージクラスを追加する
PersistentVolumeClaimResize 「PersistentVolumeClaim」リソースのリサイズリクエストを検証する
MutatingAdmissionWebhook webhookを使ったリクエストの変更を行う
ValidatingAdmissionWebhook webhookを使ったリクエストを検証する
ResourceQuota リクエストを監視し、名前空間に設定された「ResourceQuota」制限に引っかからないかをチェックする

そのほか、Kubernetesではデフォルトで多数のadmission controllerが用意されている。詳しくはドキュメントを参照して欲しい。

ユーザー認証・認可・admission controlの全体像を把握しておこう

このように、Kubernetesではリクエストを検証・制限する方法として認証・認可・admission controlという3段階のステップを踏むようになっており、かつそれぞれがモジュール化されて柔軟に利用できるようになっている。そのため全貌を把握しておかないと、認証や権限設定が難しいようなイメージがあるかもしれない。とはいえ、このようにそれぞれの動き自体はシンプルなので、理解してうまく活用していこう。