Azure Kubernetes Service (AKS ) Tips 2 / RBAC

そんなわけでAKSのTipsその2です。今回はRBACメインで。

Overview

RBACはRole-Based Access Control(ロールベースのアクセス制御)の略ですね。

Azureには各リソースに対してRBACの仕組みがあります。同様にk8sにもRBACの仕組みがあります。概念的にはだいたい同じでk8sのAPIを使用する際にユーザーのロールに応じてアクセス可否を判断することができます。

今回のPostはAzure AD (OpenID Connect)との認証連携とk8sでのRBACについてのメモになります。k8sで利用可能な他の認証方式(証明書認証とか)や認可の仕組みについては割愛します。

とりあえず以下のドキュメント参照で。

k8s側は上述のドキュメントに詳しく記載されています。細かいロール管理しようとするとUsing RBAC AuthZのドキュメントとにらめっこすることになるでしょう…

How to enable RBAC

基本的には以下のドキュメントをなぞる形になります。

事前準備としてAzure ADにアプリケーションを2つ登録しておく必要があります。(MSのドキュメント通り)
1つはAKSサーバーが使用するアプリケーション、もう1つはkubectlなどクライアントで使用するアプリケーションになります。

注意点はサーバーサイド(k8sのAPI Server内)で使用する(と思われる)アプリケーション(ドキュメントだとAKSAADServer)に対してGrant Permissionsをちゃんとしておきましょう。利用するAPIに管理者の承認が必要なものがあるので、管理者側で許可を与えて置く必要があります。Admin Consent使ってもいいのですが大変なのでポータルのボタンぽちーで済むのでいいですね。またこの操作はテナント管理者でないとダメなので注意しましょう。必ずGrant Permissionsが成功することを確認しておいてください。
image

クライアント側のほうはユーザー側で対話操作で承諾できる(はず)のでしなくてもいいですが、しておくと面倒がないかもしれません。
例:
image

あとは作成したアプリケーションの情報をAzure CLIに渡してAKSを作成すればRBACが有効になります。

作成後、k8sに対して利用するユーザーやグループに許可を与えていくことになります。最初に管理者として作業するのでaz aks get-credentialsする際に –admin 引数を付けて接続情報を取得します。これでk8sに管理者で接続できるので、ClusterRoleBindingで許可を与えたいユーザーやグループを指定したyamlを作成して kubectl applyなどしましょう。(この手順がドキュメントから抜けてますが、k8sユーザーならわかりますよね多分)

※グループに対して付与する場合はnameにグループのObject ID(GUID)を指定するようにします。

あとは一般ユーザーでaz aks get-credentialsして(admin引数なしで)kubectlでk8sに接続するとデバイス認証のURLが表示されるので認証します。問題なければ正しく結果が返ってきます。もし認証エラーになったりする場合はサーバー側のGrant PermissionsやYamlを確認してください。
image

MS公式のドキュメントのYamlだとcluster-adminロールなので、本番適用したい場合は適切なロールを割り当ててください。(この辺は完全にk8sの世界)

仕組みとか

認証・認可周りの説明が面倒くさい(うさんくさい)ですが、ざっくりまとめるとこんな感じかと。※あくまでイメージ
image

最初にAKSにアクセスしてkubectlなどで使用する接続情報を取得する必要があります。az aks get-credentials がこれに相当します。この時はAzure側のRBACの仕組みが入るので適切な権限を持っているユーザーのみ取得できます。

次にkubectlなどで実際にk8sのマスターノード(API Server)にアクセスする際ですが、k8sのRBACが有効だとこの時点で認証が必要となります。というのもaz aks get-credentialsで取得した中身はマスターノードの情報など最低限のものなので、ユーザーに関する情報は含まれていないのでk8s側は認証されてないものとして扱います。認証プロバイダーがAzureに設定されてるようなので、kubectlはデバイスログインに必要なURLとコードを返してくれます。
image
認証を行うと .kube/configファイルにアクセストークンなどが追加されます。(以降、アクセストークンとリフレッシュトークンが有効な間は再度認証をきかれたりしません)

クライアント用として登録したAzure ADのアプリケーションはk8sのサーバー用アプリケーションへのアクセス権を持っています。k8sはこのクライアント用アプリケーションへのアクセスがIdPより許可(認可)されればOK(許可されたトークンが来ればOK)という感じですね。
k8s側のAuthorizationモジュールでアクセストークンのチェックをしてその後サーバー用として登録されたAzure ADアプリケーションのIDとSecretを使ってMicrosoft Graph APIへのアクセストークンを取得してテナント(Azure AD)からグループ情報などを取得してグループに割り当てられたロールのチェックとかしてると思われます(推測)。
字ばっかりですが、この辺のフローはOAuth2.0と一緒なので察してください。

もしk8s側で対象操作に権限がない場合などは以下のようなエラーになります。( kubectl auth can-i get ns など auth can-i で権限あるか確認すると no と返ってくる状態)

Error from server (Forbidden): namespaces is forbidden: User xxxx@xxxx cannot list namespaces at the cluster scope

Azure側RBACの権限

k8sへのアクセスは許可したいけどAzure上のリソースは触れさせたくないなどありますよね?
そういう場合はAKSに対して適切にRBACを設定してaz aks get-credentialsだけ許可するようなことをしたいと考えるはずです。
ですが今時点でAzure上に適切なロールがなく、AKSにReaderなどのロールで割り当てても以下のように権限不足でエラーになります。
image

The client 'xxxx@xxxxx’ with object id 'xxxxxx' does not have authorization to perform action 'Microsoft.ContainerService/managedClusters/accessProfiles/listCredential/action' over scope '/subscriptions/xxxxxxx/resourceGroups/xxxx/providers/Microsoft.ContainerService/managedClusters/xxxx/accessProfiles/clusterUser'.

今時点での解決策はカスタムロールを定義して必要な権限を付与してあげる方法があります。
カスタムロールの例はこんな感じ。

{
  "Name": "AKS read credential",
  "IsCustom": true,
  "Description": "",
  "Actions": [
    "Microsoft.ContainerService/managedClusters/accessProfiles/listCredential/action",
    "Microsoft.ContainerService/managedClusters/listClusterUserCredential/action"
  ],
  "NotActions": [
  ],
  "AssignableScopes": [
    "/subscriptions/xxxxxx-xxxx-xxxx-xxxx-xxxxxxx"
  ]
}

az role definition create –role-definition などでカスタムロールを追加後、AKSのIAMでロールを割り当てると問題なくget-credentialsできるようになります。

※ただget-credentialsで取得できる中身は大したことがない(誰でも同じような情報になる)のと、後述するadmin権限も取れてしまうので今時点のロール定義だとこの方法での運用は危ないでしょう。ということで、わざわざこんなことしなくても管理者がユーザー権限で取得した .kube/config ファイルを共有するなりする方法がベターな気がします。
ちなみにk8sのRBACが有効な状態で get-credentials した後の初期状態は以下のようになります。

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: xxxxxxxxxxxxxxxxxxxxxxx
    server: https://xxxxxxxxxxxxx.hcp.japaneast.azmk8s.io:443
  name: xxxxxxxx
contexts:
- context:
    cluster: xxxxxxx
    user: clusterUser_xxxxxxxxxxxx
  name: xxxxxxxxx
current-context: xxxxxxxxxxx
kind: Config
preferences: {}
users:
- name: clusterUser_xxxxxxxxxx
  user:
    auth-provider:
      config:
        apiserver-id: xxxxxxxxxxxxxxxx
        client-id: xxxxxxxxxxxxxxxx
        tenant-id: xxxxxxxxxxxxxxxx
      name: azure

極端な話、certificate-authority-data と server のURL(k8sのマスターノード)、apiserver-id(Azure ADに登録したサーバー側のAppId)、client-id(Azure ADに登録したクライアント側のAppId)、tenant-id (Azure ADのテナントID)がわかれば生成できます。

~/.kube/config の中身

az aks get-credentials で –admin 引数を付けた場合、管理者権限となります。認証は管理者用証明書ベースで行う感じかな?

- name: clusterAdmin_MC_xxxxxxxxx
  user:
    client-certificate-data: xxxxxxxxxxxxxxxxxxxx
    client-key-data: xxxxxxxxxxxxxxxxxxx
    token: xxxxxxxxxxxx

client-certificate-data と client-key-data がconfigに追加されます。
ちなみにRBAC無しk8sだとget-credentialsで取得できる情報はadmin引数の有無に関係なく同じ(管理者権限の情報)になります。

RBACが有効なk8s/AKSに対してユーザー権限でaz aks get-credentialsした後、kubectlなどで実際にアクセス/認証するとconfigにアクセストークンなどが追加されます。

- name: clusterUser_xxxxxxxxxxxxxxxxxx
  user:
    auth-provider:
      config:
        access-token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
        apiserver-id: xxxxxxxxxxxxxxxxxxxx
        client-id: xxxxxxxxxxxxxx
        expires-in: "3600"
        expires-on: "1534280385"
        refresh-token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
        tenant-id: xxxxxxxxxxxxx
      name: azure

この access-token はJWTなトークンなので、そのままBearerトークンとして使えますしデコードするとちゃんと中身が見れます。
image
APIを直接叩くこともできます(結局kubectlと大差ないのですが)
image

AKS作成後にRBACを有効にしたい

今のところAzure CLI経由かARMテンプレート経由で作成時のみRBACを有効化できます(つまり後から有効化できません)。またAzureポータルからAKS作成する際にRBAC有効にするかどうかのフラグがありますが、有効にしてもプロパティのenableRBACがtrueにセットされるだけでaadProfileなどは作成されません。(Oh..)
image
Preview機能なのでそのうち直るんでしょうかね…( –authorization-mode=RBAC はされてる雰囲気なのでk8s側で頑張ればなんとかなるのか…?ならんか…)
aadProfileは後からARM Explorerなどで追加しようとしてもエラーになります。(既存AKSに対しての更新に対応してくれることを期待したい)
image
ARMテンプレートでAKS作成する際は上図のようにaadProfileを構成すれば大丈夫と思います。(enableRBACもtrueにするのを忘れずに)

Kubernetes Dashboardへのアクセス

k8sのRBACが有効な状態で kubectl proxy(またはaz aks browse)やマスターノードのk8sダッシュボードへアクセスすると、権限がなくてエラーが表示されます。
image

とりあえずサービスアカウント権限で何でも誰でも見れてもいいので回避したい場合はClusterRoleBindingを使ってkube-system:kubernetes-dashboardサービスアカウントにcluster-adminを割り当てます。

kubectl create clusterrolebinding kubernetes-dashboard --clusterrole=cluster-admin --serviceaccount=kube-system:kubernetes-dashboard

あとはaz aks browseかkubectl proxyで http://localhost:8001/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy/ にアクセスすればダッシュボードが見えると思います。

上の方法はちょっとあんまりなので、 /#!/login で表示されるログインフォームに取得した(.kube/configなどに記載されている)アクセストークンを使ってログインすれば権限に応じて見れると思います。

image

※ただしMicrosoft Edgeだとダメだった。

その他注意点

  • k8sのRBACは今のところ対象テナントのユーザー・グループのみとなります。(MSアカウントやGuest扱いの他のテナントのユーザーなどは今のところ不可)
    そのうちAzure AD B2Cなどにも対応してほしいところですが…
  • k8sのAuthorizationモジュールでRBACを使う際の連携先はAzureなどだと簡単ですが独自OIDCだと面倒くさそうです。(少なくともDiscoveryに対応してないとダメだったような)

まとめ

  • RBACはAKS作成時に設定しよう
  • AKSへの権限付与は慎重に(admin権限取れたらk8s側のRBAC意味ないので…)
  • aks get-credentialはadmin引数無しで取得したものをk8s利用者に渡すのが良いかも(今時点の話)
  • k8s側のRBACの設定は慎重に(意図した通りのロール管理は慣れないと大変と思う)

参考

字ばっかりで疲れましたね。。

コメントを残す