ServiceAccount を作成して Pod から kubectl を使って Pod の情報を取得する

作業メモ。

[Kubernetes完全ガイド impress top gearシリーズ](https://www.amazon.co.jp/dp/B07HFS7TDT/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)を読みながら手元で確認した時のメモ。

Kubernetes の ServiceAccount を作成し、Pod から kubectl を実行した。

# 環境

```bash
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.3", GitCommit:"2bba0127d85d5a46ab4b778548be28623b32d0b0", GitTreeState:"clean", BuildDate:"2018-05-28T20:03:09Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"11+", GitVersion:"v1.11.5-eks-6bad6d", GitCommit:"6bad6d9c768dc0864dab48a11653aa53b5a47043", GitTreeState:"clean", BuildDate:"2018-12-06T23:13:14Z", GoVersion:"go1.10.3", Compiler:"gc", Platform:"linux/amd64"}
```

# ServiceAccount を作成して Pod から kubectl を使って Pod の情報を取得する

参考公式ドキュメント。

[Configure Service Accounts for Pods](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/)

## ServiceAccount の作成

ServiceAccount を作成する。
以下のマニュフェストを作成する。

```sample-serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: sample-serviceaccount
  namespace: default
```

```bash
# ServiceAccount の作成
$kubectl apply -f sample-serviceaccount.yaml
serviceaccount "sample-serviceaccount" created

# 最初から存在する default 以外に作成した serviceaccount が存在する
$kubectl get serviceaccount
NAME                    SECRETS   AGE
default                 1         16d
sample-serviceaccount   1         35s

```

ServiceAccount を作成すると Secret も作成されている。

```bash
# マニュフェストにはない secret が追加されている
$kubectl get serviceaccount sample-serviceaccount -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"sample-serviceaccount","namespace":"default"}}
  creationTimestamp: 2019-01-05T21:07:40Z
  name: sample-serviceaccount
  namespace: default
  resourceVersion: "2175514"
  selfLink: /api/v1/namespaces/default/serviceaccounts/sample-serviceaccount
  uid: efcc6da1-112d-11e9-b090-0ae6cc179478
secrets:
- name: sample-serviceaccount-token-2tml5

# serviceaccount 名を使って secret が追加されている
$kubectl get secret
NAME                                TYPE                                  DATA      AGE
default-token-9shpr                 kubernetes.io/service-account-token   3         16d
sample-serviceaccount-token-2tml5   kubernetes.io/service-account-token   3         1m
test-secret                         Opaque                                2         5d

# Secret を見る
$kubectl get secret sample-serviceaccount-token-2tml5 -o yaml
apiVersion: v1
data:
  ca.crt: ・・・
  namespace: ZGVmYXVsdA==
  token: ・・・
kind: Secret
metadata:
  annotations:
    kubernetes.io/service-account.name: sample-serviceaccount
    kubernetes.io/service-account.uid: efcc6da1-112d-11e9-b090-0ae6cc179478
  creationTimestamp: 2019-01-05T21:07:40Z
  name: sample-serviceaccount-token-2tml5
  namespace: default
  resourceVersion: "2175513"
  selfLink: /api/v1/namespaces/default/secrets/sample-serviceaccount-token-2tml5
  uid: efce8bb3-112d-11e9-b090-0ae6cc179478
type: kubernetes.io/service-account-token

```

Secret はトークンと証明書から構成されている。
Pod から Kubernetes API へリクエストする場合、上記を利用する。

## Pod を起動して Kubernetes API へリクエストする(NG)

Pod 作成時に ServiceAccount を指定する。
以下のように指定する事ができる。


```sample-serviceaccount-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: sample-kubectl
spec:
  serviceAccountName: sample-serviceaccount
  containers:
    - name: kubectl-container
      image: lachlanevenson/k8s-kubectl:v1.10.4
      command: ["sleep", "86400"]
```

なお、ServiceAccount を明示的に指定しない場合 `default` という ServiceAccount が設定される。

作成した ServiceAccount を指定して起動してみる。
起動すると ServiceAccount の Secret がマウントされている。

```bash
$ kubectl apply -f sample-kubectl.yaml
pod "sample-kubectl" created

# secret がマウントされている
$kubectl get pod sample-serviceaccount-pod -o yaml |less
(一部略)

    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: sample-serviceaccount-token-mcczl
      readOnly: true
    volumes:
  - name: sample-serviceaccount-token-mcczl
    secret:
      defaultMode: 420
      secretName: sample-serviceaccount-token-mcczl

# secret の情報が配置されている
$kubectl exec -it sample-kubectl -- ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt     namespace  token

```

Pod ではマウントした Secret を使って認証を行う。

この状態で `kubectl get pods` を実行してPod 情報を取得できるかやってみる。

```bash
$kubectl exec -it sample-kubectl -- kubectl get pods
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:sample-serviceaccount" cannot list pods in the namespace "default"
command terminated with exit code 1
```

この状態ではNG。
エラーメッセージに書いてあるように Pod 情報を取得する権限がないため。

## 権限を付与する

先程作成した ServiceAccount には Pod を取得する権限が無いために失敗した。

Kubernetes では RBAC(Role Based Access Control)を採用しており、どういった操作を出来るかということを記載した Role を作成し、ServiceAccount などの User に対して Role を紐付ける(RoleBinding)ことで権限管理を行っている。

[Using RBAC Authorization](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)

Role と RoleBiding には大きく分けて2種類ある。
基本的には設定範囲が違う。

+ namespace レベルの指定:Role と RoleBinding
+ cluster レベルの指定:ClusterRole と ClusterRoleBinding

違いとして例えば namspece レベルの Role を作成したとする。
この場合、別途新しい namespace が作成され、同じような権限の設定を行いたい時には新しい namespace の Role と Rolebinding が必要。
一方、CluserRole　を作成指定場合、cluster ワイドなので新しい ClusterRole の作成は不要。
また、 Kuberntes システムコンポーネントでも ClusterRole を使っておりいくつかのプリセットも存在する。
その為、ざっくりした権限付与で良い場合、ClusterRole のプリセットを使う方法も有効。

確認してみる。

```bash
# clusterrole 一覧を確認.多くの情報が表示される.system と表示されているのが system で使っているもの
$kubectl get clusterrole
NAME                                                                   AGE
admin                                                                  16d
aws-node                                                               16d
cluster-admin                                                          16d
edit                                                                   16d
eks:node-bootstrapper                                                  16d
system:aggregate-to-admin                                              16d
system:aggregate-to-edit                                               16d
system:aggregate-to-view                                               16d
system:auth-delegator                                                  16d
system:aws-cloud-provider                                              16d
system:basic-user                                                      16d
system:certificates.k8s.io:certificatesigningrequests:nodeclient       16d
(略)

# view という clustrrole の詳細を確認。pod については get/list/watch が可能
$ kubectl describe clusterrole view

Name:         view
Labels:       kubernetes.io/bootstrapping=rbac-defaults
Annotations:  rbac.authorization.kubernetes.io/autoupdate=true
PolicyRule:
  Resources                                Non-Resource URLs  Resource Names  Verbs
  ---------                                -----------------  --------------  -----
(略)
  pods                                     []                 []              [get list watch]
  pods/log                                 []                 []              [get list watch]
  pods/status                              []                 []              [get list watch]
  horizontalpodautoscalers.autoscaling     []                 []              [get list watch]
  poddisruptionbudgets.policy              []                 []              [get list watch]

```


上記 clusterrole を 作成した serviceaccount に付与するために **ClusterRoleBinding** を作成する。

```sample-clusterrolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: sample-clusterrolebinding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: view
subjects:
- kind: ServiceAccount
  name: sample-serviceaccount
  namespace: default
```

```bash
# ClusterRoleBinding を作成
$kubectl apply -f sample-clusterrolebinding.yaml
clusterrolebinding.rbac.authorization.k8s.io "sample-clusterrolebinding" created

# ServiceAccont の sample-serviceaccount と紐付いている
$kubectl describe clusterrolebinding sample-clusterrolebinding
Name:         sample-clusterrolebinding
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRoleBinding","metadata":{"annotations":{},"name":"sample-clusterrolebinding","namespace":""...
Role:
  Kind:  ClusterRole
  Name:  view
Subjects:
  Kind            Name                   Namespace
  ----            ----                   ---------
  ServiceAccount  sample-serviceaccount  default
```

## Pod から Kubernetes API へリクエストする(OK)

権限を付与したので先程の Pod から `kubectl get pods`を実行する。

```bash
$ kubectl exec -it sample-kubectl -- kubectl get pods
NAME                                 READY     STATUS      RESTARTS   AGE
1-sample-pod                         1/1       Running     0          11d
2-sample-pod                         1/1       Running     0          11d
dapi-envars-fieldref                 1/1       Running     0          5d
```

Pod の再起動はしていないが成功。
トークンの認証は以前から成功しており、権限を付与したことで認可ができたので成功した状況と思われる。