APC 技術ブログ

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

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

【Azure】AKSデプロイの権限周りについて調べてみた

f:id:tarutaru_pyorne:20210609131755p:plain

はじめに

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

今回はAKSデプロイに纏わる権限周りについて紹介していきます。

AKS(Azure Kubernetes Service)とは

AKSはAzureが提供するマネージドのKubernetesが利用できるサービスです。 デプロイすると指定したリソースグループにコントロールプレーンにあたるリソースが作成されます。
また、AKSは複数のAzureリソースを利用するため、指定したリソースグループとは別にノードリソースグループ(MC_k8sのリソースグループ名_k8sクラスター名_location)を作成します。 デフォルトではワーカーノードであるVMSSやネットワーク関連(VNet、LoadBalancer、PublicIPなど)のリソースがノードリソースグループ内に作成されます。

マネージドIDとサービスプリンシパル

Kubernetesの管理操作を行うには一般的に「kubectl」コマンドを利用する事は既にご承知かと思います。kubectlによる指示を受けて、Kubernetes(コントロールプレーン)は様々なリソースへの設定変更作業を行います。 AKSの場合、つまり我々はkubectlを利用してAzure CLIからではなくAKS自体にこのAzureリソースの操作を行わせています。 つまり、AKSサービスにAzureリソースへの操作権限を与える必要があります。 Azureにおいてサービスにリソースへの権限を付与するためには、マネージドIDサービスプリンシパルという2つの概念があり、AKSでもこの2つの方法が用意されています。

マネージドID、サービスプリンシパルがよくわからない方は、弊社ブログか、日本マイクロソフト様の動画が大変参考になりますので参考にして下さい。

techblog.ap-com.co.jp

2:26:30 ~ サービスプリンシパルとマネージドID

www.youtube.com

マネージドID方式でAKSをデプロイした場合、作成後に変更できません。一方サービスプリンシパルは、クライアントシークレット期限切れに伴う更新やサービスプリンシパル自体の入替え、マネージドIDへの移行などが可能です。マネージドIDはAADに大きく依存しており、AADのテナント移行などもサポートされていません。
しかし、サービスプリンシパルの大きな欠点として挙げられるのがクライアントシークレットの管理です。 サービスプリンシパルはその認証にクライアントシークレットを利用する必要があります。 このクライアントシークレットは有効期限が最長24ヶ月であり、期限切れに合わせて更新作業が必要になります。この更新作業を忘れると、AKSはAzureリソースへの操作権限を一時的に失うことになり、運用環境に重大な影響が発生します。 さらにクライアントシークレットには当然パスワードが必要なので、必然的にパスワード管理を考慮する必要があります。この管理作業が面倒であり、更新を忘れた時の影響度が高いことなどからマネージドID方式が現在推奨されています。

Azure PortalだとマネージドIDが既定で選択されていますが、Azure CLIでは--enable-managed-identityをつける必要がありますので注意して下さい。

f:id:tarutaru_pyorne:20210609174028p:plain
Azure PortalでのAKSデプロイ画面

#サービスプリンシパルを使用してAKSクラスターを構築
az aks create --name myAKSCluster --resource-group myResourceGroup

#マネージドIDを使用してAKSクラスターを構築
az aks create -g myResourceGroup -n myManagedCluster --enable-managed-identity

AKSデプロイ時に必要な権限

AKSのデプロイを行うユーザーは、デプロイ手法に応じて様々な権限を持ち合わせている必要があります。以下にデプロイに必要な権限を記載します。

1. マネージドIDを使用してAKSを新規デプロイする場合

  • デプロイ先のリソースグループに対するAzure Kubernetes Service 共同作成者ロール

Azure Kubernetes Service 共同作成者ロールではMicrosoft.ContainerServiceプロバイダーに対する書き込み権限があります。AKSの新規デプロイの実態はこのプロバイダーが担います。マネージドIDの作成やノードリソースグループへのアクセス権限の付与、VMSSなどのリソースの作成も行います。

docs.microsoft.com

2. サービスプリンシパルを使用してAKSを新規デプロイする場合

  • デプロイ先のリソースグループに対するAzure Kubernetes Service 共同作成者ロール
  • サービスプリンシパルの作成権限

サービスプリンシパルの場合はAzure Kubernetes Service 共同作成者ロールに加え、AKSデプロイの実行ユーザーでサービスプリンシパルを事前に作成するため、サービスプリンシパルの作成権限が必要です。 サービスプリンシパルの作成権限はAzureAD側の話になります。 具体的な設定箇所は、「Azure Active Directory 」-「ユーザー設定」-「アプリの登録」の値です。 この値がはい(デフォルト)になっていれば、Azure ADのメンバーであれば誰でもサービスプリンシパルを登録できるようになっているため、Azure AD側の権限は不要です。 もしいいえになっている場合は、「アプリケーション開発者」「アプリケーション管理者」などのAzureADロールの割当が必要になります。

f:id:tarutaru_pyorne:20210609180029p:plain
AzureADのアプリ登録設定

f:id:tarutaru_pyorne:20210609180103p:plain
アプリケーション開発者、アプリケーション管理者ロール

docs.microsoft.com

3. 他のリソースへのアクセスが必要な場合

  • サブスクリプションに対する所有者ロール、またはユーザーアクセス管理者
  • デプロイ先のリソースグループに対するAzure Kubernetes Service 共同作成者ロール

AKSをデプロイする場合、通常は関連リソースは全てノードリソースグループに集約されます。ただ、既存のVNetにデプロイしたり、既存のApplication GatewayをIngressで利用する等でノードリソースグループとは別のリソースグループにアクセスをする場合が状況によっては発生します。 その場合、AKSが利用するマネージドIDやサービスプリンシパルに対して対象リソースへのアクセス権限を割り当てる必要があります。そのため、アクセス権限を操作できる所有者またはユーザーアクセス管理者のロールが必要になりますので注意して下さい。

docs.microsoft.com

色々紹介しましたがデプロイ操作を権限エラーなどで詰まりたくないので、結局のところAKSの管理者はクラスターへの操作を行う場合は所有者または共同作成者+ユーザーアクセス管理者権限を割り当てて作業を行うことが望ましいかと思います。

アクセス制御(IAM)とリソースグループ

Azureリソースへの操作権限はアクセス制御(IAM)で管理されており、サブスクリプションやリソースグループ、リソースなど各階層に設定することができます。 AKSの場合、ノードリソースグループに対して共同作成者(Contributor)ロールが割り当てられます。割当先は上記のマネージドIDまたはサービスプリンシパルです。 AKSはマネージドIDまたはサービスプリンシパルを利用してAzureリソースの操作を行います。

f:id:tarutaru_pyorne:20210609180514p:plain
ノードリソースグループに共同作成者ロールが割当たっている

試しに割り当てられた共同作成者権限をノードリソースグループから削除したり、サービスプリンシパルを期限切れにしてみました。そうすると、Load BalancerやIngressなどの外部IPが払い出されないエラーが発生しました。AKSがAzureリソースの操作をうまく行えていないことが分かります。
マネージドIDの場合、手動でContributorロールを追加し直しましたがAKSクラスターを再起動しないとうまく動作しませんでした。サービスプリンシパルならaz aks update-credentialsを行い新しいサービスプリンシパルで置き直せばクラスターの再起動までは不要なようです。

k_doi@Azure:~$ kubectl get svc
NAME              TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
kubernetes        ClusterIP      10.0.0.1      <none>        443/TCP        2d7h
wordpress         LoadBalancer   10.0.86.252   <pending>     80:32470/TCP   2d6h
wordpress-mysql   ClusterIP      None          <none>        3306/TCP       19h
k_doi@Azure:~$ kubectl describe svc wordpress
Name:                     wordpress
Namespace:                default
Labels:                   app=wordpress
Annotations:              <none>
Selector:                 app=wordpress,tier=frontend
Type:                     LoadBalancer
IP Families:              <none>
IP:                       10.0.86.252
IPs:                      <none>
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  32470/TCP
Endpoints:                10.244.3.6:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:
  Type     Reason                  Age                     From                  Message
  ----     ------                  ----                    ----                  -------
  Warning  ListLoadBalancers       4m3s (x7124 over 2d6h)  azure-cloud-provider  (combined from similar events): Retriable: false, RetryAfter: 0s, HTTPStatusCode: 401, RawError: Retriable: false, RetryAfter: 0s, HTTPStatusCode: 401, RawError: azure.BearerAuthorizer#WithAuthorization: Failed to refresh the Token for request to http://localhost:7788/subscriptions/********-****-****-*****-************/resourceGroups/mc_doi_test_k8s-doitest_japaneast/providers/Microsoft.Network/loadBalancers?api-version=2019-06-01: StatusCode=401 -- Original Error: adal: Refresh request failed. Status Code = '401'. Response body: {"error":"invalid_client","error_description":"AADSTS7000222: The provided client secret keys are expired. Visit the Azure Portal to create new keys for your app, or consider using certificate credentials for added security: https://docs.microsoft.com/azure/active-directory/develop/active-directory-certificate-credentials\r\nTrace ID: ********-****-****-****-************\r\nCorrelation ID: ********-****-****-****-************\r\nTimestamp: 2021-05-24 21:51:04Z","error_codes":[7000222],"timestamp":"2021-05-24 21:51:04Z","trace_id":"********-****-****-****-************","correlation_id":"********-****-****-****-************","error_uri":"https://login.microsoftonline.com/error?code=7000222"}
  Warning  SyncLoadBalancerFailed  4m3s (x7124 over 2d6h)  service-controller    (combined from similar events): Error syncing load balancer: failed to check if load balancer exists before cleanup: Retriable: false, RetryAfter: 0s, HTTPStatusCode: 401, RawError: Retriable: false, RetryAfter: 0s, HTTPStatusCode: 401, RawError: azure.BearerAuthorizer#WithAuthorization: Failed to refresh the Token for request to http://localhost:7788/subscriptions/********-****-****-****-************/resourceGroups/mc_doi_test_k8s-doitest_japaneast/providers/Microsoft.Network/loadBalancers?api-version=2019-06-01: StatusCode=401 -- Original Error: adal: Refresh request failed. Status Code = '401'. Response body: {"error":"invalid_client","error_description":"AADSTS7000222: The provided client secret keys are expired. Visit the Azure Portal to create new keys for your app, or consider using certificate credentials for added security: https://docs.microsoft.com/azure/active-directory/develop/active-directory-certificate-credentials\r\nTrace ID: ********-****-****-****-************\r\nCorrelation ID: ********-****-****-****-************\r\nTimestamp: 2021-05-24 21:51:04Z","error_codes":[7000222],"timestamp":"2021-05-24 21:51:04Z","trace_id":"********-****-****-****-************","correlation_id":"********-****-****-****-************","error_uri":"https://login.microsoftonline.com/error?code=7000222"}

終わりに

本記事の記載にあたり、以下記事を参考にしています。

docs.microsoft.com