APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

【Azure】AKSのkubectlの認証周りを調べてみた

f:id:tarutaru_pyorne:20210609131755p:plain

はじめに

Azureコンテナソリューショングループ)土居です。
我々の部署ではAzureにフォーカスをあてクラウドネイティブ関連の技術を追求しお客様をサポートしていきます。 その一環としてAzureに関して日々ブログで技術紹介していきますので宜しくお願いします。

今回はAKSで利用するkubectlの権限周りについて紹介していきます。

kubectlと~/.kube/configについて

Kubernetesで何らかの操作を行う場合は一般的に「kubectl」を利用しますが、kubectlで操作対象とするKubernetesクラスターや操作するUserなどを指定する必要があります。 kubectlは通常、~/.kube/configという設定ファイルに上記情報を格納します。 また、複数のクラスタやユーザーを管理できるように、接続先や使用ユーザーを切り替えるContextという仕組みも用意されています。

#.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に紐付けるユーザー名>

例えば、複数クラスターの.kube/configを所持している場合は、以下のコマンド等で確認・変更が可能です。

#k8s-doitest2という同じクラスターに対して2種類のユーザーでアクセスするコンテキストを所持
#現在kubectlではデフォルトでk8s-doitest2を利用するようになっている

k_doi@Azure:~$ kubectl config get-contexts
CURRENT   NAME                 CLUSTER        AUTHINFO                             NAMESPACE
*         k8s-doitest2         k8s-doitest2   clusterUser_doi_test_k8s-doitest2
          k8s-doitest2-admin   k8s-doitest2   clusterAdmin_doi_test_k8s-doitest2
          
#kubectlで利用するコンテキストをk8s-doitest2-adminに変更する

k_doi@Azure:~$ kubectl config use-context k8s-doitest2-admin
Switched to context "k8s-doitest2-admin".
k_doi@Azure:~$ kubectl config get-contexts
CURRENT   NAME                 CLUSTER        AUTHINFO                             NAMESPACE
          k8s-doitest2         k8s-doitest2   clusterUser_doi_test_k8s-doitest2
*         k8s-doitest2-admin   k8s-doitest2   clusterAdmin_doi_test_k8s-doitest2          

また、上記は.kube/configに複数の設定を保持していますが、

  • 設定ファイルを複数に分ける
kubectl config --kubeconfig=<ファイル名> use-context <コンテキスト名>
  • 環境変数KUBECONFIGを利用する

の方法で複数管理することも可能です。

az aks get-credentials

AKSではこの~/.kube/configの取得にaz aks get-credentialsコマンドを利用できます。 既定でAKSを作成した場合、az aks get-credentialsを実行するとARM APIを通じて.kube/configを取得します。 ここで誰でも.kube/configを取得できるとよろしくないので、az aks get-credentialsでARM APIにアクセスする際にAzure RBAC認証が既定で組み込まれており、Azureのロールと取得できるKubernetesのロールが紐付いています。

Azure Role 管理者トークン ユーザートークン
Owner
Contributor
Azure Kubernetes Service Cluster Admin Role
Azure Kubernetes Service Cluster User Role
Reader

では、実際に管理者トークンとユーザートークンを取得しどのようなKubernetesロールが割り当てられているのか見てみます。管理者トークンを取得する際は、az aks get-credentialsに--adminを加えればOKです。

#ユーザートークン情報
- name: clusterUser_<リソースグループ名>_<k8sクラスター名>
  user:
    client-certificate-data: <base64エンコードされた証明書データ>
    client-key-data: <base64エンコードされたキーデータ>
    token: <トークン情報>
    
#管理者トークン情報    
- name: clusterAdmin_<リソースグループ名>_<k8sクラスター名>
  user:
    client-certificate-data: <base64エンコードされた証明書データ>
    client-certificate-data: <base64エンコードされた証明書データ>
    client-key-data: <base64エンコードされたキーデータ>
    token: <トークン情報>

az aks get-credentialsで~/.kube/configを取得すると、上記のようなuserオブジェクトが作成されます。client-certificate-dataとあるので、デフォルトではAKSはx509証明書で認証を行っている事がわかります。また、ここで注目してもらいたいのが、管理者トークンとユーザートークンでは取得しているclient-certificate-dataと client-key-dataが全く同じである点です。(ここでは**で隠していますが、実際に取得してもらえば同じになっている事が分かると思います。)
このclient-certificate-dataの中身を解析するとSubject: O = system:masters, CN = masterclientこのような情報が入っています。

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            db:8e:00:3a:2e:30:ce:17:d1:03:84:71:dc:ba:dc:58
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN = ca
        Validity
            Not Before: Jun  1 03:57:49 2021 GMT
            Not After : Jun  1 04:07:49 2023 GMT
        Subject: O = system:masters, CN = masterclient
        ~~中略~~

system:mastersこのグループがKubernetesのロールに紐付いています。 これは、cluster-adminというKubernetesのClusterRoleに該当し、こいつはAKSクラスターリソースに対して何でもできちゃう最も強力な権限です。

k_doi@Azure:~$ kubectl get clusterrolebindings cluster-admin -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  creationTimestamp: "****-**-*****:**:***"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: cluster-admin
  resourceVersion: "101"
  selfLink: /apis/rbac.authorization.k8s.io/v1/clusterrolebindings/cluster-admin
  uid: ********-****-****-****-********
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:masters
k_doi@Azure:~$ kubectl get clusterrole cluster-admin -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  creationTimestamp: "****-**-*****:**:***"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: cluster-admin
  resourceVersion: "44"
  selfLink: /apis/rbac.authorization.k8s.io/v1/clusterroles/cluster-admin
  uid: ********-****-****-****-********
rules:
- apiGroups:
  - '*'
  resources:
  - '*'
  verbs:
  - '*'
- nonResourceURLs:
  - '*'
  verbs:
  - '*'

つまり、管理者トークンとユーザートークンどちらも一番強力な権限を取得できるという点では全く違いがなく細かな権限付与はできません。これは、AKSをデフォルトでデプロイした場合(AAD統合をしない)の既定の動作になります。もちろん、Azure Kubernetes Service Cluster User Roleのみ持っているユーザーでも結局は~/.kube/configを取得すればそれはAKSのcluster-adminロールを持ちます。

docs.microsoft.com

Azure AD を使用するクラスターでは、clusterUser ロールのユーザーには空の kubeconfig ファイルがあり、これによってログインを求められます。 ログインすると、ユーザーは、Azure AD のユーザーまたはグループの設定に基づいてアクセスできます。 clusterAdmin ロールのユーザーは管理者アクセス権を持ちます。 Azure AD を使用していないクラスターは、clusterAdmin ロールのみを使用します。

AAD統合AKSにおけるkubectlの認証

AKSを新規作成する場合に、AADと統合を行うことができます。 このAAD統合の利点は次の3点があります。

  1. kubectl実行時にユーザー認証を行うよう矯正できる。
  2. Azure Active Directoryのユーザー毎にKuberntesのロールを設定することができる。
  3. Kubernetesのロールを使わず、Azure RBACでKubernetesリソースへの権限管理が可能(プレビュー)

まずは、AAD統合AKSの作成方法について記載します。 もちろん、既存のAKSを後からAAD統合することも可能です。 サービスプリンシパルでの作り方(レガシ)はここでは割愛します。

#CLIから作成する場合は--enable-addを追加する
az aks create -g myResourceGroup -n myManagedCluster --enable-aad

f:id:tarutaru_pyorne:20210608155255p:plain
GUIからAAD統合を有効にしてAKSを作成する場合

  • kubectl実行時にユーザー認証を行うよう矯正できる。

AAD統合を行ったAKSから.kube/configを取得すると、管理者は証明書とトークン情報を、ユーザは認証プロバイダーにAADを指定と違いがでたことが分かります。

users:
- name: clusterUser_doi_test_k8s-doitest5
  user:
    auth-provider:
      config:
        apiserver-id: ********-****-****-*****-************
        client-id: ********-****-****-*****-************
        config-mode: '1'
        environment: AzurePublicCloud
        tenant-id: ********-****-****-*****-************
      name: azure
- name: clusterAdmin_doi_test_k8s-doitest5
  user:
  user:
    client-certificate-data: <base64エンコードされた証明書データ>
    client-certificate-data: <base64エンコードされた証明書データ>
    client-key-data: <base64エンコードされたキーデータ>
    token: <トークン情報>

ユーザー側の認証プロバイダーのほうはまだAKS接続に必要な情報を持ち合わせていません。この後、AADにログインし必要な権限を割り当てられます。

Azure AD を使用するクラスターでは、clusterUser ロールのユーザーには空の kubeconfig ファイルがあり、これによってログインを求められます。 ログインすると、ユーザーは、AAD のユーザーまたはグループの設定に基づいてアクセスできます。

ユーザーのコンテキストでkubectlで何か実施すると以下のような認証が求め、Web画面上でAzureへのログインが成功しない限りkubectlが実行されません。 つまり、~/.kube/configファイルが万が一流出して不正なアクセスが行われてもAzure側の認証で防ぐ事が可能となります。

k_doi@Azure:~$ kubectl get clusterrole
To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code ******** to authenticate.

f:id:tarutaru_pyorne:20210608154849p:plain
Azure認証画面(その1)

f:id:tarutaru_pyorne:20210608155031p:plain
Azure認証画面(その2)

--adminのコンテキストのほうは既にトークンを.kube/configに持ち合わせているためAAD統合してもAzureの認証は発生しません。この事から、一般的に--adminで取得するのはトラブル等の緊急時を除いては推奨されません。
AKSのAAD統合時に管理者グループを設定できます。指定されたAzureのグループは下記のようにAKSのClusterRoleBindingsにKubernetesのcluster-adminロールとAzureの指定グループが関連付けられるのでそちらを利用します。

k_doi@Azure:~$ kubectl describe clusterrolebindings aks-cluster-admin-binding-aad
Name:         aks-cluster-admin-binding-aad
Labels:       addonmanager.kubernetes.io/mode=Reconcile
              kubernetes.io/cluster-service=true
Annotations:  <none>
Role:
  Kind:  ClusterRole
  Name:  cluster-admin
Subjects:
  Kind   Name                                  Namespace
  ----   ----                                  ---------
  Group  ********-****-****-*****-************ <Group-ID>
  Group  ********-****-****-*****-************ <GROUP-ID>


* Azureのユーザー毎にKuberntesのロールを設定することができる

せっかくAzureユーザーで認証できるならそのユーザー毎にKubernetesの触れるリソースを制限したいと思うのはごく普通の流れです。Azureのユーザーを用いてKubernetesのRBACを使った方法がこちらです。

docs.microsoft.com

f:id:tarutaru_pyorne:20210608160350p:plain
Azure ADロールとKubernetes RBACの関係性

大規模環境やAKSクラスター上に複数プロジェクトなどが稼働しnamespaceで分割されるようなケースではこちらの利用が一般的かと思います。
しかし、KubernetesRBACを使うデメリットとしてあげられるのが、ユーザーグループとロールをAADとAKS別々で管理する点です。例えば、退職やチーム離脱時のRoleBindingsへのID削除忘れであったり、トラブルシューティングの複雑化などの問題もあります。
また、RoleBindingにGroupIDやメールアドレスを登録しなければならず、AzureAADの画面を開きながら見比べる必要がありかなり視認性が悪いです。それらを解消すると期待されているのが、次のプレビュー機能(2021.6.7現在)のAzure RBACです。

  • Kubernetesのロールを使わず、Azure RBACでKubernetesリソースへの権限管理が可能(プレビュー)

~/.kube/configを取得する際にはAzureのロール権限で取得できるconfigを制御しました。なのでKubernetesのRBACも全てAzureで一元管理できれば楽ですよね。
Azure RBACを利用すると、KubernetesリソースへのアクセスもAzure RBACで行うことが可能になります。動作原理はKubernetesのAuthorizationにKubernetes RBACを利用せずにAzure RBACを利用します。Azureリソース外(Kubernetsのサービスアカウント)の認可は通常のKubernets RBACが利用されます。

docs.microsoft.com

f:id:tarutaru_pyorne:20210608160455p:plain
Azure RBACによるAKSリソース権限管理

この機能を有効化することでAzure上の以下の組み込みロールを利用できるので、これらを割り当ててKubernetesのリソース権限付与を行います。
管理者はKubernetesのRoleBindingやRoleを管理する必要がなくなります。(Azureがよろしくやってくれる、多分裏ではAzureがRoleBindingsとか自動で作成してくれているだけのような気もしますが、これらは時間あればまた確認してみます)

Azure Role 管理者トークン
Azure Kubernetes Service RBAC 閲覧者 名前空間内のほとんどのオブジェクトを表示するための読み取り専用アクセスが許可されます。 ロールまたはロールのバインドを表示することはできません。 このロールでは、Secrets の表示は許可されません。これは、Secrets の内容を読み取ると、名前空間の ServiceAccount 資格情報にアクセスでき、それにより名前空間の任意の ServiceAccount として API にアクセスできるようになるためです (特権エスカレーションの形式)
Azure Kubernetes Service RBAC ライター 名前空間内のほとんどのオブジェクトに対する読み取りと書き込みのアクセスが許可されます。 このロールでは、ロールまたはロールのバインドを表示または変更することはできません。 ただし、このロールを使用すると、Secrets にアクセスし、名前空間内の任意の ServiceAccount としてポッドを実行できるので、名前空間内の任意の ServiceAccount の API アクセス レベルを取得するために使用できます。
Azure Kubernetes Service RBAC 管理者 名前空間内で付与されることが意図された、管理者アクセスが許可されます。 名前空間内でロールおよびロール バインドを作成する能力など、名前空間 (またはクラスター スコープ) 内のほとんどのリソースへの読み取りおよび書き込みアクセスが許可されます。 このロールでは、リソース クォータまたは名前空間自体への書き込みアクセスは許可されません。
Azure Kubernetes Service RBAC クラスター管理者 任意のリソースに対して任意のアクションを実行できるスーパー ユーザー アクセスが許可されます。 これにより、クラスター内およびすべての名前空間内のすべてのリソースを完全に制御できます。

また、上記のロールはAKSの特定の名前空間への割当(RoleBinding相当)も可能で、AADのカスタムロール作成では、AKSでRoleを作成するようにnamespacesやactionsなどを指定することが可能のようです。

#Azure CLIによるロール割当例
az role assignment create --role "Azure Kubernetes Service RBAC Viewer" --assignee <AAD-ENTITY-ID> --scope $AKS_ID/namespaces/<namespace-name>


#カスタムロール定義例 .jsonファイル
{
    "Name": "AKS Deployment Viewer",
    "Description": "Lets you view all deployments in cluster/namespace.",
    "Actions": [],
    "NotActions": [],
    "DataActions": [
        "Microsoft.ContainerService/managedClusters/apps/deployments/read"
    ],
    "NotDataActions": [],
    "assignableScopes": [
        "/subscriptions/<YOUR SUBSCRIPTION ID>"
    ]
}

AKSのユーザー管理は
1. az aksコマンドの実行ロール
2. ~/kube/configを取得するロール
3. AKSのリソースロール

と複数観点で考慮が必要になるため、GAになればこちらが管理面からも推奨になる可能性も十分にありますし、少し動向を追ってみたいと思います。

その他

その他、kubectlの認証・認可におけるセキュリティ要件を高める方法として、以下2つがあります。

docs.microsoft.com

docs.microsoft.com

プライベートクラスターは一般的に制限事項がかなり多くなり設計や運用負荷もかなり高くなるので、相当な要件事項がない限り使うのは避けたほうがよいかと思います。 それよりは、上記のAAD統合やAADの条件アクセス制限であったり、APIサーバのIP制限などで指定された環境以外からのアクセスを遮るパターンで落とし所を見つけるのがよいかなと思います。