secretsとserviceaccountを理解する

# 目的
- secretsとserviceaccountを理解する

# 手段
killercodaで手を動かす

### 環境
killercoda

### secrets とは
secrets は k8s 上でパスワード・トークン・キーを保存するオブジェクトです。パスワード等を secrets に格納して、pod 等から参照して利用します。利用方法は pod の環境変数として代入する、または、pod 内の特定パスに対して volume マウントして利用します。

###### secret 作成
まず、secret を作成します。

```bash
controlplane:~$ k create secret generic holy --from-literal creditcard=1111222233334444 --dry-run=client -oyaml
apiVersion: v1
data:
  creditcard: MTExMTIyMjIzMzMzNDQ0NA==
kind: Secret
metadata:
  creationTimestamp: null
  name: holy
# 作成前に yaml フォーマットで確認します

controlplane:~$ k get secrets holy 
NAME   TYPE     DATA   AGE
holy   Opaque   1      8s
# 作成したオブジェクトを確認します

controlplane:~$ k get secrets holy -oyaml
apiVersion: v1
data:
  creditcard: MTExMTIyMjIzMzMzNDQ0NA==
kind: Secret
metadata:
  creationTimestamp: "2025-04-15T02:19:31Z"
  name: holy
  namespace: default
  resourceVersion: "6739"
  uid: 05b43f90-68c6-4454-926f-513a643bb671
# 作成した値は base64 でエンコード後の値で保持されます
# つまり、このオブジェクトを参照可能なユーザは チルダcreditcardチルダ の値をデコードして参照可能です
```

secrets 作成方法は literal と呼ばれる直接値を指定する方法やファイルから作成可能です。literal の作成方法は前述の作成方法です。次に、ファイルから secrets を作成します。

```bash
controlplane:~$ cat /opt/ks/secret-diver.yaml 
apiVersion: v1
data:
  hosts: MTI3LjAuMC4xCWxvY2FsaG9zdAoxMjcuMC4xLjEJaG9zdDAxCgojIFRoZSBmb2xsb3dpbmcgbGluZXMgYXJlIGRlc2lyYWJsZSBmb3IgSVB2NiBjYXBhYmxlIGhvc3RzCjo6MSAgICAgbG9jYWxob3N0IGlwNi1sb2NhbGhvc3QgaXA2LWxvb3BiYWNrCmZmMDI6OjEgaXA2LWFsbG5vZGVzCmZmMDI6OjIgaXA2LWFsbHJvdXRlcnMKMTI3LjAuMC4xIGhvc3QwMQoxMjcuMC4wLjEgaG9zdDAxCjEyNy4wLjAuMSBob3N0MDEKMTI3LjAuMC4xIGNvbnRyb2xwbGFuZQoxNzIuMTcuMC4zNSBub2RlMDEKMTcyLjE3LjAuMjMgY29udHJvbHBsYW5lCg== # hosts は key となり、エンコードされた文字列は value となります
kind: Secret 
metadata:
  name: diver # secret のオブジェクト名
# secret 作成元のファイルを確認します

controlplane:~$ k create -f /opt/ks/secret-diver.yaml 
secret/diver created
# creat コマンドで secret オブジェクトを作成します

controlplane:~$ k get secrets diver 
NAME    TYPE     DATA   AGE
diver   Opaque   1      11s
# get で参照します

controlplane:~$ k get secrets diver -o yaml
apiVersion: v1
data:
  hosts: MTI3LjAuMC4xCWxvY2FsaG9zdAoxMjcuMC4xLjEJaG9zdDAxCgojIFRoZSBmb2xsb3dpbmcgbGluZXMgYXJlIGRlc2lyYWJsZSBmb3IgSVB2NiBjYXBhYmxlIGhvc3RzCjo6MSAgICAgbG9jYWxob3N0IGlwNi1sb2NhbGhvc3QgaXA2LWxvb3BiYWNrCmZmMDI6OjEgaXA2LWFsbG5vZGVzCmZmMDI6OjIgaXA2LWFsbHJvdXRlcnMKMTI3LjAuMC4xIGhvc3QwMQoxMjcuMC4wLjEgaG9zdDAxCjEyNy4wLjAuMSBob3N0MDEKMTI3LjAuMC4xIGNvbnRyb2xwbGFuZQoxNzIuMTcuMC4zNSBub2RlMDEKMTcyLjE3LjAuMjMgY29udHJvbHBsYW5lCg==
kind: Secret
metadata:
  creationTimestamp: "2025-04-15T02:25:18Z"
  name: diver
  namespace: default
  resourceVersion: "7189"
  uid: 3418e296-7ea8-4390-8f81-c60c8feba0c5
type: Opaque
# -oyaml で secret オブジェクトの中身を確認します
```


##### secret を pod にマウント
ここからは作成した secret を pod で利用する方法を確認します。まずは、volume として pod 内の特定パスにマウントします。

```bash
controlplane:~$ k run pod1 --image=nginx --dry-run=client -oyaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: pod1
  name: pod1
spec:
  containers:
  - image: nginx
    name: pod1
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}
# ベースのyamlを作成する
```

```bash
controlplane:~$ cat pod1.yaml 
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: pod1
  name: pod1
spec:
  volumes:
    - name: diver
      secret:
        secretName: diver # 事前に作成した secret を指定する
  containers:
  - image: nginx
    name: pod1
    volumeMounts:
      - name: diver
        readOnly: true # readOnlyであれば指定する
        mountPath: "/etc/diver/" # secret をマウントするパスを指定する
    resources: {}                # マウントするhostsはテキストファイルなので上位のディレクトリまでを指定する
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}
```

次に正しくマウントできているかを確認する。

```bash
controlplane:~$ k exec pod1 -- cat /etc/diver/hosts
127.0.0.1       localhost
127.0.1.1       host01

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
127.0.0.1 host01
127.0.0.1 host01
127.0.0.1 host01
127.0.0.1 controlplane
172.17.0.35 node01
172.17.0.23 controlplane
# マウントしたファイルを確認できます
```

続いて、secret を環境変数として代入します。

```bash
controlplane:~$ cat pod1.yaml 
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: pod1
  name: pod1
spec:
  volumes:
    - name: diver
      secret:
        secretName: diver # 事前に作成した secret を指定する
  containers:
  - image: nginx
    name: pod1
    volumeMounts:
      - name: diver
        readOnly: true # readOnlyであれば指定する
        mountPath: "/etc/diver/" # secret をマウントするパスを指定する
    env:
    - name: HOLY # 環境変数名を指定
      valueFrom:
        secretKeyRef:
          name: holy # 参照先の secret 名を指定
          key: creditcard # secret 内の key を指定
    resources: {}                # マウントするhostsはテキストファイルなので上位のディレクトリまでを指定する
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}
# yaml 内容を確認します

controlplane:~$ k exec pod1 -- env |grep HOLY
HOLY=1111222233334444
# 正しく環境変数に設定されているか確認します

controlplane:~$ k get secrets holy -oyaml
apiVersion: v1
data:
  creditcard: MTExMTIyMjIzMzMzNDQ0NA==
kind: Secret
metadata:
  creationTimestamp: "2025-04-15T10:56:40Z"
  name: holy
  namespace: default
  resourceVersion: "2475"
  uid: b88654b7-b22e-403c-9740-5b4eec44c641
type: Opaque
# secret 内容を確認します
```


#### secret をデコード
デコードした secret の中身を確認します。

```bash
kubectl -n one get secret s1 -ojsonpath="{.data.data}" | base64 -d
kubectl -n one get secret s2 -ojsonpath="{.data.data}" | base64 -d
# スマートに jsonpath 指定で取得できます

controlplane:~$ k get -n one secrets s1 -oyaml |grep "data: " |awk '{ print $2 }' | base64 --decode >> /opt/ks/one
controlplane:~$ k get -n one secrets s2 -oyaml |grep "data: " |awk '{ print $2 }' | base64 --decode >> /opt/ks/one
# 実際に手を動かすとこんな感じです...
```


### serviceaccount とは
サービスアカウントは人間以外のアカウントの一種で、KubernetesではKubernetesクラスタ内で個別のIDを提供しますAPIサーバーへの認証などのさまざまな場面で役立ちます。

##### serviceaccount 作成
create コマンドで作成します。

```bash
controlplane:~$ k create serviceaccount secret-manager
serviceaccount/secret-manager created
# 作成コマンドです

controlplane:~$ k get serviceaccounts -oyaml
apiVersion: v1
items:
- apiVersion: v1
  kind: ServiceAccount
  metadata:
    creationTimestamp: "2025-03-22T19:51:55Z"
    name: default # デフォルトの Serviceaccount です
    namespace: default
    resourceVersion: "426"
    uid: 72c16a73-bb3a-4ddf-b484-8cd235d8a21a
- apiVersion: v1
  kind: ServiceAccount
  metadata:
    creationTimestamp: "2025-04-15T11:15:56Z"
    name: secret-manager # 作成した ServiceAccount です
    namespace: default
    resourceVersion: "2664"
    uid: 6466b9af-d85e-48dd-a914-13a7db94c724
kind: List
metadata:
  resourceVersion: ""
```

次に pod に作成した serviceaccount を設定します。

```bash
controlplane:~$ cat secret-manager.yaml 
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: secret-manager
  name: secret-manager
  namespace: ns-secure
spec:
  serviceAccountName: secret-manager # 作成した serviceaccount を指定します
  volumes:
    - name: secret-volume
      secret:
        secretName: sec-a2
  containers:
  - image: httpd:alpine
    name: secret-manager
    resources: {}
    volumeMounts:
      - name: secret-volume
        readOnly: true
        mountPath: "/etc/sec-a2"
    env:
    - name: SEC_A1
      valueFrom:
        secretKeyRef:
          name: sec-a1
          key: aaa
  dnsPolicy: ClusterFirst
  restartPolicy: Always

controlplane:~$ k apply -f secret-manager.yaml 
pod/secret-manager created
```

##### serviceaccounttoken の自動マウントを制御
次に serviceaccouttoken の自動マウント動作を確認します。

```bash
controlplane:~$ cat /opt/ks/pod-one.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-one
  namespace: one
spec:
  serviceAccountName: custom
  automountServiceAccountToken: false # serviceaccouttoken の自動マウント有無を指定します
  containers:
  - name: webserver
    image: nginx:1.19.6-alpine
    ports:
    - containerPort: 80

controlplane:~$ k apply -f /opt/ks/pod-one.yaml 
pod/pod-one created
# yaml を適用します
```

##### serviceaccounttoken のマウント状態を確認
serviceaccouttoken のマウント状態を確認します。

```bash
controlplane:~$ k exec -n one pod-one -- mount | grep serviceaccout
controlplane:~$ k exec -n one pod-one -- mount | cat /var/run/secrets/kubernetes.io/serviceaccount/token
cat: /var/run/secrets/kubernetes.io/serviceaccount/token: No such file or directory
# マウントを無効化しているため、マウントされていません
```

次に automoutServiceAccoutToken を true にして pod を作成します。

```bash
controlplane:~$ cat /opt/ks/pod-one.yaml |grep auto 
  automountServiceAccountToken: true

controlplane:~$ k replace -f /opt/ks/pod-one.yaml --force
pod "pod-one" deleted
pod/pod-one replaced
```

マウント状況を確認します

```bash
controlplane:~$ k exec -n one pod-one -- mount | grep serviceaccount
tmpfs on /run/secrets/kubernetes.io/serviceaccount type tmpfs (ro,relatime,size=1912956k,inode64,noswap)

controlplane:~$ kubectl -n one exec -it pod-one -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6InRaVDhLaEdBeGVWTHRXTkJJRUNDT1BGRmxkYmNzOFVCd2doYWpuSlNrODgifQ.--snip---
controlplane:~$ 
# マウントされていることを確認できます。
```

# あとがき
アウトプットは良いですね！

# ソース
- [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/)
- [Distribute Credentials Securely Using Secrets](https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/)
- [Service Accounts](https://kubernetes.io/docs/concepts/security/service-accounts/)
- []()