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

前回に続いて、AKSとAD(Active Directory)の連携機能を試してみたい。ADのグループのメンバーのクラスタの特定のネームスペースに対するアクセス制限をコンフィグレーションしてみる。KubernetesのRBAC(Role-based-access-control)を使用する。基本的には、下記の記事を理解しながら実施する

* [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)

今回は、開発者と、SREの２つのロールの権限を設定します。

# AD グループの作成

ADのグループの作成を行います。ポータルからも可能ですが、AzureCLIでサクッと作ります。まずは、開発者用のグループです。

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

できたか確認してみる。

```
$ az ad group list --query '[*].displayName' -o tsv
Team5
appdev
```

既存のグループがあったみたいだが、`appdev`が無事作成されている。ちなみに、Azure CLIのクエリーの文法は、[JMSPath](http://jmespath.org/tutorial.html) と言うものらしい。よく使うので、これから常に使えるようにしておこう。

# ロールのアサイン

まず、現在の、クラスタのIDを取得する。

```
AKS_ID=$(az aks show \
    --resource-group myResourceGroup \
    --name myAKSCluster \
    --query id -o tsv)
```

このIDの中身をみてみると、一意になるURLである。

```
$ echo $AKS_ID
/subscriptions/<<YOUR_SUBSCRIPTION>>/resourcegroups/UshioTestGroup/providers/Microsoft.ContainerService/managedClusters/tsushicluster
```

グループにロールをアサインしよう。クラスタに対してロールをアサインしただけで、まずは何の権限もない。

```
az role assignment create \
  --assignee $APPDEV_ID \
  --role "Azure Kubernetes Service Cluster User Role" \
  --scope $AKS_ID
```

ポータルのADのページに行くと、新しいロールがアサインされていることがわかる。AKSのページの`Access Control(IAM) > Role assignments` 

<img width="1210" alt="Screen Shot 2019-07-18 at 11.13.19 PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/cc9aa3be-b639-a718-b5cc-99501feedaa1.png">

同じノリで、SREのグループを作成して、ロールをアサインする。

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

```
az role assignment create \
  --assignee $OPSSRE_ID \
  --role "Azure Kubernetes Service Cluster User Role" \
  --scope $AKS_ID
```

ポータルで確認すると、しっかり増えている。

<img width="1008" alt="Screen Shot 2019-07-18 at 11.17.06 PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/0991f0d7-1483-deba-226e-2f429af22665.png">

## グループへのメンバーの追加

メンバーのオブジェクトIDを与えて、グループに参加させる。ObjectIDはポータルからみることができる。`appdev` `opssre`それぞれ登録する。ポータルではなく、コマンドラインで実施したい場合は次の通り。

```
$ az ad user list --query '[*].[displayName, objectId]'
```

これで見つけたユーザのオブジェクトIDを下記のコマンドでグループに登録しよう。

```
az ad group member add --group appdev --member-id <<User ObjectId>>
```

確認してみる。

```
$ az ad group member list --group appdev --query '[*].displayName' -o tsv
Hacker Two
invincible:azure-voting-app-redis ushio$ az ad group member list --group opssre --query '[*].displayName' -o tsv
Hacker Six
```

しっかり入っている様子。

# ネームスペースの割り当てとロールの割り当て

この作業はk8sの管理者ユーザで実施する。次のコマンドで管理者用のクレデンシャルを取得して、`.kube/config` に設定することができる。

```
az aks get-credentials --resource-group $RESOURCE_GROUP --name $AKSNAME --admin
```

開発者用のNamespaceをK8S側で作成。

```
kubectl create ns dev
```

Kubernetes では、`Role`を定義して、パーミッションを付与して、`RoleBindings`を使って、ユーザやグループに紐付けるようになる。下記の設定では、`dev-user-full-access`と言うロールを作成している。`dev`のネームスペースに対して、全てのリソースに対して、全ての操作が行えるように設定されている。

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

rules以下に、APIグループごとに設定を行う。APIグループは、APIをグループ化したもので、""はcoreAPIを表す。下記のリファレンスページをみると各APIにグループが記載されている。また、下記のコマンドでも現在のグループを閲覧できる。

```bash
$ kubectl api-resources -o wide
NAME                              SHORTNAMES   APIGROUP                       NAMESPACED   KIND                             VERBS
bindings                                                                      true         Binding                          [create]
componentstatuses                 cs                                          false        ComponentStatus                  [get list]
configmaps                        cm                                          true         ConfigMap                        [create delete deletecollection get list patch update watch]
endpoints                         ep                                          true         Endpoints                        [create delete deletecollection get list patch update watch]
events                            ev                                          true         Event                            [create delete deletecollection get list patch update watch]
limitranges                       limits                                      true         LimitRange                       [create delete deletecollection get list patch update watch]
namespaces                        ns                                          false        Namespace                        [create delete get list patch update watch]
nodes                             no                                          false        Node                             [create delete deletecollection get list patch update watch]
persistentvolumeclaims            pvc                                         true         PersistentVolumeClaim            [create delete deletecollection get list patch update watch]
persistentvolumes                 pv                                          false        PersistentVolume                 [create delete deletecollection get list patch update watch]
pods                              po                                          true         Pod                              [create delete deletecollection get list patch update watch]
podtemplates                                                                  true         PodTemplate                      [create delete deletecollection get list patch update watch]
replicationcontrollers            rc                                          true         ReplicationController            [create delete deletecollection get list patch update watch]
resourcequotas                    quota                                       true         ResourceQuota                    [create delete deletecollection get list patch update watch]
secrets                                                                       true         Secret                           [create delete deletecollection get list patch update watch]
serviceaccounts                   sa                                          true         ServiceAccount                   [create delete deletecollection get list patch update watch]
services                          svc                                         true         Service                          [create delete get list patch update watch]
initializerconfigurations                      admissionregistration.k8s.io   false        InitializerConfiguration         [create delete deletecollection get list patch update watch]
mutatingwebhookconfigurations                  admissionregistration.k8s.io   false        MutatingWebhookConfiguration     [create delete deletecollection get list patch update watch]
validatingwebhookconfigurations                admissionregistration.k8s.io   false        ValidatingWebhookConfiguration   [create delete deletecollection get list patch update watch]
customresourcedefinitions         crd,crds     apiextensions.k8s.io           false        CustomResourceDefinition         [create delete deletecollection get list patch update watch]
apiservices                                    apiregistration.k8s.io         false        APIService                       [create delete deletecollection get list patch update watch]
controllerrevisions                            apps                           true         ControllerRevision               [create delete deletecollection get list patch update watch]
daemonsets                        ds           apps                           true         DaemonSet                        [create delete deletecollection get list patch update watch]
deployments                       deploy       apps                           true         Deployment                       [create delete deletecollection get list patch update watch]
replicasets                       rs           apps                           true         ReplicaSet                       [create delete deletecollection get list patch update watch]
statefulsets                      sts          apps                           true         StatefulSet                      [create delete deletecollection get list patch update watch]
tokenreviews                                   authentication.k8s.io          false        TokenReview                      [create]
localsubjectaccessreviews                      authorization.k8s.io
   :
```

どう言う操作が設定可能なのかは１００％クリアではないですが、公式の[cluster-roles.yaml](https://github.com/kubernetes/kubernetes/blob/master/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml)が参考になるかも。だいたいこんなもの。抜粋してみた。

```
- create 
- delete 
- deletecollection 
- patch 
- update 
- get 
- list 
- watch 
```

リソースに関しては`kubectl get api-resources` コマンドで一覧をみることができる。

*[Role and ClusterRole](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)
*[Role](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.15/#role-v1-rbac-authorization-k8s-io)

解析できたので、Applyしておく

```
kubectl apply -f role-dev-namespace.yaml
```

## RoleBindings

AD側のグループのオブジェクトIDを取得しておく。

```
az ad group show --group appdev --query objectId -o tsv
```

これで、オブジェクトIDが取得できるので、これを、RoleBindingsで使用する。`groupObjectId` を書き換える。

```
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
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
```

同じようにapplyしておく。

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

同様に、SRE側の設定を実施アプライする。

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

groupId はADのグループのオブジェクトIdを貼り付けること。

```yaml
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
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
```

# テストの実施

今回は、２つのユーザを使った。hacker2(dev) とhacker6(sre)。だ。今までは、hacker2を使っていたけど、hacker6を使ってログインし直す。方法は、クレデンシャルをアップデートする。

```bash
$ az aks get-credentials --resource-group $RESOURCE_GROUP --name $AKSNAME --overwrite-existing
```

すると、ログイン情報を聞かれるので、今回は、hacker6でログインする。

```
$ kubectl get pod --all-namespaces
To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code <<SOMEKEY>> to authenticate.
```

## 他リソースの閲覧の失敗

SREでログインしているので、sre ネームスペースのみが見えたり、操作できたりするはずである。 しっかり失敗する。

```
$ kubectl get pod --all-namespaces
No resources found.
Error from server (Forbidden): pods is forbidden: User "hacker609o@ABCDE.onmicrosoft.com" cannot list resource "pods" in API group "" at the cluster scope
```

## 閲覧可能なネームスペースの閲覧

しっかり閲覧できる。

```
$ kubectl get pod -n sre
NAME        READY     STATUS    RESTARTS   AGE
nginx-sre   1/1       Running   0          178m
```

## devリソースの閲覧

当然の事ながら閲覧できない。

```
$ kubectl get pod -n dev
No resources found.
Error from server (Forbidden): pods is forbidden: User "hacker609o@ABCDE.onmicrosoft.com" cannot list resource "pods" in API group "" in the namespace "dev"
```

# ハマったところ

最初に、設定の結果を、hacker2 でテストすると全てのリソースが見えてしまった。理由は、hacker2は、管理者ログインで使ったアカウントと同じだから、管理者扱いされた様子。だから、hacker6でログインし直すとしっかりRBACが有効であるのが確認できた。

# 終わりに

RoleとRoleBindings そして、前回のブログにあった通りに、クラスタを設定する事で、ADのグループと、RBACを連携させることができた。思ったよりずっと簡単だった。