はじめに

平素は大変お世話になっております。
クイックガードのパー子です。

弊社の最近の傾向として、Kubernetesクラスタの運用をお任せいただく案件が多くなってきています。
このような案件においては、正式に受注する前の環境調査のために、まずはクラスタの参照権限のみ頂戴しています。

クラウド上にシステムを構築している場合、IAM (= Identity and Access Management) のメカニズムに基づいて権限を付与するのが一般的です。
近年ではマネージドの Kubernetesサービスを利用するシステムが増えてきていますが、IAM と Kubernetes の権限を結びつける仕組みは各クラウド・プロバイダによって少しずつ異なっており、適切な権限管理のためにはそれらの違いを正しく理解する必要があります。

本記事では、EKS (AWS)、GKE (Google Cloud)、AKS (Microsoft Azure) のそれぞれにおいて、ベンダーなどの外部パートナーに対して参照権限を付与する方法を解説します。

シナリオ

具体的に以下のシナリオを想定し、それに基づいて権限付与の仕組みを探っていきます。

動機と背景

すでに Kubernetesクラスタの構築が完了し、運用が開始されているものとします。
顧客はこのクラスタの運用の一部をベンダーに委任することを考えています。

一方、ベンダーは正式な受発注に先立って、見積もりやリスク評価のためにクラスタ環境の調査を必要としています。

調査の際、操作ミスによってクラスタが破壊されるリスクを回避したいため、付与される権限は参照に限定します。

権限付与の基準

アカウント管理のベストプラクティスに則って、権限付与はクラウド・プロバイダの IAM の仕組みに基づいて行います。

顧客 (= クラスタの所有者) とベンダーは異なる組織であり、そして、ゲスト・アカウントを作ることなく組織間で権限を委任したいと考えています。

また、スタッフのアカウントを個別に扱うのは煩雑なので、グループ単位で権限を管理したいところです。

スコープの限定

1つのクラスタ内に独立した複数のサービスが相乗りしているケースも多いため、特定の Namespace に限定して権限を付与する必要があります。
これにより、ベンダーが不意に委任契約の範囲外のサービスへアクセスしてしまうリスクを回避できます。

権限モデル

以上のシナリオを踏まえて、それぞれのクラウド・プロバイダが提供するマネージドKubernetes の権限モデルについて概説します。

EKS

EKS では、aws-auth という ConfigMap を使って IAM と Kubernetes の RBACマッピング します。

具体的には、Kubernetes の RBAC でグループと権限を定義しつつ、さらに ConfigMap で IAMプリンシパルと当該グループを紐づけます。

ここで指定できる IAMプリンシパルは、IAMロールと IAMユーザの 2種類 です。

IAMグループは、

An IAM group isn’t an IAM principal, so it can’t be added to the ConfigMap.

とのことなので、ConfigMap に追加することはできません。
そのため、グループに権限付与したい場合は代わりに IAMロールを ConfigMap に定義したうえで、当該ロールを AssumeRole で引き受けるようにします。

また、kubectl のコンテキストを設定するために IAM権限 eks:DescribeCluster が必要です。

GKE

GKE での アクセス制御 には、(a) Kubernetes の RBAC を使う方法と、(b) Kubernetes RBAC を触らずに Google Cloud の IAMロールを単体で使う方法の 2つがあります。

詳細な制御が必要な場合は Kubernetes RBAC を使用し、大雑把な制御でよければ IAMロールが適しています。

RoleBinding の Subject として Googleグループを指定 できますが、そのためには目的のグループを gke-security-groups という 名称固定のグループの下にネストさせておく 必要があります。

kubectl のコンテキストを設定するために 必要な IAM権限container.clusters.get です。
IAMロールとして Kubernetes Engine Cluster Viewer (container.clusterViewer) あたりを割り当てておくとよいでしょう。

AKS

AKSでは、認証に Azure Active Directory (= Azure AD) を使用できます。

認可については、GKE と同じく (a) Kubernetes RBAC と (b) Azureロールのどちらも使用できます。

認可に用いることができる Azureロールは 組み込みのものが 4つ あるほか、権限要件に応じて カスタムロール を定義することもできます。
(ただし、細かなアクセス制御が必要な場合は素直に Kubernetes RBAC を使用したほうがわかりやすいかと思います。)

また、アカウント・プリンシパルにロールを割り当てる際にスコープを限定すれば、権限を行使できる Namespace を絞ることができます。

Kubernetes RBAC を使用する 場合、Role binding する際にユーザやグループの Object ID を指定します。

kubectl のコンテキスト設定に必要な Azureロールは Azure Kubernetes Service Cluster User Role です。
似たロールに Azure Kubernetes Service Cluster Admin Role も存在しますが、これは 認証に AD を用いない特権ローカル・アカウント を使用して Kuberneteseリソースを操作するためのロールなので、本稿では扱いません。

設定例

権限モデルを理解したところで、次は実際にサービスを触りながら設定してみましょう。

なお、Kubernetesクラスタには以下の構成で Pod がデプロイされているものとします。
名前空間 ns-a に限定した参照権限を設定することを目指します。

名前空間Pod
ns-anginx-a
ns-bnginx-b
namespace-a.yml
---
apiVersion: 'v1'
kind: 'Namespace'
metadata:
  name: 'ns-a'
namespace-b.yml
---
apiVersion: 'v1'
kind: 'Namespace'
metadata:
  name: 'ns-b'
pod-a.yml
---
apiVersion: 'v1'
kind: 'Pod'
metadata:
  name: 'nginx-a'
spec:
  containers:
    - name: 'nginx'
      image: 'nginx:1.25.1-alpine3.17-slim'
      ports:
        - containerPort: 80
pod-b.yml
---
apiVersion: 'v1'
kind: 'Pod'
metadata:
  name: 'nginx-b'
spec:
  containers:
    - name: 'nginx'
      image: 'nginx:1.25.1-alpine3.17-slim'
      ports:
        - containerPort: 80
$ kubectl get namespaces
NAME              STATUS   AGE
default           Active   47h
kube-node-lease   Active   47h
kube-public       Active   47h
kube-system       Active   47h
ns-a              Active   4m38s
ns-b              Active   4m35s

$ kubectl get -n 'ns-a' pods
NAME      READY   STATUS    RESTARTS   AGE
nginx-a   0/1     Pending   0          2m7s

$ kubectl get -n 'ns-b' pods
NAME      READY   STATUS    RESTARTS   AGE
nginx-b   0/1     Pending   0          60s

EKS

AWS では、ベンダーは クロスアカウント で顧客環境のロールにスイッチしているケースが多いと思います。
そのため、ベンダーに委任する IAMロールに対して EKS のアクセス権限を付与します。

顧客側の作業

目的のロールの ARN は arn:aws:iam::123456789012:role/K8sReadOnlyRole-Vendor であり、IAM権限 eks:DescribeCluster を有しているものとします。

まずはこのロールを ConfigMap aws-auth に追加します。

$ kubectl edit -n 'kube-system' configmaps 'aws-auth'

エディタが開くので、IAMロールと Kubernetesユーザ/グループを適当な名前でマッピングします。

data:
  mapRoles: |
    - ...(デフォルトのマッピング)...
    - rolearn: 'arn:aws:iam::123456789012:role/K8sReadOnlyRole-Vendor'
      username: 'vendor'
      groups:
        - 'vendor'    

続いて名前空間 ns-a に対する参照権限を持つ Role を用意します。

Kubernetes の 組み込みClusterRole として view が存在するので、これを名前空間 ns-a と、ConfigMap aws-auth でマッピングしたグループ vendor に紐づけます。

./role-binding-eks.yml
---
apiVersion: 'rbac.authorization.k8s.io/v1'
kind: 'RoleBinding'
metadata:
  name: 'vendor'
  namespace: 'ns-a'
subjects:
  - kind: 'Group'
    name: 'vendor'
    apiGroup: 'rbac.authorization.k8s.io'
roleRef:
  kind: 'ClusterRole'
  name: 'view'
  apiGroup: 'rbac.authorization.k8s.io'
$ kubectl apply -f ./role-binding-eks.yml
rolebinding.rbac.authorization.k8s.io/vendor created

ベンダー側の作業

ベンダー側の作業は特にありません。

ベンダーのアカウントを使って EKSクラスタにアクセスできるか確認します。
以下の操作を行い、狙いどおりに操作が許可 or 拒否されるかを見ます。

ケース名前空間操作実行可否
1ns-aGet
2ns-aEdit不可
3ns-bGet不可
4クラスタ全体Get不可

なお、この EKSクラスタの名称は eks-cluster とします。

# kubeconfig セットアップ
$ aws eks update-kubeconfig --name 'eks-cluster'
Added new context arn:aws:eks:ap-northeast-1:123456789012:cluster/eks-cluster to /Users/yumeko/.kube/config

# ケース1 => OK
$ kubectl get -n 'ns-a' pods
NAME      READY   STATUS    RESTARTS   AGE
nginx-a   0/1     Pending   0          4h31m

# ケース2 => OK
$ kubectl edit -n 'ns-a' pods 'nginx-a'
error: pods "nginx-a" could not be patched: pods "nginx-a" is forbidden: User "vendor" cannot patch resource "pods" in API group "" in the namespace "ns-a"
You can run `kubectl replace -f /var/folders/dy/wzzvq1nd309_5r55lbcttdqc0000gn/T/kubectl-edit-1730301081.yaml` to try this update again.

# ケース3 => OK
$ kubectl get -n 'ns-b' pods
Error from server (Forbidden): pods is forbidden: User "vendor" cannot list resource "pods" in API group "" in the namespace "ns-b"

# ケース4 => OK
$ kubectl get namespaces
Error from server (Forbidden): namespaces is forbidden: User "vendor" cannot list resource "namespaces" in API group "" at the cluster scope

以上のとおり、名前空間 ns-a のリソースの閲覧はできるものの変更はできず、他の名前空間やクラスタ全体のスコープのリソースにはアクセスできないことが確認できました。

GKE

ベンダーのグループに対して Kubernetes RBAC を適用します。

なお、顧客側とベンダー側のドメインをそれぞれ以下とします。

組織ドメイン
顧客customer.quickguard.net
ベンダーvendor.quickguard.net

顧客側の作業

gke-security-groups というグループのメンバーに、参照権限を与えたいベンダーのグループを追加します。

ここでは、対象のグループを team-yumeko@vendor.quickguard.net としました。

また、このグループに IAMロール Kubernetes Engine Cluster Viewer (container.clusterViewer) を割り当てておきます。

続いて、Google Cloud のコンソールにて目的の GKEクラスタを選択し、“DETAILS"タブ » “Security"セクション » “Google Groups for RBAC” を有効化します。

目的のグループに対して Role を割り当てます。

./role-binding-gke.yml
---
apiVersion: 'rbac.authorization.k8s.io/v1'
kind: 'RoleBinding'
metadata:
  name: 'vendor'
  namespace: 'ns-a'
subjects:
  - kind: 'Group'
    name: 'team-yumeko@vendor.quickguard.net'
    apiGroup: 'rbac.authorization.k8s.io'
roleRef:
  kind: 'ClusterRole'
  name: 'view'
  apiGroup: 'rbac.authorization.k8s.io'
$ kubectl apply -f ./role-binding-gke.yml
rolebinding.rbac.authorization.k8s.io/vendor created

ベンダー側の作業

ベンダー側で必要な作業はありません。

参照権限を付与されたグループのメンバー・アカウント (ここでは gondawara-yumeko@vendor.quickguard.net) に切り替えて GKEクラスタにアクセスしてみます。

目的の GKEクラスタの名称は gke-cluster とし、EKS と同じく 4つのケースについて確認します。

# Google Cloud ログイン
$ gcloud auth login 'gondawara-yumeko@vendor.quickguard.net'
Your browser has been opened to visit:

    https://accounts.google.com/o/oauth2/auth?...(snip)...


You are now logged in as [gondawara-yumeko@vendor.quickguard.net].
Your current project is [proj-a].  You can change this setting by running:
  $ gcloud config set project PROJECT_ID

# kubeconfig セットアップ
$ gcloud container clusters get-credentials --region 'asia-northeast1' 'gke-cluster'
Fetching cluster endpoint and auth data.
kubeconfig entry generated for gke-cluster.

# ケース1 => OK
$ kubectl get -n 'ns-a' pods
NAME      READY   STATUS    RESTARTS   AGE
nginx-a   1/1     Running   0          13h

# ケース2 => OK
$ kubectl edit -n 'ns-a' pods 'nginx-a'
error: pods "nginx-a" could not be patched: pods "nginx-a" is forbidden: User "gondawara-yumeko@vendor.quickguard.net" cannot patch resource "pods" in API group "" in the namespace "ns-a": requires one of ["container.pods.update"] permission(s).
You can run `kubectl replace -f /var/folders/dy/wzzvq1nd309_5r55lbcttdqc0000gn/T/kubectl-edit-1937391533.yaml` to try this update again.

# ケース3 => OK
$ kubectl get -n 'ns-b' pods
Error from server (Forbidden): pods is forbidden: User "gondawara-yumeko@vendor.quickguard.net" cannot list resource "pods" in API group "" in the namespace "ns-b": requires one of ["container.pods.list"] permission(s).

# ケース4 => OK
$ kubectl get namespaces
Error from server (Forbidden): namespaces is forbidden: User "gondawara-yumeko@vendor.quickguard.net" cannot list resource "namespaces" in API group "" at the cluster scope: requires one of ["container.namespaces.list"] permission(s).

AKS

本来ならば Azure Lighthouse を用いてベンダーに権限を委任したいところですが、現時点では残念ながら Kubernetesリソースへのアクセスを委任することができません。

方式Lighthouse との併用不可の理由
Kubernetes RBACAKSクラスタを複数の Azure AD に紐づけることができないため。
(通常はすでに 管理者用グループ のために顧客側の Azure AD が紐づいているはず。)
AzureロールLighthouse が Kubernetesリソースの認可用ロール (= Azure Kubernetes Service RBAC *) をサポートしていないため。

そのため、管理が面倒ですが、ベンダーのユーザを顧客テナントにゲストとして招待する必要があります。
認可の方式はどちらでも構いませんが、Kubernetes RBAC を用いることにします。

顧客側の作業

まず、適当なグループを作り、そこにベンダーのスタッフをゲスト・ユーザとして招待します。
(グループの Object ID は ce260d4a-52ea-e541-3f16-14b9bbe6ddae とします。)

そのグループに、Azureロール Azure Kubernetes Service Cluster User Role を与えておきます。

続いて、当該グループに Role を割り当てます。

./role-binding-aks.yml
---
apiVersion: 'rbac.authorization.k8s.io/v1'
kind: 'RoleBinding'
metadata:
  name: 'vendor'
  namespace: 'ns-a'
subjects:
  - kind: 'Group'
    name: 'ce260d4a-52ea-e541-3f16-14b9bbe6ddae'
    apiGroup: 'rbac.authorization.k8s.io'
roleRef:
  kind: 'ClusterRole'
  name: 'view'
  apiGroup: 'rbac.authorization.k8s.io'
$ kubectl apply -f ./role-binding-aks.yml
rolebinding.rbac.authorization.k8s.io/vendor created

ベンダー側の作業

ゲスト・ユーザとして AKSクラスタにアクセスします。

Azure AD を用いて認証するために、事前に Azure Kubelogin をインストールしておきます。

AKSクラスタの名称は aks-cluster で、リソース・グループ aks-rg に配置されているものとします。

確認のパターンは EKS と同じく 4ケースです。

$ az aks get-credentials -g 'aks-rg' -n 'aks-cluster'
Merged "aks-cluster" as current context in /Users/yumeko/.kube/config

# ケース1 => OK
$ kubectl get -n 'ns-a' pods
NAME      READY   STATUS    RESTARTS   AGE
nginx-a   1/1     Running   0          2d7h

# ケース2 => OK
$ kubectl edit -n 'ns-a' pods 'nginx-a'
error: pods "nginx-a" could not be patched: pods "nginx-a" is forbidden: User "3386fb83-dc08-c2cb-5202-fa421226bef0" cannot patch resource "pods" in API group "" in the namespace "ns-a": User does not have access to the resource in Azure. Update role assignment to allow access.
You can run `kubectl replace -f /var/folders/dy/wzzvq1nd309_5r55lbcttdqc0000gn/T/kubectl-edit-3175532603.yaml` to try this update again.

# ケース3 => OK
$ kubectl get -n 'ns-b' pods
Error from server (Forbidden): pods is forbidden: User "3386fb83-dc08-c2cb-5202-fa421226bef0" cannot list resource "pods" in API group "" in the namespace "ns-b": User does not have access to the resource in Azure. Update role assignment to allow access.

# ケース4 => OK
$ kubectl get namespaces
Error from server (Forbidden): namespaces is forbidden: User "3386fb83-dc08-c2cb-5202-fa421226bef0" cannot list resource "namespaces" in API group "" at the cluster scope: User does not have access to the resource in Azure. Update role assignment to allow access.

まとめ

本記事では、各クラウド・プロバイダのマネージドKubernetesサービスにおいて、ベンダーなどの外部パートナーに対して参照権限のみを付与する方法をご紹介しました。

EKS (AWS)、GKE (Google Cloud)、AKS (Microsoft Azure) について、それぞれの権限モデルを解説したあと、実際にサービスを触って別組織のアカウントに対して参照権限を付与する設定例を見ました。

この記事が、外部パートナーに過大な権限を持たせることなく、適切なレベルに留めて委任するための一助になれば幸いです。

今後ともよろしくお願い申し上げます。


当記事の図表には AWS Architecture IconsGoogle Cloud product iconsMaterial SymbolsAzure architecture iconsKubernetes logo を使用しています。