AKS の Active Directory インテグレーションを触ってみる

本ブログは自分の学習のメモとしての内容で、実際にやっている内容は、次のオフィシャルドキュメントの内容である。
https://docs.microsoft.com/en-us/azure/aks/managed-aad

# Azure AD 認証の概要
Azure AD 認証は、AKSに対して OpenID Connect と共に提供される。[OpenID Connect](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-protocols-oidc)は、OAuth 2.0 protocol の上に作られた Identity Layer である。

Kubernetes クラスタの内部から、 Webhook Token Authentication が認証トークンを Verify するのに使われる。[Webhook Token Authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#webhook-token-authentication) はAKSクラスターの一部として構成される。

# 前提条件

`az` コマンドの 2.11.0 以上インストールは必須ですが、それ以外にも、テナントのAdminである必要があります。例えば私は自社のAdminではないですが、旧MSDNのメンバーシップはあるので、そちらの方だと私はAdminの権限を持っているのでそちらのSubscriptionで実施する必要がありました。

# AD グループの作成

AD のグループを作成します。下記の２つのパラメータは必須なので記述していますが、他にもパラメータはあります。`--description` などは後々便利かも。まだ、グループという概念が正確に理解できていませんが、こののちわかるのでしょう。

```bash
$ az ad group create --display-name tsushiAksGroup --mail-nickname tsushiAksGroup
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/3e8d5a30-2dd7-72e9-7a8a-e16e2b0e11d0.png)

# AD enabled の AKS クラスタを作成する

ちなみに、aks を japanwest にデプロイしようとしましたが、私の Subscription (旧MSDN) ではだめなようでした。 先ほどのコマンドで、取得した、`objectId`を `--aad-admin-group-object-ids` に引き渡してあげれば良いようです。

```bash
$ az aks create -g $resourceGroup -n $aksName --enable-aad --aad-admin-group-object-ids <<objectId>>
```

先ほど設定した`objectId` が、`adminGroupObjectIds`にセットされており、`tenantId` が正しければデプロイ成功。

```
{                                                                     
  "aadProfile": {
    "adminGroupObjectIds": [
      "<<objectId>>"
    ],
    "clientAppId": null,
    "enableAzureRbac": false,
    "managed": true,
    "serverAppId": null,
    "serverAppSecret": null,
    "tenantId": "<<tenantId>>"
  },
```

# Kubernetes のクレデンシャルの取得

```bash
$ az aks get-credentials --resource-group $resourceGroup --name $aksName
```

普通に`kubectl get nodes` を実施すると思いっきり失敗するがな。

```
kubectl get nodes
To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code <<CODE>> to authenticate.
E0606 21:21:29.761965    1809 round_trippers.go:174] CancelRequest not implemented by *azure.azureRoundTripper
error: You must be logged in to the server (Unauthorized)
```

いろいろ試したがダメだったので、質問を投げて次に進む
https://github.com/MicrosoftDocs/azure-docs/issues/76479

## Root Cause

最初の手順を怠っていたため。自分は、`kubectl` を持っているので必要ないと思ってスキップしたが、今のバージョンでは、`kubectl`に加えて、[kubelogin](https://github.com/int128/kubelogin)というのが含まれており、これが、AD 連携を行ってくれる。これは、`kubectl`のプラグインなので、これ無しでは、当然 ADのインテグレーションは使えないということ。

```bash
$ sudo az aks install-cli
```

# admin でログインする

先ほどは失敗したので、admin でログインして [Control access to cluster resources using Kubernetes role-based access control and Azure Active Directory identities in Azure Kubernetes Service](https://docs.microsoft.com/en-us/azure/aks/azure-ad-rbac)　のページに進んでみよう。

```
$ az aks get-credentials --resource-group $resourceGroup --name ${aksName} --admin
```

# AD のアカウントを作成

DevとSREのアカウントを作成してみる。`ADD_DEV_UPN`, `ADD_DEV_PW` 、`ADD_SRE_UPN`、`ADD_SRE_PW` にそれぞれ、User Principal Name (あなたのドメインのユーザ名。ドメインをつけて、`xxx@xxxx.com`　の形式で作成する。パスワードはルールが厳しいので、大文字小文字、英数、記号を交えて作成。　その後次のようなスクリプトを作成して流してみる。

とても単純なのですが、AD のグループを作成して

```bash
$ APPDEV_ID=$(az ad group create --display-name appdev --mail-nickname appdev --query objectId -o tsv)
$ OPSSRE_ID=$(az ad group create --display-name opssre --mail-nickname opssre --query objectId -o tsv)
```

その後、ADのグループにユーザを作成して、追加していきます。

```bash
AKS_ID=$(az aks show --resource-group $resourceGroup --name ${aksName} --query id -o tsv)

AKSDEV_ID=$(az ad user create \
  --display-name "AKS Dev" \
  --user-principal-name $AAD_DEV_UPN \
  --password $AAD_DEV_PW \
  --query objectId -o tsv)

az ad group member add --group appdev --member-id $AKSDEV_ID


AKSSRE_ID=$(az ad user create \
  --display-name "AKS SRE" \
  --user-principal-name $AAD_SRE_UPN \
  --password $AAD_SRE_PW \
  --query objectId -o tsv)

az ad group member add --group opssre --member-id $AKSSRE_ID
```
# Kubernetes のリソースの作成
## Namespace の作成

Kubernetes 側の作業として、ネームスペースをそれぞれ `dev` `sre` と作成します。作成ししたネームスペースに対して、`Role` と `RoleBindings` を作成していきます。ポイントは、`ServiceAccount` を使う代わりに、`Group` を定義して、ADのグループの`objectId`を引き渡します。

## Role の作成
Dev, SRE それぞれの Role を定義します。それぞれの Namespace を使えるようにします。

```yaml:role-dev-namespace.yaml
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: dev-user-full-access
  namespace: dev
rules:
- apiGroups: ["", "extensions", "apps"]
  resources: ["*"]
  verbs: ["*"]
- apiGroups: ["batch"]
  resources:
  - jobs
  - cronjobs
  verbs: ["*"]
```

```yaml:role-sre-namespace.yaml
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: sre-user-full-access
  namespace: sre
rules:
- apiGroups: ["", "extensions", "apps"]
  resources: ["*"]
  verbs: ["*"]
- apiGroups: ["batch"]
  resources:
  - jobs
  - cronjobs
  verbs: ["*"]
```

## Role Bindings

Role Bindings の方で、AD の Group の `objectId` を渡します。これだけ！

```bash
kubectl create namespace dev 

kubectl apply -f role-dev-namespace.yaml

groupObjectId=$(az ad group show --group appdev --query objectId -o tsv)

cat << EOF > rolebinding-dev-namespace.yaml
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: dev-user-access
  namespace: dev
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: dev-user-full-access
subjects:
- kind: Group
  namespace: dev
  name: ${groupObjectId}
EOF

kubectl apply -f rolebinding-dev-namespace.yaml

kubectl create namespace sre 
kubectl apply -f role-sre-namespace.yaml

groupObjectId=$(az ad group show --group opssre --query objectId -o tsv)

cat << EOY > rolebinding-sre-namespace.yaml
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: sre-user-access
  namespace: sre
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: sre-user-full-access
subjects:
- kind: Group
  namespace: sre
  name: ${groupObjectId}
EOY

kubectl apply -f rolebinding-sre-namespace.yaml 
```



# テスト実行
クレデンシャルをクリアしておきます。

```bash
az aks get-credentials --resource-group $resourceGroup --name $aksName --overwrite-existing
```

次に、対象のオペレーションを行うと、AD のログインが要求されます。今回は`dev`でログインしたので、devにはデプロイ可能です。

```bash
kubectl run nginx-dev-01 --image=mcr.microsoft.com/oss/nginx/nginx:1.15.5-alpine --namespace dev
To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code SVXPBTST3 to authenticate.
pod/nginx-dev-01 created
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/4b7f2c97-8464-74df-07b7-12af59346306.png)

SREのネームスペースにデプロイすると当然はじかれます。

```sre
kubectl run nginx-sre-01 --image=mcr.microsoft.com/oss/nginx/nginx:1.15.5-alpine --namespace sre
Error from server (Forbidden): pods is forbidden: User "appdev@ushiosimplearchitect.onmicrosoft.com" cannot create resource "pods" in API group "" in the namespace "sre"
```

自分が試したときには、なぜか、dev は、正しい動きだけど、sre はなぜか devにもデプロイできました。上記の k8s のオブジェクトを見直しましたが、正しくコンフィグレーションされています。では何だったか？というと、AD の設定ミスでした。両方登録されとるがな。

```bash
az ad group member list --group appdev --query '[].userPrincipalName'
[
  "appdev@ushiosimplearchitect.onmicrosoft.com",
  "sre@ushiosimplearchitect.onmicrosoft.com"
]
```


## 内部的には
Service Account と default secrets が作られているようですね

```bash
 kubectl get sa default -n sre -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: "2021-06-07T05:51:57Z"
  name: default
  namespace: sre
  resourceVersion: "9761"
  selfLink: /api/v1/namespaces/sre/serviceaccounts/default
  uid: 4059160d-a83d-4a89-a7ec-fc08cda977c5
secrets:
- name: default-token-z5sng
```

# 終わりに
自分のしょーもない２つのミスで時間をロスしました。

* kubelogin の手順を飛ばしていた
* AD のグループの登録をミスしていた

多分スピーディにやろうとし過ぎて失敗していると思うので、確認しながら実施したほうが結局の早く物事が進みそうです。反省。しかし、大体インテグレーションのイメージはつくようになりました。