kakakakakku blog

Weekly Tech Blog : Keep on Learning 👍

Secret の自動マウントをオプトアウトするかどうか : automountServiceAccountToken フィールド

Kubernetes のセキュリティ対策として,Service AccountPod に設定できる automountServiceAccountToken フィールド(Secret の自動マウントをオプトアウトするかどうか)の動作確認をした.とは言え,Service AccountSecret の関係性なども整理する必要があるため,一歩一歩進めていく.今回は Kubernetes v1.20 を検証環境とした.

kubernetes.io

Pod と Service Account

まず,Service Account を指定せずに Pod を作ると default Service Account と紐付く.確認をするために nginx:1.21 イメージを指定した以下のマニフェストを検証用に適用する.

apiVersion: v1
kind: Pod
metadata:
  name: my-nginx
spec:
  containers:
    - name: my-nginx
      image: nginx:1.21

以下に kubectl get pods my-nginx -o yaml コマンドの結果を抜粋して載せる.serviceAccountName: default になっている通り,デフォルトでは Poddefault Service Account が紐付いている.

apiVersion: v1
kind: Pod
metadata:
  name: my-nginx
  namespace: default
spec:
  containers:
  - image: nginx:1.21
    name: my-nginx
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: default-token-5qnst
      readOnly: true
  serviceAccountName: default
  volumes:
  - name: default-token-5qnst
    secret:
      defaultMode: 420
      secretName: default-token-5qnst

Service Account と Secret

もう1点重要なのは,上記 YAML の volumesvolumeMounts で,default-token-5qnst SecretPod のボリュームとしてファイルシステムにマウントしている.この default-token-5qnst Secretdefault Service Account に紐付いている.以下に kubectl get serviceaccounts default -o yaml コマンドの結果を抜粋して載せる.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: default
  namespace: default
secrets:
- name: default-token-5qnst

そして kubectl get secrets default-token-5qnst -o yaml コマンドで default-token-5qnst Secret の値を確認すると,Base64 でエンコードされた token を確認できる(以下は xxxxxxxxxx としてマスキングをした).

apiVersion: v1
data:
  token: xxxxxxxxxx
kind: Secret
metadata:
  name: default-token-5qnst
  namespace: default
type: kubernetes.io/service-account-token

Kubernetes API にリクエストを送る

実際に kubectl exec コマンドを使って Pod に接続をすると,ファイルシステムにマウントされた /var/run/secrets/kubernetes.io/serviceaccount/tokenファイルを確認できる.token ファイルは Base64 でデコードされた値となる(以下は yyyyyyyyyy としてマスキングをした).この token を HTTP Header に乗せると Kubernetes API にリクエストを送れるようになる.今回 default Service Account には権限を付与していないため Forbidden になっているのは期待通りと言える.

$ kubectl exec -it my-nginx -- /bin/sh

# cat /var/run/secrets/kubernetes.io/serviceaccount/token
yyyyyyyyyy

# TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)

# curl -s -k https://kubernetes/api/v1/namespaces/default/ --header "Authorization: Bearer ${TOKEN}"
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {
  },
  "status": "Failure",
  "message": "namespaces \"default\" is forbidden: User \"system:serviceaccount:default:default\" cannot get resource \"namespaces\" in API group \"\" in the namespace \"default\"",
  "reason": "Forbidden",
  "details": {
    "name": "default",
    "kind": "namespaces"
  },
  "code": 403
}

Kubernetes API にリクエストを送る手順は以下のドキュメントを参考にした.

kubernetes.io

Pod : automountServiceAccountToken フィールド

一般的には Pod から Kubernetes API にリクエストを送る場面は少なく,不要なら automountServiceAccountToken フィールドを false にして Secret の自動マウントをオプトアウトする.例えば EKS Best Practices Guides にもベストプラクティスとして載っている.

aws.github.io

まず,PodautomountServiceAccountToken: false を指定すると,Pod レベルで Secret の自動マウントをオプトアウトできる.以下のようにマニフェストに1行追加して,Pod を作り直す.

apiVersion: v1
kind: Pod
metadata:
  name: my-nginx
spec:
  containers:
    - name: my-nginx
      image: nginx:1.21
  automountServiceAccountToken: false

すると volumesvolumeMounts の設定はなくなり,Secret の自動マウントはされなくなった.

$ kubectl get pod my-nginx -o yaml
apiVersion: v1
kind: Pod
metadata:
  name: my-nginx
  namespace: default
spec:
  automountServiceAccountToken: false
  containers:
  - image: nginx:1.21
    name: my-nginx
  serviceAccountName: default

念のため /var/run/secrets/kubernetes.io/serviceaccount/token ファイルを確認したところ,存在しなかった.

$ kubectl exec -it my-nginx -- /bin/sh

# cat /var/run/secrets/kubernetes.io/serviceaccount/token
cat: /var/run/secrets/kubernetes.io/serviceaccount/token: No such file or directory

Service Account : automountServiceAccountToken フィールド

Pod レベルではなく,Service Account レベルで automountServiceAccountToken フィールドを false にすることもできる.以下のマニフェストで新しく auto-mount-off Service Account を作る.

$ cat serviceaccount.yaml 
apiVersion: v1
kind: ServiceAccount
metadata:
  name: auto-mount-off
automountServiceAccountToken: false

次に serviceAccountName: auto-mount-off を指定した Pod を作る.今回は Pod には automountServiceAccountToken: false を指定していない.

$ cat pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: my-nginx
spec:
  serviceAccountName: auto-mount-off
  containers:
    - name: my-nginx
      image: nginx:1.21

以下に kubectl get pods my-nginx -o yaml コマンドの結果を抜粋して載せる.同じく volumesvolumeMounts の設定はなくなり,Secret の自動マウントはされなくなった.

apiVersion: v1
kind: Pod
metadata:
  name: my-nginx
  namespace: default
spec:
  containers:
  - image: nginx:1.21
    name: my-nginx
  serviceAccountName: auto-mount-off

なお,Service AccountSecret は紐付いているため,以下のように確認できる.

$ kubectl get serviceaccounts 
NAME             SECRETS   AGE
auto-mount-off   1         3m10s
default          1         50m

$ kubectl get secrets
NAME                         TYPE                                  DATA   AGE
auto-mount-off-token-7mhnv   kubernetes.io/service-account-token   3      3m10s
default-token-5qnst          kubernetes.io/service-account-token   3      50m

まとめ

Kubernetes のセキュリティ対策として,Service AccountPod に設定できる automountServiceAccountToken フィールド(Secret の自動マウントをオプトアウトするかどうか)の動作確認をした.CKS (Certified Kubernetes Security Specialist) の出題範囲にも関連するトピックが含まれているし,最近読んでいる「Docker/Kubernetes開発・運用のためのセキュリティ実践ガイド」にも automountServiceAccountToken フィールドの解説が載っていたので,気になっていた!引き続き試していくぞー!