Azure Kubernetes Service と Azure Active Directory の統合 (1)

自分向けのメモとして、Azure Kubernetes Service のカスタマイズについて学びます。RBACをActiveDirectoryと統合して使ってみたいと思います。

* [Integrate Azure Active Directory with Azure Kubernetes Service using the Azure CLI](https://docs.microsoft.com/en-us/azure/aks/azure-ad-integration-cli)

# AzureADサーバーコンポーネントの作成

ServicePrincipalとそのアプリケーション、AzureADグループメンバーシップを作成します。AKSとAzure AD の認証は、[OpenID Connect](https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-protocols-openid-connect-code)で実施されます。Kubernetes側は、[webhook authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#webhook-token-authentication)という仕組みを使っている様子。

## AD アプリケーションの作成と、グループメンバーシップクレームの設定変更

ADアプリケーションを作ったのちに、`groupMembershipClaim` というプロパティを変更します。　設定の意味や取りうるオプションはこちら。[Azure Active Directory app manifest](https://docs.microsoft.com/en-us/azure/active-directory/develop/reference-app-manifest)、ユーザーか、もしくは、アプリが期待するOAuth2.0のアクセストークンのグループのクレームのコンフィグレーションです。全てのセキュリティグループを受けいれるよう設定しています。claim は、疑わしいものに対して正しいかを認める行為です。

```
aksname="tsushicluster"
# Create the Azure AD application
serverApplicationId=$(az ad app create \
    --display-name "${aksname}Server" \
    --identifier-uris "https://${aksname}Server" \
    --query appId -o tsv)

# Update the application group memebership claims
az ad app update --id $serverApplicationId --set groupMembershipClaims=All
```
## サービスプリンシパルの作成

先ほど作成したアプリケーションのIDを使って、サービスプリンシパルを作成して、クレデンシャルをリセットしています。これは、自動でパスワードを取得するためです。パスワードは作成時しか獲得できないからです。

```
# Create a service principal for the Azure AD application
az ad sp create --id $serverApplicationId

# Get the service principal secret
serverApplicationSecret=$(az ad sp credential reset \
    --name $serverApplicationId \
    --credential-description "AKSPassword" \
    --query password -o tsv)
```
## パーミッションの付与とGrant

パーミッションを設定します。また、それに許可を与えます。このコマンドだけをみるとわけがわからないのですが、MicrosoftGraphのAPIに対して

* Directory のデータの読み込み
* Sin in と、ユーザプロファイルの読み込み

の権限を与えています。

```
az ad app permission add \
    --id $serverApplicationId \
    --api 00000003-0000-0000-c000-000000000000 \
    --api-permissions e1fe6dd8-ba31-4d61-89e7-88639da4683d=Scope 06da0dbc-49e2-44d2-8312-53f166ab848a=Scope 7ab1d382-f21e-4acd-a863-ba3e13f7da61=Role
```

GUI でやる手順が出ているので、そちらをみるとイメージしやすいと思います。
　
* [Use Azure Portal](https://docs.microsoft.com/en-us/azure/aks/azure-ad-integration)

 次に設定したパーミッションを有効化します。ADのAdminの権限が必要です。

```
az ad app permission grant --id $serverApplicationId --api 00000003-0000-0000-c000-000000000000
az ad app permission admin-consent --id  $serverApplicationId
```

当初自分の環境では、`az ad app permission admin-consent` が見つからないと言われた。AzureCLIのバージョンを最新にしてみたら動作しました。`2.0.58`では動作せず`2.0.69`では動作しました。

## Consent とは

辞書で調べると、Consent は何かをする権限を与えることで、ADにはコンセントフレームワークと言うものがあります。

* [Consent Framework](https://docs.microsoft.com/en-us/azure/active-directory/develop/consent-framework)

# Azure AD クライアントコンポーネントの作成

次に、同じように、Azure AD アプリケーションを作成します。これは、ユーザがAKSのクラスタにkubectlを使ってコマンドを発行するときに使われます。ユーザからの認証リクエストを取得して、クレデンシャルを認証、認可します。サーバーのそれと同じように作ります。

```
clientApplicationId=$(az ad app create \
    --display-name "${aksname}Client" \
    --native-app \
    --reply-urls "https://${aksname}Client" \
    --query appId -o tsv)
```

```
az ad sp create --id $clientApplicationId
```
OAtuh2 ID を取得します。

```
oAuthPermissionId=$(az ad app show --id $serverApplicationId --query "oauth2Permissions[0].id" -o tsv)
```

ちなみに、`az ad app show --id $serverApplicationId` とすると詳細が帰ってきますが、oAuthPermissionの部分は次のようになります。

```
  "oauth2Permissions": [
    {
      "adminConsentDescription": "Allow the application to access tsushiclusterServer on behalf of the signed-in user.",
      "adminConsentDisplayName": "Access tsushiclusterServer",
      "id": "<<上記のoauthPermissionIdがここに>>",
      "isEnabled": true,
      "type": "User",
      "userConsentDescription": "Allow the application to access tsushiclusterServer on your behalf.",
      "userConsentDisplayName": "Access tsushiclusterServer",
      "value": "user_impersonation"
    }
```

そしてパーミッションを与えます。これは、APIに対してパーミッションを与えるものです。ここでは、サーバーのAPIに対してパーミションを与えています。

* [az ad app permission add](https://docs.microsoft.com/en-us/cli/azure/ad/app/permission?view=azure-cli-latest#az-ad-app-permission-add)

```
az ad app permission add --id $clientApplicationId --api $serverApplicationId --api-permissions $oAuthPermissionId=Scope
az ad app permission grant --id $clientApplicationId --api $serverApplicationId
```

# AKS クラスタの作成

Updateも試したがうまくいかなかった。新しいクラスタをデプロイする方が良さそう。

```
az group create --name myResourceGroup --location EastUStenantId=$(az account show --query tenantId -o tsv)

tenantId=$(az account show --query tenantId -o tsv)

az aks create \
    --resource-group myResourceGroup \
    --name $aksname \
    --node-count 1 \
    --generate-ssh-keys \
    --aad-server-app-id $serverApplicationId \
    --aad-server-app-secret $serverApplicationSecret \
    --aad-client-app-id $clientApplicationId \
    --aad-tenant-id $tenantId

az aks create \
    --resource-group UshioTestGroup \
    --name $aksname \
    --node-count 1 \
    --generate-ssh-keys \
    --aad-server-app-id $serverApplicationId \
    --aad-server-app-secret $serverApplicationSecret \
    --aad-client-app-id $clientApplicationId \
    --aad-tenant-id $tenantId
```

`--aad-server-app-id` はサービスプリンシパルを付与した、Appで、タイプは、Web app/APIに何る。これは、KubernetesのAPIサーバーのアプリケーションになる。また、`--aad-client-app-id` はNativeタイプで、kubectlを使うときのユーザ認証、認可で使われる。

## admin ユーザのクレデンシャルを取得

kubectl で使うクレデンシャルを取得します。そのときに、Admin権限のものを取得しておきます。

```
az aks get-credentials --resource-group myResourceGroup --name $aksname --admin
```

# RBAC バインディング

ClusterRoleBindingを作成します。Admin権限が必要です。

_basic-azure-ad-binding.yml_

```
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: contoso-cluster-admins
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: userPrincipalName_or_objectId
```

一番最後の、ユーザプリンシパルの名前か、オブジェクトIDのところをちゃんと埋めるようにします。同じテナントの場合は、ユーザプリンシパル名を使い、そうでない場合は、オブジェクトIDを使うのが良いらしいです。これで、取得できます。

```
az ad signed-in-user show --query userPrincipalName -o tsv
```

UPN(UserPrincipalName)って何だろう？と思ってし食べたら、システムユーザの名前らしい。ってシステムユーザーって何？

[What is UPN](https://www.codetwo.com/kb/upn/)

先ほどの、yamlを適用します。

```
kubectl apply -f basic-azure-ad-binding.yml
```

# テスト

Admin権限ではなく、一般のユーザの権限に戻します。

```
az aks get-credentials --resource-group myResourceGroup --name $aksname --overwrite-existing
```

この状態で、kubectlを実行します。

```
kubectl get pods
```

すると、ブラウザでの認証が求められて、そのあと結果が帰って来れば成功

```
$ kubectl get pods --all-namespaces
To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code <<SOMECODE>> to authenticate.
NAMESPACE     NAME                                    READY     STATUS    RESTARTS   AGE
kube-system   coredns-696c56cf6f-d24hz                1/1       Running   0          44m
kube-system   coredns-696c56cf6f-s97d4                1/1       Running   0          40m
kube-system   coredns-autoscaler-bc55cb685-qh765      1/1       Running   0          44m
kube-system   heapster-5678f88989-km7kr               2/2       Running   0          40m
kube-system   kube-proxy-hbj7k                        1/1       Running   0          41m
kube-system   kubernetes-dashboard-7b749f655b-cp654   1/1       Running   1          44m
kube-system   metrics-server-5b7d5c6f8d-m5l9h         1/1       Running   1          44m
kube-system   tunnelfront-5db696d55d-h5997            1/1       Running   0          44m
```

では次は、セキュリティグループを使って、グループに対して、権限を付与するケースを試します。

* [Control access to cluster resources using role-based access control and Azure Active Directory identities in Azure Kubernetes Service](https://docs.microsoft.com/en-us/azure/aks/azure-ad-rbac)