【実務・中級編】 Kubernetes API ServerのRBAC最小権限原則とServiceAccountトークン管理 – インフラ・ネットワーク & クラウドセキュリティ防御ガイド

Kubernetesの「デフォルト」を信じるな:API Serverを狙う権限昇格を防ぐ実戦的RBAC管理

現場でインシデント対応をしていると、溜息が出るほど多いのが「とりあえず動けばいい」という理由で、Podにcluster-admin権限を付与したり、defaultのServiceAccountをそのまま使い回したりしているケースだ。

KubernetesのAPI Serverは、クラスタの心臓部だ。ここに攻撃者が触れれば、コンテナの脱獄(Breakout)は時間の問題となる。今回は、現場の泥臭い経験から、Kubernetesのセキュリティを「守り」から「攻め」の視点で要塞化するための勘所を叩き込む。

—

なぜAPI Serverが狙われるのか?(PoC的な視点)

攻撃者がPodへの侵入に成功したとき、真っ先に確認するのが /var/run/secrets/kubernetes.io/serviceaccount/token だ。

もし、このトークンに不適切な権限が付与されていたらどうなるか? 攻撃者はそのトークンを使い、API Serverに対して以下のようなクエリを投げる。

# 攻撃者がPod内で実行するクエリのイメージ
# クラスタ内の全シークレットを列挙する
curl -k -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
  https://kubernetes.default.svc/api/v1/namespaces/default/secrets

これが通ってしまった瞬間、DBの接続情報やクラウドのクレデンシャルが盗まれ、被害はコンテナの枠を超えてクラウドインフラ全体へ波及する。これを防ぐには、「不要なものは持たせない」「必要なものだけを、短期間だけ貸し出す」という鉄則を徹底するしかない。

—

1. 自動マウントされるトークンの無効化

まず、多くの現場で放置されているのが「全Podに対するServiceAccountトークンの自動マウント」だ。これ、実はほとんどのPodで不要だ。

Podの定義(YAML)で、明示的にマウントを拒否するように設定しよう。

# deployment.yaml の設定例
apiVersion: apps/v1
kind: Deployment
metadata:
  name: secure-app
spec:
  template:
    spec:
      # トークンの自動マウントを無効化する(これが基本!)
      automountServiceAccountToken: false
      containers:
        - name: app
          image: my-app:latest

これだけで、Podが侵入されてもAPI Serverへの踏み台として利用されるリスクを劇的に下げられる。

—

2. 最小権限を実現するRBAC設計

どうしてもAPI Serverと通信が必要なアプリがある場合は、専用のServiceAccountを作成し、RoleBindingで極小の権限を与える。ClusterRoleは強力すぎる。原則としてRoleでスコープを限定せよ。

以下は、「特定の名前空間で、特定のPodのステータス取得のみを許可する」セキュアな設計例だ。

# service-account.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-limited-sa
  namespace: production
---
# role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: production
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"] # 最小限の操作のみ
---
# rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods-binding
  namespace: production
subjects:
- kind: ServiceAccount
  name: app-limited-sa
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

—

3. 実践:PythonでAPIトークンを利用する際のベストプラクティス

もしアプリ側でKubernetes APIを叩く必要がある場合、標準のクライアントライブラリを使うはずだ。この時も、ハードコーディングは厳禁。環境変数やマウントされたトークンを扱う際は、以下の点に注意せよ。

from kubernetes import client, config

def get_pod_list():
    # クラスタ内ならロード、外ならkubeconfigを参照するセキュアなロード手法
    try:
        config.load_incluster_config()
    except config.ConfigException:
        config.load_kube_config()
        
    v1 = client.CoreV1Api()
    # 権限がある範囲内でのみリクエストを投げる
    pods = v1.list_namespaced_pod(namespace="production")
    return [pod.metadata.name for pod in pods.items]

# このコードを実行するコンテナは、必ず上記で作成した 
# ServiceAccount(app-limited-sa) を利用するように設定すること。

—

セキュリティチーフからの「最後の警告」

最後に、運用フェーズで必ずやってほしいことがある。それは「過剰権限の棚卸し」だ。

以下のコマンドで、現在クラスタ内に「権限が強すぎるBinding」がないか、定期的にチェックする習慣をつけてほしい。

# 権限が強すぎるClusterRoleBindingを探索する(手動チェック用)
kubectl get clusterrolebindings -o json | jq '.items[] | select(.roleRef.name=="cluster-admin")'

ここでsystem:serviceaccountが返ってきたら、それは赤信号だ。すぐにそのServiceAccountの用途を調査し、必要なければ削除、必要なら適切なRoleへの付け替えを行うこと。

Kubernetesのセキュリティは「設定して終わり」ではない。攻撃者は常に「設定の隙間」を探している。最小権限原則(Principle of Least Privilege)は、単なる教科書のスローガンではなく、君たちが開発したサービスを死守するための最後の防壁なのだ。

手を動かせ。そして、今日から automountServiceAccountToken: false をデプロイの標準にすること。それが、最強の防御への第一歩だ。

コメント

タイトルとURLをコピーしました