目次


Kubernetes RBAC とサービス・アカウントを使用する

RBAC を使用して Kubernetes API を部分的に公開する方法

Comments

Kubernetes に備わっている強力な側面の 1 つは、アプリケーションによる Kubernetes API の呼び出しによって構成を拡張できることです。Kubernetes 1.8 以降、セキュリティーを強化するために、API へのアクセスは ロール・ベースのアクセス制御 (RBAC) モデルで管理されるようになっています。このチュートリアルでは、 Kubernetes 内で API を使用して RBAC モデルへの移行の影響を調べます。また、 RBAC を正しく使用した構成の作成方法についても説明します。

学習の目的

このチュートリアルを完了すると、以下の方法がわかるようになります。

  • Role と RoleBinding を使用して Kubernetes API を部分的に公開する
  • ServiceAccount を作成して、API 呼び出しを許可するポッドをきめ細かく制限する

前提条件

このチュートリアルに従うには、次の前提条件を満たす必要があります。

  • IBM Cloud の従量課金アカウント (無料枠では、このチュートリアルで作成する Kubernetes クラスターを 使用できません)。まだアカウントを作成していない場合は、このリンク先のページで登録して ください。

  • IBM Cloud Kubernetes Service 内でプロビジョニング済みの Kubernetes クラスター

  • お使いのマシンに IBM Cloud Developer Tools がインストールされていること。

  • git clone で rbac-in-k8s git リポジトリーを複製済みであること。 このリポジトリーに、イメージを作成するための構成ファイルと、チュートリアルの手順に必要な Kubernetes yaml ファイルがすべて揃っています。

所要時間

このチュートリアルの所要時間は約 90 分です。

手順

1. アプリケーション用のイメージをビルドする

アプリケーション用のイメージを作成して起動 しましょう。ここで使用するイメージは、 ny-power アプリケーション内で使用されているイメージに変更を加えたバージョンです。変更後のイメージには、 Kubernetes API に簡単にアクセスできるように kubectl ツールが組み込まれています。

まず、イメージを保管するレジストリー名前空間を 作成する必要があります。

> ibmcloud cr namespace-add rbac-tutorial

Adding namespace 'rbac-tutorial'...

Successfully added namespace 'rbac-tutorial'

OK

次に、サンプル・イメージをビルドします。

> ibmcloud cr build --tag registry.ng.bluemix.net/rbac-tutorial/mqtt-img:1 deploy/mqtt-img

Sending build context to Docker daemon  6.656kB
Step 1/13 : FROM ubuntu:xenial
...
1: digest: sha256:ea1afeb4e5754f8defcae039f9a43aff8b81ecc24ef9ed2d907381a9a99d0b2b size: 2821

OK
> ibmcloud cr build --tag registry.ng.bluemix.net/rbac-tutorial/tools-img:1 deploy/tools-img

Sending build context to Docker daemon  4.096kB
Step 1/9 : FROM ubuntu:xenial
...
1: digest: sha256:78b0639d6c77af5b6fda4d690273a702c43c55cb386f5ab24e78a693a6664f2a size: 2200

OK

2. サンプル・アプリケーションと tools ポッドを起動する

ステップ 1 でビルドしたイメージのそれぞれをベースに、2 つのデプロイメントを起動します。

> kubectl apply -f deploy/mqtt.yaml -f deploy/tools.yaml

secret "mqtt-secret" created
persistentvolumeclaim "mqtt-nfs" created
deployment.apps "mqtt" created
service "mqtt" created
deployment.apps "tools-no-rbac" created

プロビジョニングが完了するまで 2、3 分かかります。kubectl get all を実行すると、現在のステータスが表示されます。両方のポッドが Running の状態になるまで待ってから、 次のステップに進んでください。

> kubectl get all
NAME                                 READY     STATUS    RESTARTS   AGE
pod/mqtt-5ccf8b68b6-m8hfl            1/1       Running   0          2m
pod/tools-no-rbac-7dc96f489b-d9gcl   1/1       Running   0          2m

NAME                 TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)                       AGE
service/kubernetes   ClusterIP      172.21.0.1       <none>          443/TCP                       16d
service/mqtt         LoadBalancer   172.21.173.243   169.60.93.179   1883:31532/TCP,80:31517/TCP   2m

NAME                                  DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.extensions/mqtt            1         1         1            1           2m
deployment.extensions/tools-no-rbac   1         1         1            1           2m

NAME                                             DESIRED   CURRENT   READY     AGE
replicaset.extensions/mqtt-5ccf8b68b6            1         1         1         2m
replicaset.extensions/tools-no-rbac-7dc96f489b   1         1         1         2m

NAME                            DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/mqtt            1         1         1            1           2m
deployment.apps/tools-no-rbac   1         1         1            1           2m

NAME                                       DESIRED   CURRENT   READY     AGE
replicaset.apps/mqtt-5ccf8b68b6            1         1         1         2m
replicaset.apps/tools-no-rbac-7dc96f489b   1         1         1         2m

3. Kubernetes API にアクセスしてみる

Kubernetes の強力な機能の 1 つは、クラスター内で Kubernetes API とやり取りできることです。この機能により、アプリケーションは 独自のリソースを積極的に管理して状況に適応することができます。

この機能をデモンストレーションするために、tools アプリに接続して kubectl を使用します。

前の手順において環境内で起動した tools ポッドの名前を取得するために、次のコマンドを実行します。

> kubectl get pods -l rbac=none
NAME                             READY     STATUS    RESTARTS   AGE
tools-no-rbac-7dc96f489b-ph7h9   1/1       Running   0          1h

ポッドの名前を取得した後は、次のコマンドを使用して、 ポッド上で bash セッションを作成します。

> kubectl exec -it tools-no-rbac-7dc96f489b-ph7h9 bash

root@tools-no-rbac-7dc96f489b-ph7h9:/#

ポッド内に入りました!次のステップは、このポッドからアクセスできるリソースを調べることです。それには、kubectl get all を実行します。

root@tools-no-rbac-7dc96f489b-ph7h9:/# kubectl get all

Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:default" cannot list pods in the namespace "default"
Error from server (Forbidden): replicationcontrollers is forbidden: User "system:serviceaccount:default:default" cannot list replicationcontrollers in the namespace "default"
Error from server (Forbidden): services is forbidden: User "system:serviceaccount:default:default" cannot list services in the namespace "default"
Error from server (Forbidden): daemonsets.extensions is forbidden: User "system:serviceaccount:default:default" cannot list daemonsets.extensions in the namespace "default"
Error from server (Forbidden): deployments.extensions is forbidden: User "system:serviceaccount:default:default" cannot list deployments.extensions in the namespace "default"
Error from server (Forbidden): replicasets.extensions is forbidden : User "system:serviceaccount:default:default" cannot list replicasets.extensions in the namespace "default"
Error from server (Forbidden): daemonsets.apps is forbidden: User "system:serviceaccount:default:default" cannot list daemonsets.apps in the namespace "default"
Error from server (Forbidden): deployments.apps is forbidden: User "system:serviceaccount:default:default" cannot list deployments.apps in the namespace "default"
Error from server (Forbidden): replicasets.apps is forbidden: User "system:serviceaccount:default:default" cannot list replicasets.apps in the namespace "default"
Error from server (Forbidden): statefulsets.apps is forbidden: User "system:serviceaccount:default:default" cannot list statefulsets.apps in the namespace "default"
Error from server (Forbidden): horizontalpodautoscalers.autoscaling is forbidden: User "system:serviceaccount:default:default" cannot list horizontalpodautoscalers.autoscaling in the namespace "default"
Error from server (Forbidden): jobs.batch is forbidden: User "system:serviceaccount:default:default" cannot list jobs.batch in the namespace "default"
Error from server (Forbidden): cronjobs.batch is forbidden: User "system:serviceaccount:default:default" cannot list cronjobs.batch in the namespace "default"

上手く行きませんでした。何が原因だったのでしょうか?

Kubernetes 1.9 以降、ロールベースの アクセス制御 (RBAC) システムで API を保護することが必須となっています。以前のように、デフォルトで API に対するアクセス権がアプリケーションに付与されることはなくなりました。現在は、アプリケーションで API にアクセスするには、 API のその特定の部分に対するアクセスを、アプリケーションに明示的に許可しなければなりません。このことから、RBAC モデルへの移行に備えていなかった多数のアプリケーションが機能しなくなる結果となりました。

Kubernetes では、次の 2 つのリソースによって API へのアクセスを制御します。

  • Role: 付与するアクセス権を指定します。
  • RoleBinding: Role の適用対象を指定します。

これらのリソースによるアクセス制御の仕組みを理解するために、これからいくつかの方法で、この両方のリソースを作成します。

4. Role と RoleBinding を作成する

最初のステップは、Role を作成することです。以下に示すサンプル Role では 2 つの要素に対する操作が許可されます。具体的には、サービスの一覧表示/取得、そして シークレットの作成/削除です。

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: api-role
  namespace: default
  labels:
    app: tools-rbac
rules:
- apiGroups: [""]
  resources: ["services"]
  verbs: ["get", "list"]
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["create"]
- apiGroups: [""]
  resources: ["configmaps"]
  resourceNames: ["mqtt-pub-address"]
  verbs: ["update", "delete"]

このように、Role は一連のルールとして指定されます。これらのルールで基準としているのは、apiGroup (コア・リソースの場合は空)、 resources (リソースの名前)、verbs (リソースに適用する動詞) です。 また、必要に応じて resourceName を設定し、さらにきめ細かく制限することもできます (Secret リソースと ConfigMap リソースでよく使用されます)。

このサンプル Role を使うと、サービスの一覧表示、特定のサービスに関する情報の取得、 単一のシークレットの作成または削除が許可されます。

Role は孤立した状態では何の役目も果たしません。 RoleBinding にバインドして初めて機能するようになります。

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: global-rolebinding
  namespace: default
  labels:
    app: tools-rbac
subjects:
- kind: Group
  name: system:serviceaccounts
  apiGroup: rbac.authorization.k8s.io
  namespace: default
roleRef:
  kind: Role
  name: api-role
  apiGroup: ""

RoleBinding は Role をその適用対象 (subject) にリンクします。Role の適用対象を操作するさまざまな方法があります。 この例では、デフォルト名前空間内のすべてのサービス・アカウントに Role を適用しています。この操作は実質的に、すべてのポッドに 該当する API へのアクセスを許可することを意味します。

この変更を適用するには、リポジトリー内の yaml ファイルを使用します。

> kubectl apply -f deploy/role.yaml -f deploy/global-role-assign.yaml
role.rbac.authorization.k8s.io "api-role" created
rolebinding.rbac.authorization.k8s.io "global-rolebinding" created

5. 変更後のアクセスをテストする

tools ポッドに接続して、変更による影響を確認しましょう。

> kubectl exec -it tools-no-rbac-7dc96f489b-ph7h9 bash

root@tools-no-rbac-7dc96f489b-ph7h9:/# kubectl get all
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                       AGE
kubernetes   ClusterIP      172.21.0.1     <none>          443/TCP                       15d
mqtt         LoadBalancer   172.21.91.88   169.60.93.179   1883:32145/TCP,80:31639/TCP   22h
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:default" cannot list pods in the namespace "default"
Error from server (Forbidden): replicationcontrollers is forbidden: User "system:serviceaccount:default:default" cannot list replicationcontrollers in the namespace "default"
Error from server (Forbidden): daemonsets.extensions is forbidden: User "system:serviceaccount:default:default" cannot list daemonsets.extensions in the namespace "default"
Error from server (Forbidden): deployments.extensions is forbidden: User "system:serviceaccount:default:default" cannot list deployments.extensions in the namespace "default"
Error from server (Forbidden): replicasets.extensions is forbidden: User "system:serviceaccount:default:default" cannot list replicasets.extensions in the namespace "default"
Error from server (Forbidden): daemonsets.apps is forbidden: User "system:serviceaccount:default:default" cannot list daemonsets.apps in the namespace "default"
Error from server (Forbidden): deployments.apps is forbidden: User "system:serviceaccount:default:default" cannot list deployments.apps in the namespace "default"
Error from server (Forbidden): replicasets.apps is forbidden: User "system:serviceaccount:default:default" cannot list replicasets.apps in the namespace "default"
Error from server (Forbidden): statefulsets.apps is forbidden: User "system:serviceaccount:default:default" cannot list statefulsets.apps in the namespace "default"
Error from server (Forbidden): horizontalpodautoscalers.autoscaling is forbidden:User "system:serviceaccount:default:default" cannot list horizontalpodautoscalers.autoscaling in the namespace "default"
Error from server (Forbidden): jobs.batch is forbidden: User "system:serviceaccount:default:default" cannot list jobs.batch in the namespace "default"
Error from server (Forbidden): cronjobs.batch is forbidden: User "system:serviceaccount:default:default" cannot list cronjobs.batch in the namespace "default"

root@tools-no-rbac-7dc96f489b-ph7h9:/# kubectl get services
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                       AGE
kubernetes   ClusterIP      172.21.0.1     <none>          443/TCP                       15d
mqtt         LoadBalancer   172.21.91.88   169.60.93.179   1883:32145/TCP,80:31639/TCP   22h

上記の出力からわかるように、クラスター内のサービスにアクセスできるようになりました。

次に必要な作業は、mqtt ポッドのパブリック・アドレスの configmap エントリーを作成することです。それには、次のコマンドを使用します。

root@tools-no-rbac-7dc96f489b-ph7h9:/# kubectl create configmap mqtt-pub-address --from-literal=host=169.60.93.179
configmap "mqtt-pub-address" created

ポッド内から ConfigMap が作成されても、このリソースにアクセスすることはできません (必要なアクセス・レベルが設定されていないためです)。

root@tools-no-rbac-7dc96f489b-ph7h9:/# kubectl get configmap/mqtt-pub-address
Error from server (Forbidden): configmaps "mqtt-pub-address" is forbidden: User "system:serviceaccount:default:default" cannot get configmaps in the namespace "default"

ポッドからではなく、すべての権限が付与されているコンピューターからアクセスすれば、 configmap の内容を確認できます。

> kubectl get configmap/mqtt-pub-address -o yaml
apiVersion: v1
data:
  host: 169.60.93.179
kind: ConfigMap
metadata:
  creationTimestamp: 2018-07-12T14:45:13Z
  name: mqtt-pub-address
  namespace: default
  resourceVersion: "418889"
  selfLink:/api/v1/namespaces/default/configmaps/mqtt-pub-address
  uid: 2eee2331-85e2-11e8-857f-06cd14ab6bce

上記の出力から、このアクセス権は環境内のすべてのポッドに与えられていることも わかります。したがって、mqtt ポッドに接続している場合でも、 同じコマンドを実行できます。

> kubectl get pod -l app=mqtt
NAME                    READY     STATUS    RESTARTS   AGE
mqtt-5ccf8b68b6-bkdf9   1/1       Running   0          2m

> kubectl exec -it mqtt-5ccf8b68b6-bkdf9 bash

root@mqtt-5ccf8b68b6-bkdf9:/# kubectl get services

NAME         TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                       AGE
kubernetes   ClusterIP      172.21.0.1     <none>          443/TCP                       15d
mqtt         LoadBalancer   172.21.91.88   169.60.93.179   1883:32145/TCP,80:31639/TCP   23h

これでは、アクセス権をむやみに付与していることになります。この問題に対処すべく、 tools ポッドだけに具体的なアクセス権を 付与する方法を見ていきましょう。

けれどもその前に global-role-binding を削除して、 今後のサンプル・コマンドを実行する際の妨げにならないようにする必要があります。

> kubectl delete -f deploy/global-role-assign.yaml

rolebinding.rbac.authorization.k8s.io "global-rolebinding" deleted

tools ポッドに対するアクセス権が取り消されたことを確認します。

root@tools-no-rbac-7dc96f489b-ph7h9:/# kubectl get services

Error from server (Forbidden): services is forbidden: User "system:serviceaccount:default:default" cannot list services in the namespace "default"

復習: これまでに学習したこと

この例から学んだことは、次のとおりです。

  • デフォルトでは、Kubernetes API へのアクセスはクラスター内で制限されます。
  • アクセス権を付与するには、Role リソースと RoleBinding リソースを使用します。
  • Role は、リソース、動詞、さらに場合によっては resourceName に基づく一連のルールとして指定されます。
  • Role のバインド先の対象で kind: Groupname: system:serviceaccounts が設定されている場合、システム内のすべてのポッドにアクセス権が付与されます。

6. サービス・アカウントを作成する

セキュリティーにおけるベスト・プラクティスとなっているのは、 必要最小限の権限付与です。Kubernetes ではそのための 1 つの手段として、 ServiceAccounts を使用します。デフォルトでは、すべてのアプリケーションは default ServiceAccount で実行されますが、アプリケーションに固有の サービス・アカウントを追加で作成することができます。

以下に示す yaml ファイルでは、基本的な ServiceAccount を定義しています。

apiVersion: v1
kind: ServiceAccount
metadata:
  name: service-account-1
  labels:
    app: tools-rbac

ServiceAccount を使用してポッドを起動するには、ポッドの spec 定義に ServiceAccount を追加します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: tools-service-account
  labels:
    app: tools
    rbac: service-account-1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tools
      rbac: service-account-1
  template:
    metadata:
      labels:
        app: tools
        rbac: service-account-1
    spec:
      serviceAccountName: service-account-1
      containers:
        - name: tools
          image: "registry.ng.bluemix.net/rbac-tutorial/tools-img:1"
          imagePullPolicy: Always
          command: ["/bin/sleep", "3601"]

上記では、ポッドの spec で serviceAccountName: service-account-1 が定義されています。これにより、ポッドはこのサービス・アカウントとして実行されます。 さらに、このポッドから起動されるすべてのコンテナーもこの同じサービス・アカウントで 実行されることになります。

7. ServiceAccount を使用してデプロイメントを起動する

このポッドを該当するサービス・アカウントで起動するには、まず、次のコマンドを 実行します。

> kubectl apply -f deploy/tools-service-account.yaml

serviceaccount "service-account-1" configured
deployment.apps "tools-service-account" configured

成功です。次は、ポッドの動作を確認しましょう。

> kubectl get pods
NAME                                     READY     STATUS         RESTARTS   AGE
mqtt-5ccf8b68b6-bkdf9                    1/1       Running        0          51m
tools-no-rbac-7dc96f489b-ph7h9           1/1       Running        22         22h
tools-service-account-6664bdf7f-jzpcg    0/1       ErrImagePull   0          36s

上手くいっていません。ポッドは何が原因で起動しなかったのでしょうか。

> kubectl describe pod/tools-service-account-6664bdf7f-jzpcg

...
Events:
  Type     Reason                 Age               From                     Message
  &#8209;&#8209;&#8209;&#8209;     &#8209;&#8209;&#8209;&#8209;&#8209;&#8209;                 &#8209;&#8209;&#8209;&#8209;              &#8209;&#8209;&#8209;&#8209;                     &#8209;&#8209;&#8209;&#8209;&#8209;&#8209;&#8209;
  Normal   Scheduled              2m                default-scheduler        Successfully assigned tools-service-account-6664bdf7f-jzpcg to 10.188.103.254
  Normal   SuccessfulMountVolume  2m                kubelet, 10.188.103.254  MountVolume.SetUp succeeded for volume "service-account-1-token-kcbfz"
  Normal   Pulling                1m (x4 over 2m)   kubelet, 10.188.103.254  pulling image "registry.ng.bluemix.net/rbac-tutorial/tools-img:1"
  Warning  Failed                 1m (x4 over 2m)   kubelet, 10.188.103.254  Failed to pull image "registry.ng.bluemix.net/rbac-tutorial/tools-img:1": rpc error: code = Unknown desc = Error response from daemon:Get https://registry.ng.bluemix.net/v2/rbac-tutorial/tools-img/manifests/1: unauthorized: authentication required
  Warning  Failed                 1m (x4 over 2m)   kubelet, 10.188.103.254  Error: ErrImagePull
  Normal   BackOff                48s (x6 over 2m)  kubelet, 10.188.103.254  Back-off pulling image "registry.ng.bluemix.net/rbac-tutorial/tools-img:1"
  Warning  Failed                 48s (x6 over 2m)  kubelet, 10.188.103.254  Error: ImagePullBackOff

この出力から察するに、新しいサービス・アカウントにはイメージ・レジストリーへのアクセスが 許可されていないようです。default サービス・アカウントと service-account-1 サービス・アカウントの両方に対して get を実行することで、 極めて重要な違いがわかります。

> kubectl get sa default -o yaml

apiVersion: v1
imagePullSecrets:
- name: bluemix-default-secret
- name: bluemix-default-secret-regional
- name: bluemix-default-secret-international
kind: ServiceAccount
metadata:
  creationTimestamp: 2018-06-26T20:33:40Z
  name: default
  namespace: default
  resourceVersion: "241"
  selfLink:/api/v1/namespaces/default/serviceaccounts/default
  uid: 360775a5-7980-11e8-857f-06cd14ab6bce
secrets:
- name: default-token-x5gbt

> kubectl get sa service-account-1 -o yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"labels":{"app":"tools-rbac"},"name":"service-account-1","namespace":"default"}}
  creationTimestamp: 2018-07-11T18:05:35Z
  labels:
    app: tools-rbac
  name: service-account-1
  namespace: default
  resourceVersion: "420289"
  selfLink:/api/v1/namespaces/default/serviceaccounts/service-account-1
  uid: 02a91878-8535-11e8-857f-06cd14ab6bce
secrets:
- name: service-account-1-token-kcbfz

default サービス・アカウントには、imagePullSecrets が設定されていて、そこに一連の属性が指定されています。これらの属性により、このサービス・アカウントはイメージ・レジストリー にアクセスできるというわけです。追加で作成したサービス・アカウントの定義を更新してこれらの属性を組み込めば、 イメージを起動できるはずです。

> kubectl apply -f deploy/fix-service-account-1.yaml

serviceaccount "service-account-1" configured

> kubectl delete pod/tools-service-account-6664bdf7f-jzpcg

pod "tools-service-account-6664bdf7f-jzpcg" deleted

このエラー状態が原因で失敗した前のポッドを削除します。Kubernetes が自動的にこのポッドの起動を再試行しないようにするためです。ポッドを削除すると、デプロイメントが再びポッドの起動を試みます。 今回は成功するはずです。

> kubectl get pods
NAME                                    READY     STATUS    RESTARTS   AGE
mqtt-5ccf8b68b6-bkdf9                   1/1       Running   0          1h
tools-no-rbac-7dc96f489b-ph7h9          1/1       Running   22         22h
tools-service-account-6664bdf7f-rv5n2   1/1       Running   0          1m

8. サービス・アカウント用の Role と RoleBinding を追加する

現在、tools-service-account ポッドは service-account-1 として実行されています。次の構成により、 前に使用した Role をこのサービス・アカウントにバインドできます。

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: service-account-rolebinding
  namespace: default
  labels:
    app: tools-rbac
subjects:
- kind: ServiceAccount
  name: service-account-1
roleRef:
  kind: Role
  name: api-role
  apiGroup: ""

グローバルなアクセス権の割り当てとの違いは、対象が kind: Group ではなく kind: ServiceAccount に設定されていることです。この設定では、 この単一のサービス・アカウントにだけ API が公開されます。

この変更を適用するには、次のコマンドを使用します。

> kubectl apply -f deploy/service-account-role-assign.yaml

rolebinding.rbac.authorization.k8s.io "service-account-rolebinding" configured

9. アクセスできるかどうかテストする

ポッド内の以上の変更がどのように反映されるかを確認しましょう。それには、すべての実行中のポッド内で直接 exec を実行すればよいだけです。

> kubectl get pods
NAME                                    READY     STATUS    RESTARTS   AGE
mqtt-5ccf8b68b6-bkdf9                   1/1       Running   0          5h
tools-no-rbac-7dc96f489b-ph7h9          1/1       Running   26         1d
tools-service-account-6664bdf7f-rv5n2   1/1       Running   3          3h

> kubectl exec mqtt-5ccf8b68b6-bkdf9 kubectl get services
Error from server (Forbidden): services is forbidden: User "system:serviceaccount:default:default" cannot list services in the namespace "default"
command terminated with exit code 1

> kubectl exec tools-no-rbac-7dc96f489b-ph7h9 kubectl get services
Error from server (Forbidden): services is forbidden: User "system:serviceaccount:default:default" cannot list services in the namespace "default"
command terminated with exit code 1

> kubectl exec tools-service-account-6664bdf7f-rv5n2 kubectl get services
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                       AGE
kubernetes   ClusterIP      172.21.0.1     <none>          443/TCP                       15d
mqtt         LoadBalancer   172.21.91.88   169.60.93.179   1883:32145/TCP,80:31639/TCP   1d

前よりもずっとよくなりました。期待通り、これらの API 呼び出しを使用できるポッドは、 service-account-1 として実行中のポッドだけです。他のポッドには、 アクセス権がまったく付与されていません。

復習: これまでに学習したこと

この例から学んだことは、次のとおりです。

  • ポッドを隔離するには、デフォルトのサービス・アカウントとは別の ServiceAccount を作成します。
  • ServiceAccount がプライベート・イメージ・レジストリーにアクセスするには、 明示的な権限が必要です。
  • 特定のポッドだけが API にアクセスできるようにするには、RoleBinding を使用して Role を特定の ServiceAccount にバインドします。

10. ClusterRole と ClusterRoleBinding

RolesRoleBindings は、単一の名前空間にのみ適用されます。これまではこれで目的を果たしてきましたが、すべての名前空間にわたってアクセスを可能にしなければならない場合は どうすればよいでしょうか?あるいは、名前空間には存在しない nodes などのリソースへのアクセス、さらには リソースではない healthz エンドポイントへのアクセスが必要になった場合を考えてください。このような場合に必要となるのは、 ClusterRolesClusterRoleBindings です。

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: node-cluster-role
rules:
&#8209; apiGroups: [""]
  resources: ["nodes"]
  verbs: ["get", "list", "watch"]
&#8209;&#8209;&#8209;
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: service-account-clusterrolebinding
  labels:
    app: tools-rbac
subjects:
&#8209; kind: ServiceAccount
  name: service-account-1
  namespace: default
roleRef:
  kind: ClusterRole
  name: node-cluster-role
  apiGroup: ""

このように、ClusterRole は名前空間固有のバージョンとよく似ていますが、 大きな違いが 1 つあります。それは、subject の参照で、サービス・アカウントが存在する namespace を指定しなければならないことです。ClusterRoleBinding は 名前空間内に存在していないため、デフォルトはありません。

ClusterRole を適用するには、次のコマンドを使用します。

> kubectl apply -f deploy/service-account-cluster-role.yaml

clusterrole.rbac.authorization.k8s.io "node-cluster-role" configured
clusterrolebinding.rbac.authorization.k8s.io "service-account-clusterrolebinding" created

これで、以下の一連のコマンドが機能するようになります。

> kubectl exec tools-service-account-6664bdf7f-rv5n2 kubectl get nodes

NAME             STATUS    ROLES     AGE       VERSION
10.188.103.209   Ready     <none>    15d       v1.9.8-2+af27ab4b096122
10.188.103.229   Ready     <none>    15d       v1.9.8-2+af27ab4b096122
10.188.103.254   Ready     <none>    15d       v1.9.8-2+af27ab4b096122

一方、これらのコマンドはクラスター内の他のどのポッドから実行しても失敗します。 この ClusterRole は単一の ServiceAccount に割り当てられているためです。

> kubectl exec  tools-no-rbac-7dc96f489b-ph7h9 kubectl get nodes

Error from server (Forbidden): nodes is forbidden: User "system:serviceaccount:default:default" cannot list nodes at the cluster scope
command terminated with exit code 1

実例 - ny-power

この類のアプリケーションの内部構造を調べたいという場合に備え、このチュートリアルを作成する際にベースとして使用した サンプル・アプリケーションを紹介します。

この ny-power アプリケーションは、 ニューヨークの送電網からリアルタイムのデータを配信する MQTT メッセージ・ストリーミング・サービス です。送電網の各所から、同じ Web コンソールでこの MQTT サービスの パブリック・ポートに直接接続します。そのためには、Web サイトを起動するポッドに、ロード・バランサーからパブリック IP アドレスを、 渡す必要があります。

さらに、kubectl のようなコマンド・ライン・ツールをすべてのイメージに組み込むのは賢明な方法ではありません。 そこで、init コンテナーを作成しました。この init コンテナーが起動して各サービスを確認し、そこからパブリック IP を抽出してシークレット内に設定します。init コンテナーがこの一連の処理を完了した後に、 Web コンテナーが起動して環境からパブリック IP をプルするという仕組みです。これにより コンテナーは遥かに軽量になり、アクセスするツールの数も少なくなります。

サービス・アカウントを使用して、このポッドだけが API にアクセスできるようにします。以下に、このアプリケーションから抜粋した構成例を記載します。完全な アプリケーションの詳細については、 ここをクリックすると確認できます。

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: ny-power-web
    chart: ny-power-0.2.0
    heritage: Tiller
    release: prod
  name: prod-ny-power-web
  namespace: default
spec:
  selector:
    matchLabels:
      app: ny-power-web
      release: prod
  template:
    spec:
      serviceAccountName: prod-ny-power-readersa
      initContainers:
      &#8209; command:
        &#8209; /root/setvalue.sh
        env:
        &#8209; name: MQTT_CONTAINER_NAME
          value: prod-ny-power-mqtt
        &#8209; name: MQTT_SECRET_NAME
          value: prod-ny-power-mqtt
        image: registry.ng.bluemix.net/ny-power/ny-power-ibm-cloud:9
        imagePullPolicy: Always
        name: prod-ny-power-web-init
        resources: {}
        terminationMessagePath:/dev/termination-log
        terminationMessagePolicy: File
      containers:
      &#8209; env:
        &#8209; name: INFLUXDB_HOST
          value: prod-ny-power-influx
        &#8209; name: MQTT_HOST
          valueFrom:
            secretKeyRef:
              key: host
              name: prod-ny-power-mqtt
        &#8209; name: MQTT_PUMP_PASS
          valueFrom:
            secretKeyRef:
              key: password
              name: prod-ny-power-mqtt-pump
        image: registry.ng.bluemix.net/ny-power/ny-power-web:17
        imagePullPolicy: Always
        name: prod-ny-power-web
        ports:
        &#8209; containerPort: 80
          protocol: TCP
        resources: {}
        terminationMessagePath:/dev/termination-log
        terminationMessagePolicy: File
      restartPolicy: Always

まとめ

このチュートリアルでは、次のことを学びました。

  • Kubernetes API にはクラスター内部でアクセスできます。
  • Kuberenetes 1.8 以降から、デフォルトでは API のアクセスがロールベースのアクセス制御によって 拒否されるようになっています。
  • Role リソースと RoleBinding リソースを使用すると、特定の権限を有効にできます。
  • Secrets リソースと Configmaps リソースに対する Role権限には、 該当する secret キーまたは configmap キーの名前を組み込むことで極めて きめ細かくアクセスを制御できます。
  • アクセス権の適用範囲を絞り込むには、アプリケーション用の ServiceAccount を使用します。またはグループやユーザーではなく ServiceAccount にリンクする_RoleBinding を使用することでも 適用範囲を絞り込むことができます。
  • Kubernetes 内の一部の API では、ポッド・スコープ (ノード・アクセスなど)_が設定されません。このような API を公開するには ClusterRoleClusterRoleBinding を使用します。

さらに学習を続けるには

RBAC は難解なトピックであり、このモデルを理解するのは 簡単なことではないでしょう。RBAC を明確に把握する上で、このチュートリアルと サンプル・アプリケーションをインタラクティブに調べるスキルが役立つことを願います。

詳細を学ぶには、以下のリソースを参照してください。


ダウンロード可能なリソース


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Cloud computing
ArticleID=1066320
ArticleTitle=Kubernetes RBAC とサービス・アカウントを使用する
publish-date=07252019