Kubernetes API Server の 認可 (Authorization Plugin) によるアクセス制御

# はじめに

Authorization Plugin を利用することで、ユーザごとにアクセス可能なリソースを制御することができます。

Authorization Plugin を使うには、誰がリソースにアクセスしているのかを識別する必要があります。
Basic認証やトークン、クライアント証明書などを用いて認証 (Authentication) を行った上で、認証されたユーザや ServiceAccount に対する認可 (Authorization) を行います。

認証・認可が行われるのは secure port に対するアクセスのみで、insecure port に対するアクセスはチェックされません。

認可についての本家ドキュメントは [こちら](http://kubernetes.io/docs/admin/authorization/)
認証に関しては [こちら](http://kubernetes.io/docs/admin/authentication/)

テスト環境の Kubernetes クラスタは [こちらのように kubespray で作った](http://qiita.com/albatross/items/7f472a973d0c04041a4d)想定です。
Kubernetes `1.3.0` には RBAC 関連で [正しく動作しないバグ](https://github.com/kubernetes/kubernetes/pull/29071) があるので `1.3.4` 以降を使用しています。

# API Server の設定

`ABAC (Attribute-Based Access Control)` と `RBAC (Roles-Based Access Control)` を試します。
API Server の起動時の引数を変更して `ABAC` と `RBAC` 両方を有効にします。この場合はどちらかの Authorization Plugin で認可が下りればアクセスできるようになります。

`kubespary` で構成した例では kubelet から API Server が起動されるので、manifest を修正します。
API Server を動作させる 2台両方で設定します。

```console:node1,node2で実施
## manifests ディレクトリは watch されているので、先に準備を整える
# vi /etc/kubernetes/abac-policy.jsonl    ## 内容は後述
# chmod 600 /etc/kubernetes/abac-policy.jsonl

# cd /etc/kubernetes/manifests
# cp -ip kube-apiserver.manifest /tmp
# vi kube-apiserver.manifest    ## 変更点を後述
```

```json:/etc/kubernetes/abac-policy.jsonl
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "kube-node", "namespace": "*", "resource": "*", "apiGroup": "*"}}
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "*", "readonly": true, "nonResourcePath": "*"}}

{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "root", "namespace": "*", "resource": "*", "apiGroup": "*"}}
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "kube", "namespace": "test", "resource": "services", "apiGroup": "*", "readonly": true}}
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "kube", "namespace": "test", "resource": "pods", "apiGroup": "*"}}

{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "system:serviceaccount:test:default", "namespace": "test", "resource": "*", "apiGroup": "*", "readonly": true}}
```

```diff:kube-apiserver.manifestの修正点
--- /tmp/kube-apiserver.manifest        2016-08-12 00:35:27.311905955 +0900
+++ /etc/kubernetes/manifests/kube-apiserver.manifest   2016-08-12 00:50:44.237718445 +0900
@@ -23,6 +23,9 @@
     - --tls-private-key-file=/etc/kubernetes/ssl/apiserver-key.pem
     - --token-auth-file=/etc/kubernetes/tokens/known_tokens.csv
     - --service-account-key-file=/etc/kubernetes/ssl/apiserver-key.pem
+    - --authorization-mode=ABAC,RBAC
+    - --authorization-policy-file=/etc/kubernetes/abac-policy.jsonl
+    - --authorization-rbac-super-user=root
     - --secure-port=443
     - --insecure-port=8080
     - --v=2
```

`kube-apiserver.manifest` を変更・保存すると自動的に kube-apiserver が再起動されます。
引数が変わっていることを確認しておきます。

```console
# ps -eo args | grep hyperkube\ apiserve[r] | sed "s/ /\n/g"
/hyperkube
apiserver
--advertise-address=192.168.100.1
--etcd-servers=http://127.0.0.1:2379
--insecure-bind-address=127.0.0.1
--apiserver-count=2
--admission-control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
--service-cluster-ip-range=10.233.0.0/18
--client-ca-file=/etc/kubernetes/ssl/ca.pem
--basic-auth-file=/etc/kubernetes/users/known_users.csv
--tls-cert-file=/etc/kubernetes/ssl/apiserver.pem
--tls-private-key-file=/etc/kubernetes/ssl/apiserver-key.pem
--token-auth-file=/etc/kubernetes/tokens/known_tokens.csv
--service-account-key-file=/etc/kubernetes/ssl/apiserver-key.pem
--authorization-mode=ABAC,RBAC
--authorization-policy-file=/etc/kubernetes/abac-policy.jsonl
--authorization-rbac-super-user=root
--secure-port=443
--insecure-port=8080
--v=2
--allow-privileged=true
2>&1
>>
/var/log/kubernetes/kube-apiserver.log
```

ついでにテスト用の Namespace を作っておきます。

```console
# kubectl create ns test
namespace "test" created
# kubectl get ns
NAME          STATUS    AGE
default       Active    12m
kube-system   Active    12m
test          Active    2s
```

# kubedns の設定

API Server で認可が有効になったため、このままでは `kubedns` から API Server にアクセスできません。
`kubedns` は API Server にリクエストして得られた情報をもとに DNS レコードを更新するので、API Server にアクセスできるようにしておく必要があります。

この環境では `Replication Controller` としてデプロイされているので、その設定を書き替えます。変更が必要なのは `kube2sky` の箇所だけです。
やり方は色々ありますが、ここでは `ConfigMap` で kubeconfig.yaml を用意し、ホストの `/etc/kubernetes/ssl` にある証明書と秘密鍵を `HostPath` でコンテナに見せて TLS クライアント認証をします。
(証明書・秘密鍵は全てのホストが持っている構成です)

```console:ConfigMap作成
# vi kubeconfig.yaml
# kubectl --namespace=kube-system create configmap kubeconfig --from-file=kubeconfig.yaml
configmap "kubeconfig" created
# kubectl --namespace=kube-system get configmap
NAME         DATA      AGE
kubeconfig   1         7s
```

```yaml:kubeconfig.yaml
apiVersion: v1
kind: Config
clusters:
- name: local
  cluster:
    certificate-authority: /etc/kubernetes/ssl/ca.pem
    server: https://10.233.0.1/
users:
- name: kubedns
  user:
    client-certificate: /etc/kubernetes/ssl/node.pem
    client-key: /etc/kubernetes/ssl/node-key.pem
contexts:
- context:
    cluster: local
    user: kubedns
  name: kubedns-cluster.local
current-context: kubedns-cluster.local
```

API Server の指定は ClusterIP で行いました。

```
# kubectl get svc
NAME         CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   10.233.0.1   <none>        443/TCP   15m
```

ConfigMap を作ったら、続いて現在の設定をベースに Replication Controller の修正を行います。

```console:ReplicationController修正
# kubectl --namespace=kube-system get rc kubedns -o yaml > kubedns.yaml
# vi kubedns.yaml
# diff -u <(kubectl --namespace=kube-system get rc kubedns -o yaml) kubedns.yaml
```

```diff:
--- /dev/fd/63  2016-08-12 00:56:59.701544080 +0900
+++ kubedns.yaml        2016-08-12 00:56:45.148329156 +0900
@@ -52,6 +52,7 @@
           name: etcd-storage
       - args:
         - --domain=cluster.local
+        - --kubecfg-file=/etc/kubernetes/kubeconfig.yaml
         image: gcr.io/google_containers/kube2sky:1.14
         imagePullPolicy: IfNotPresent
         name: kube2sky
@@ -60,6 +61,12 @@
             cpu: 100m
             memory: 50Mi
         terminationMessagePath: /dev/termination-log
+        volumeMounts:
+        - mountPath: /etc/kubernetes
+          name: kubeconfig
+        - mountPath: /etc/kubernetes/ssl
+          name: etc-kube-ssl
+          readOnly: true
       - args:
         - -machines=http://localhost:4001
         - -addr=0.0.0.0:53
@@ -118,6 +125,12 @@
       volumes:
       - emptyDir: {}
         name: etcd-storage
+      - configMap:
+          name: kubeconfig
+        name: kubeconfig
+      - hostPath:
+          path: /etc/kubernetes/ssl
+        name: etc-kube-ssl
 status:
   fullyLabeledReplicas: 1
   observedGeneration: 1
```

設定を作ったら Replication Controller を更新します。
Pod を作り直させるために、一度削除します。
削除してしばらく待つと自動的に Replication Controller が Pod を作り直します。

```console
# kubectl replace -f kubedns.yaml
replicationcontroller "kubedns" replaced

# kubectl --namespace=kube-system get pod -l k8s-app=kubedns
NAME            READY     STATUS    RESTARTS   AGE
kubedns-gy54j   4/4       Running   0          17m

# kubectl --namespace=kube-system delete pod kubedns-gy54j
pod "kubedns-gy54j" deleted

# kubectl --namespace=kube-system get pod -l k8s-app=kubedns
NAME            READY     STATUS    RESTARTS   AGE
kubedns-ys89d   4/4       Running   0          44s
```

Pod が動いたら適当な Service を作って名前が引けるか確認しておきます。
名前登録されることを確認したいだけなので、Endpoint は存在しなくて問題ありません。

```console
# kubectl expose svc kubernetes --name=hoge --port=1111 --selector="foo=bar"
service "hoge" exposed
# kubectl get svc
NAME         CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
hoge         10.233.62.169   <none>        1111/TCP   4s
kubernetes   10.233.0.1      <none>        443/TCP    21m

# dig +short hoge.default.svc.cluster.local.
10.233.62.169
```

無事に名前が引けたら余計な Service は削除しておきます。

```console
# kubectl delete svc hoge
service "hoge" deleted
```


# ABAC 

## ABAC policy 設定の説明

どのようにアクセス可否が設定されたかを確認する前に、[API Server の設定](#api-server-の設定) で記述した `abac-policy.jsonl` の設定について説明します。


以下は kube-node ユーザに全ての Namespace 及びリソースへのアクセスを許可するものです。

```json
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "kube-node", "namespace": "*", "resource": "*", "apiGroup": "*"}}
```

この環境で kubelet の認証設定は `/etc/kubernetes/node-kubeconfig.yaml` を参照するようになっています。

```yaml
apiVersion: v1
kind: Config
clusters:
- name: local
  cluster:
    certificate-authority: /etc/kubernetes/ssl/ca.pem
users:
- name: kubelet
  user:
    client-certificate: /etc/kubernetes/ssl/node.pem
    client-key: /etc/kubernetes/ssl/node-key.pem
contexts:
- context:
    cluster: local
    user: kubelet
  name: kubelet-cluster.local
current-context: kubelet-cluster.local
```

この証明書の CN が kube-node です。これにより kubelet は API Server へアクセスできます。

```console
# openssl x509 -in /etc/kubernetes/ssl/node.pem -subject -noout
subject= /CN=kube-node
```

kube-proxy も同じ `node-kubeconfig.yaml` を参照しています。
(Master として `kubespray` で構成したホストは localhost の insecure port を参照しますが、Master の機能を持たないよう `kubespray` で構成したホストは Master にしたホストの secure port を参照します。)
kubedns も設定は若干異なりますが、同じ証明書を使ってクライアント認証しています。

以下は nonResourcePath へのアクセスを全てのユーザに許可するものです。
kubectl を叩く際にバージョン情報のリクエストなどに用いられます。

```json
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "*", "readonly": true, "nonResourcePath": "*"}}
```

以下は root ユーザに全ての Namespace 及びリソースへのアクセスを許可するものです。
この環境では後述する RBAC のスーパーユーザとしても root ユーザを使用します。

```json
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "root", "namespace": "*", "resource": "*", "apiGroup": "*"}}
```

以下は kube ユーザに限定的なアクセスを許可するものです。
test Namespace において Service の読み取りと Pod の読み書きを可能にしています。

```json
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "kube", "namespace": "test", "resource": "services", "apiGroup": "*", "readonly": true}}
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "kube", "namespace": "test", "resource": "pods", "apiGroup": "*"}}
```

以下は test Namespace の default という ServiceAccount に test Namespace の全てのリソースへ読み取りアクセスを許可するものです。
ServiceAccount の設定を書く場合は `system:serviceaccount:<namespace>:<serviceaccountname>` という形式のユーザ名にします。

```json
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "system:serviceaccount:test:default", "namespace": "test", "resource": "*", "apiGroup": "*", "readonly": true}}
```

default という ServiceAccount は test Namespace を作成した際に自動的に作られたものです。

```console
# kubectl --namespace=test get sa
NAME      SECRETS   AGE
default   1         15m
# kubectl --namespace=test get secret
NAME                  TYPE                                  DATA      AGE
default-token-a9aii   kubernetes.io/service-account-token   3         15m
```

## ABAC 動作テスト

設定した通りに制御されるか確認します。
kubectl の認証設定をします。

ここで注意が必要なのは、insecure port へのアクセスは認証・認可が必要ない (常にアクセス許可される) ということです。
Authorization Plugin の動作テストをするには、kubectl でのアクセスを secure port の方に向ける必要があります。
(kubectl のデフォルトは http://locahost:8080/ 宛てになっています)

```console
## 認証ありにしたい場合は secure port に向ける
# kubectl config set-cluster kubespray-secure --server=https://kubernetes.default.svc.cluster.local/ --certificate-authority=/root/.kube/ca.pem
# cp /etc/kubernetes/ssl/ca.pem /root/.kube/

## root ユーザに関する設定
# kubectl config set-credentials root --username=root --password=changeme
# kubectl config set-context kubespray-root --cluster=kubespray-secure --user=root

## kube ユーザに関する設定
# kubectl config set-credentials kube --username=kube --password=changeme
# kubectl config set-context kubespray-kube --cluster=kubespray-secure --user=kube

## insecure port にアクセスするための設定
# kubectl config set-cluster kubespray-insecure --server=http://localhost:8080/
# kubectl config set-context insecure --cluster=kubespray-insecure --user=""

## test Namespace の default ServiceAccount に関する設定
# yum install -y jq
# kubectl config set-credentials test-default-sa \
--token=$(kubectl --namespace=test get secret \
 $(kubectl --namespace=test get sa default -o json | \
jq -r .secrets[0].name) -o json | jq -r .data.token | base64 -d)
# kubectl config set-context kubespray-test-default-sa --cluster=kubespray-secure --user=test-default-sa
```

4つの context を設定しました。これらは use-context によって切り替えが可能です。
設定ファイルは以下のようになりました。

```console:/root/.kube/configの確認
# kubectl config view
apiVersion: v1
clusters:
- cluster:
    server: http://localhost:8080/
  name: kubespray-insecure
- cluster:
    certificate-authority: ca.pem
    server: https://kubernetes.default.svc.cluster.local/
  name: kubespray-secure
contexts:
- context:
    cluster: kubespray-insecure
    user: ""
  name: insecure
- context:
    cluster: kubespray-secure
    user: kube
  name: kubespray-kube
- context:
    cluster: kubespray-secure
    user: root
  name: kubespray-root
- context:
    cluster: kubespray-secure
    user: test-default-sa
  name: kubespray-test-default-sa
current-context: ""
kind: Config
preferences: {}
users:
- name: kube
  user:
    password: changeme
    username: kube
- name: root
  user:
    password: changeme
    username: root
- name: test-default-sa
  user:
    token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJ0ZXN0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tYTlhaWkiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjgxNzY2MGJhLTVmZGItMTFlNi05NzJlLTAwMGMyOTJlZWMzNyIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDp0ZXN0OmRlZmF1bHQifQ.5N_TDpepXRL3yf_vslVoU0b76VcE2FqXToGbIBIZH-o5Fxqn-yuzXQ16JRdZaAKpFQHX1vG8xnHqRsrt3xrM228zuF6h6vZjephsLqPYNdy3dHhc62ZhhiI_RWzcb6uMYlAxT4YwfGWL-DikSuZtH5YJiASreo17Uf4Xy2_6WeRPzweLG4GHDHvdmTV5Llz-nRqcIC6CaYvDpxoShTx4DdbG9PkH7txnhHGGxZj-7qC_vThaN0APrJrWTeRPyu2oFy78vioAbB5mPHo9XNZ7hjWKtzUwzy26wFDgzdCUyzRmRaGKnTe1DQLQRoR5iw5byfaaM8tsHGtbPWsk8Dw3mA
```

まずは root ユーザとしてアクセスしてみます。 

```console
# kubectl config use-context kubespray-root
# kubectl config current-context
kubespray-root

# kubectl get secret --all-namespaces
NAMESPACE     NAME                  TYPE                                  DATA      AGE
default       default-token-vxtr6   kubernetes.io/service-account-token   3         31m
kube-system   default-token-gh3tp   kubernetes.io/service-account-token   3         31m
test          default-token-a9aii   kubernetes.io/service-account-token   3         18m
```

全てを試すのは面倒なので secret だけですが、全ての Namespace にアクセスできました。同様に Service や Pod も問題なくアクセスできます。

kube ユーザの場合はどうでしょうか。切り替えてアクセスしてみます。

```console
# kubectl config use-context kubespray-kube
# kubectl config current-context
kubespray-kube

## kube-system Namespace にはアクセスできない
# kubectl --namespace=kube-system get pod
the server does not allow access to the requested resource (get pods)

## test Namespace ならリソースの種類によってはアクセスできる (オブジェクトは未作成なので無い)
# kubectl --namespace=test get pod
# kubectl --namespace=test get svc
# kubectl --namespace=test get secret
the server does not allow access to the requested resource (get secrets)

## test Namespace で Pod を作れるが Service は作れない
# kubectl --namespace=test run test -l foo=bar --image=nginx:alpine --restart=Never
pod "test" created
# kubectl --namespace=test get pod
NAME      READY     STATUS    RESTARTS   AGE
test      1/1       Running   0          11s
# kubectl --namespace=test expose pod test --port=80 --target-port=80
Error from server: the server does not allow access to the requested resource (post services)
```

test Namespace の default という ServiceAccount も試します。

```console
# kubectl config use-context kubespray-test-default-sa
# kubectl config current-context
kubespray-test-default-sa

## kube-system Namespace にはアクセスできない
# kubectl --namespace=kube-system get pod
the server does not allow access to the requested resource (get pods)

## test Namespace ならアクセスできるが、readonly で Pod を作れない
# kubectl --namespace=test get pod
NAME      READY     STATUS    RESTARTS   AGE
test      1/1       Running   0          1m
# kubectl --namespace=test get svc
# kubectl --namespace=test get secret
NAME                  TYPE                                  DATA      AGE
default-token-a9aii   kubernetes.io/service-account-token   3         20m
# kubectl --namespace=test run test2 -l foo=baz --image=nginx:alpine --restart=Never
Error from server: the server does not allow access to the requested resource (post pods)
```

insecure port を使って制限されないことを確認するついでに、作成した Pod を削除します。

```console
# kubectl config use-context insecure
# kubectl config current-context
insecure

# kubectl --namespace=test delete pod test
pod "test" deleted
```


# RBAC

RBAC では ABAC より細かい制御が可能です。
RBAC はまだアルファ版なので、本番運用するには注意が必要かもしれません。

## RBAC 設定作成

RBAC の設定には以下のものがあります。

- RBAC のスーパーユーザ
- 特定の Namespace にのみ効果が及ぶ `Role`, `RoleBinding`
- クラスタ全体に効果が及ぶ `ClusterRole`, `ClusterRoleBinding`

`Role`, `ClusterRole` で権限セットを定義し、`RoleBidnnig`, `ClusterRoleBinding` でどのユーザまたは ServiceAccount に適用するかを決定します。
`ClusterRole` を `RoleBinding` で使用する場合は効果が Namespace 内に限定されます。
クラスタ全体に効果が及ぶのは `ClusterRole` を `ClusterRoleBinding` で使用する場合のみです。

1種類だけ作って試します。

ABAC のような readonly フラグだけではなく、それぞれの操作を細かく設定できます。
以下の例は Deployment や Service の作成・更新を行うようなユーザを想定しています。`kube` ユーザと `test Namespace の default` ServiceAccount をまとめて同じ role に bind します。

```yaml:user-role.yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1alpha1
metadata:
  # "namespace" omitted since ClusterRoles are not namespaced.
  #namespace: default
  name: normal-user
rules:
  - apiGroups: [""] # The API group "" indicates the default API Group.
    resources: ["pods"]
    verbs: ["get", "list", "create", "update", "watch", "delete", "deletecollection"]
    nonResourceURLs: []

  - apiGroups: [""] # The API group "" indicates the default API Group.
    resources: ["endpoints", "events", "services"]
    verbs: ["get", "list", "create", "update", "delete", "watch" ]
    nonResourceURLs: [""]

  - apiGroups: ["extensions"] # The API group "" indicates the default API Group.
    resources: ["replicasets", "deployments"]
    verbs: ["get", "list", "create", "update", "watch", "delete", "deletecollection", "patch"]
    nonResourceURLs: []
```

```yaml:user-bind.yaml
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1alpha1
metadata:
  name: test-normal-users
  namespace: test
subjects:
  - kind: ServiceAccount
    name: default
    namespace: test
  - kind: User
    name: kube
roleRef:
  kind: ClusterRole
  name: normal-user
  apiVersion: rbac.authorization.k8s.io/v1alpha1
```

RBAC の設定を変更するには RBAC のスーパーユーザ (ここでは root) になる必要があるので context を切り替えてから設定を投入します。

```console:ClusterRole,RoleBinding作成・確認
# kubectl config use-context kubespray-root
# kubectl config current-context
kubespray-root

# kubectl create -f user-role.yaml
clusterrole "normal-user" created
# kubectl create -f user-bind.yaml
rolebinding "test-normal-users" created

# kubectl get clusterroles
NAME          AGE
normal-user   6s
# kubectl --namespace=test get rolebindings
NAME                AGE
test-normal-users   23s
```


## RBAC 動作テスト

作成したオブジェクトのテストをします。
まずは `kube` ユーザが `normal-user` ロールの権限を持つことを確認します。

```console
# kubectl config use-context kubespray-kube
# kubectl config current-context
kubespray-kube

## test Namespace で bind しているので kube-system Namespace にはアクセスできない
# kubectl --namespace=kube-system get pod
the server does not allow access to the requested resource (get pods)

## test Namespace なら Deployment, ReplicaSet, Pod が作れる
# kubectl --namespace=test run test -l foo=bar --image=nginx:1.10.1-alpine
deployment "test" created
# kubectl --namespace=test get deployment
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
test      1         1         1            0           8s
# kubectl --namespace=test get rs
NAME              DESIRED   CURRENT   AGE
test-2066847110   1         1         9s
# kubectl --namespace=test get pod -o wide
NAME                    READY     STATUS    RESTARTS   AGE       IP             NODE
test-2066847110-wk5ze   1/1       Running   0          28s       10.233.120.3   node3

## Service も作れる
# kubectl --namespace=test expose deployment/test --port=80 --target-port=80
service "test" exposed
# kubectl --namespace=test get svc
NAME      CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
test      10.233.32.165   <none>        80/TCP    6s
# kubectl --namespace=test get ep
NAME      ENDPOINTS         AGE
test      10.233.120.3:80   12s

## test Namespace でも serviceaccount や secret にはアクセスできない
# kubectl --namespace=test get sa
the server does not allow access to the requested resource (get serviceaccounts)
# kubectl --namespace=test get secret
the server does not allow access to the requested resource (get secrets)
```


続いて `test Namespace` の `default` ServiceAccount も確認します。

```console
# kubectl config use-context kubespray-test-default-sa
# kubectl config current-context
kubespray-test-default-sa

## test Namespace で bind しているので kube-system Namespace にはアクセスできない
# kubectl --namespace=kube-system get pod
the server does not allow access to the requested resource (get pods)

## test Namespace のリソースを読み取れる
# kubectl --namespace=test get deployment
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
test      1         1         1            1           1m
# kubectl --namespace=test get rs
NAME              DESIRED   CURRENT   AGE
test-2066847110   1         1         1m
# kubectl --namespace=test get pod -o wide
NAME                    READY     STATUS    RESTARTS   AGE       IP             NODE
test-2066847110-wk5ze   1/1       Running   0          1m        10.233.120.3   node3

## test Namespace なら Deployment, ReplicaSet, Pod の変更ができる
# kubectl --namespace=test scale --replicas=2 deployment/test
deployment "test" scaled
# kubectl --namespace=test get deployment
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
test      2         2         2            1           2m
# kubectl --namespace=test get rs
NAME              DESIRED   CURRENT   AGE
test-2066847110   2         2         2m
# kubectl --namespace=test get pod -o wide
NAME                    READY     STATUS    RESTARTS   AGE       IP             NODE
test-2066847110-bcenk   1/1       Running   0          21s       10.233.82.3    node1
test-2066847110-wk5ze   1/1       Running   0          3m        10.233.120.3   node3
# kubectl --namespace=test get svc
NAME      CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
test      10.233.32.165   <none>        80/TCP    2m
# kubectl --namespace=test get ep
NAME      ENDPOINTS                        AGE
test      10.233.120.3:80,10.233.82.3:80   2m

## test Namespace で serviceaccount や secret にアクセスできる
# kubectl --namespace=test get sa
NAME      SECRETS   AGE
default   1         28m
# kubectl --namespace=test get secret
NAME                  TYPE                                  DATA      AGE
default-token-a9aii   kubernetes.io/service-account-token   3         28m

## serviceaccount の作成はできない
# kubectl --namespace=test create sa hoge
Error from server: the server does not allow access to the requested resource (post serviceaccounts)
```

Secret へアクセス可能なことを不審に思った方もいるかもしれません。
`kube` ユーザが `ServiceAccount`, `Secret` にアクセスできないのに、同じ `ClusterRole` である `test Namespace の default` ServiceAccount は何故アクセスできてしまうのでしょうか？

[API Server の設定](#api-server-の設定) でも触れたように、`ABAC` 及び `RBAC` が両方設定されている場合には、どちらか一方で認可されればアクセス可能になります。

`test Namespace の default` ServiceAccount は `ABAC` のポリシーファイルに以下のように設定されています。これにより test Namespace の全てのリソースへの readonly なアクセスは許されているのです。

```json
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "system:serviceaccount:test:default", "namespace": "test", "resource": "*", "apiGroup": "*", "readonly": true}}
```

最後に role を削除してもう一度確認してみます。
`ABAC` の設定で許されている操作のみが行えるはずです。

```console:ClusteRole,RoleBindingの削除
# kubectl config use-context kubespray-root
# kubectl config current-context
kubespray-root

# kubectl delete -f user-role.yaml
clusterrole "normal-user" deleted
# kubectl delete -f user-bind.yaml
rolebinding "test-normal-users" deleted

## 何も表示されないことを確認
# kubectl get clusterroles
# kubectl --namespace=test get rolebindings
```

```console:アクセスチェック
## kube ユーザの確認
# kubectl config use-context kubespray-kube
# kubectl config current-context
kubespray-kube
# kubectl --namespace=test get pod -o wide
NAME                    READY     STATUS    RESTARTS   AGE       IP             NODE
test-2066847110-bcenk   1/1       Running   0          2m        10.233.82.3    node1
test-2066847110-wk5ze   1/1       Running   0          5m        10.233.120.3   node3
# kubectl --namespace=test get deployment
the server does not allow access to the requested resource (get deployments.extensions)
# kubectl --namespace=test get rs
the server does not allow access to the requested resource (get replicasets.extensions)

## default ServiceAccount の確認
# kubectl config use-context kubespray-test-default-sa
# kubectl config current-context
kubespray-test-default-sa
# kubectl --namespace=test get pod -o wide
NAME                    READY     STATUS    RESTARTS   AGE       IP             NODE
test-2066847110-bcenk   1/1       Running   0          3m        10.233.82.3    node1
test-2066847110-wk5ze   1/1       Running   0          5m        10.233.120.3   node3
# kubectl --namespace=test get deployment
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
test      2         2         2            2           6m
# kubectl --namespace=test get secret
NAME                  TYPE                                  DATA      AGE
default-token-a9aii   kubernetes.io/service-account-token   3         31m
# kubectl --namespace=test run hoge -l foo=baz --image=nginx:alpine --restart=Never
Error from server: the server does not allow access to the requested resource (post pods)
```