🎫
概念 #セキュリティ #Kubernetes #RBAC #ServiceAccount #最小権限 📚 Kubernetesセキュリティ

Kubernetes RBAC・ServiceAccount

Kubernetes の認証・認可フロー

kubectl や Pod からのすべての API リクエストは以下の順序で処理される。

リクエスト


①認証(Authentication)
    誰か?
    ─ X.509 クライアント証明書
    ─ Bearer Token(ServiceAccount Token)
    ─ OIDC(外部 IdP との連携)


②認可(Authorization)
    何をしてよいか?
    ─ RBAC(Role-Based Access Control)← 現在の標準
    ─ ABAC / Webhook(特殊用途)


③Admission Control
    ポリシーに準拠しているか?
    ─ OPA Gatekeeper / Kyverno


④etcd への永続化 / 実行

RBAC の4リソース

Kubernetes の RBAC は4つのリソースで構成される。

リソーススコープ役割
RoleNamespace 内権限の定義
ClusterRoleクラスタ全体権限の定義(Namespace をまたぐ)
RoleBindingNamespace 内Role を Subject に紐づける
ClusterRoleBindingクラスタ全体ClusterRole を Subject に紐づける
Subject(誰が)
  ├── User
  ├── Group
  └── ServiceAccount
        ↓ RoleBinding / ClusterRoleBinding
Role / ClusterRole(何ができるか)
  └── rules:
        apiGroups・resources・verbs の組み合わせ

Role / ClusterRole の設計

verbs(動詞)の種類

verb対応する操作
get単一リソースの取得
listリソース一覧の取得
watchリソースの変更を監視
create新規作成
update既存リソースの更新
patch部分更新
delete削除
deletecollection複数削除
*すべての操作(使用は最小限に)

最小権限の Role 設計例

# NG: ワイルドカードで全権限を付与
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: bad-role
  namespace: production
rules:
- apiGroups: ["*"]
  resources: ["*"]
  verbs: ["*"]

---
# OK: 必要な操作のみ列挙
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: production
rules:
- apiGroups: [""]           # core API グループ
  resources: ["pods", "pods/log"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list"]

ClusterRole の使いどころ

# Namespace をまたいでログを読む監視ツール用 ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: log-reader
rules:
- apiGroups: [""]
  resources: ["pods", "pods/log", "namespaces"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
  resources: ["deployments", "replicasets"]
  verbs: ["get", "list"]

ServiceAccount

Pod が Kubernetes API にアクセスするための ID。
ユーザーアカウント(人間)に対して、ServiceAccount はワークロード(Pod)用のアカウント。

デフォルト ServiceAccount の問題

# 指定しない場合、Pod は Namespace の "default" ServiceAccount を使う
spec:
  containers:
  - name: app
    image: my-app:latest
# ↑ default SA は何もしないように見えるが、
#   クラスタによっては予想以上の権限を持っている場合がある

原則: Pod には専用の ServiceAccount を作成し、default SA は使わない。

専用 ServiceAccount の作成と紐づけ

# 1. 専用 ServiceAccount を作成
apiVersion: v1
kind: ServiceAccount
metadata:
  name: order-service-sa
  namespace: production
  annotations:
    # GKE の場合: Workload Identity で GCP SA と紐づける
    iam.gke.io/gcp-service-account: order-sa@project.iam.gserviceaccount.com

---
# 2. 最小権限の Role を作成
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: order-service-role
  namespace: production
rules:
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get"]            # ConfigMap の読み取りのみ

---
# 3. RoleBinding で紐づける
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: order-service-binding
  namespace: production
subjects:
- kind: ServiceAccount
  name: order-service-sa
  namespace: production
roleRef:
  kind: Role
  apiGroupt: rbac.authorization.k8s.io
  name: order-service-role

---
# 4. Pod に ServiceAccount を指定
spec:
  serviceAccountName: order-service-sa
  automountServiceAccountToken: false  # API アクセス不要なら Token をマウントしない
  containers:
  - name: order-service
    image: order-service:latest

automountServiceAccountToken の制御

デフォルトでは ServiceAccount の JWT トークンが /var/run/secrets/kubernetes.io/serviceaccount/token に自動マウントされる。
API アクセスが不要な Pod にはマウント自体を無効化する。

# ServiceAccount レベルで無効化(この SA を使う全 Pod に適用)
apiVersion: v1
kind: ServiceAccount
metadata:
  name: no-api-access-sa
automountServiceAccountToken: false

# Pod レベルで無効化(個別に制御したい場合)
spec:
  automountServiceAccountToken: false

Projected Volume による短命トークン

古いバージョンでは SA トークンに有効期限がなかった。
現在は Bound Service Account Token(Projected Volume)で有効期限付きトークンを発行する。

spec:
  volumes:
  - name: token
    projected:
      sources:
      - serviceAccountToken:
          path: token
          expirationSeconds: 3600   # 1時間で期限切れ
          audience: my-service      # 対象サービスを限定
  containers:
  - name: app
    volumeMounts:
    - mountPath: /var/run/secrets/tokens
      name: token

よくある RBAC の設定ミス

cluster-admin の乱用

# 危険: 全ユーザーにクラスタ管理者権限を付与
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: dangerous-binding
subjects:
- kind: Group
  name: system:authenticated   # 認証済み全ユーザー!
roleRef:
  kind: ClusterRole
  name: cluster-admin          # 最高権限
  apiGroup: rbac.authorization.k8s.io

secrets の get 権限の見落とし

# 注意: secrets への get 権限は非常に強力
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "list"]   # list だけでも全 Secret の名前がわかる
                            # get があれば Secret の中身を読める

Secret への権限は特に慎重に設計する。CI/CD パイプラインや監視ツールでも
secrets リソースへの get が本当に必要かを必ず問い直す。

escalate / bind verb の危険性

# escalate: 自分が持っていない権限を含む Role を作れる
# bind: 自分より高い権限を他者に付与できる
# → どちらも実質的な権限昇格につながる
# → これらの verb は cluster-admin 相当のユーザーのみに限定する

RBAC の検査・監査ツール

# 特定ユーザー・SA が何をできるか確認
kubectl auth can-i create pods --as=system:serviceaccount:production:order-service-sa
kubectl auth can-i '*' '*' --as=system:serviceaccount:default:default

# Namespace 内の全権限を確認
kubectl auth can-i --list --namespace=production \
  --as=system:serviceaccount:production:order-service-sa

# rbac-lookup: SA や User の権限を人間が読みやすい形で表示
rbac-lookup order-service-sa -k serviceaccount -n production

# rakkess: リソースごとにどの操作が可能かをマトリクス表示
rakkess --sa production:order-service-sa

# audit2rbac: 監査ログから最小権限の RBAC を自動生成
audit2rbac --filename audit.log --serviceaccount production:order-service-sa

Terraform による RBAC 管理

RBAC の設定も Terraform(kubernetes provider)で管理することでドリフトを防ぐ。

resource "kubernetes_service_account" "order_service" {
  metadata {
    name      = "order-service-sa"
    namespace = "production"
    annotations = {
      "iam.gke.io/gcp-service-account" = "order-sa@${var.project_id}.iam.gserviceaccount.com"
    }
  }
  automount_service_account_token = false
}

resource "kubernetes_role" "order_service" {
  metadata {
    name      = "order-service-role"
    namespace = "production"
  }
  rule {
    api_groups = [""]
    resources  = ["configmaps"]
    verbs      = ["get"]
  }
}

resource "kubernetes_role_binding" "order_service" {
  metadata {
    name      = "order-service-binding"
    namespace = "production"
  }
  subject {
    kind      = "ServiceAccount"
    name      = kubernetes_service_account.order_service.metadata[0].name
    namespace = "production"
  }
  role_ref {
    api_group = "rbac.authorization.k8s.io"
    kind      = "Role"
    name      = kubernetes_role.order_service.metadata[0].name
  }
}

チェックリスト

□ Pod に専用の ServiceAccount を割り当てている(default SA を使っていない)
□ automountServiceAccountToken: false を API 不要な Pod に設定している
□ ワイルドカード(*)を使わず必要な verb のみ許可している
□ secrets リソースへのアクセス権限を最小限にしている
□ cluster-admin を一般ユーザーや SA に付与していない
□ ClusterRoleBinding より RoleBinding(Namespace スコープ)を優先している
□ kubectl auth can-i で定期的に権限の棚卸しをしている
□ RBAC 設定を Terraform / GitOps で管理しドリフトを防いでいる

参考文献

  • Kubernetes 公式ドキュメント『Using RBAC Authorization』(kubernetes.io)— RBAC の仕様と設定例の公式リファレンス
  • CIS Kubernetes Benchmark v1.8 Section 5(Policies)— RBAC に関するセキュリティチェックリスト
  • Kaizhe Huang & Pranjal Jumde『Kubernetes Security』(Packt, 2021)— ServiceAccount とトークン管理の実践的解説
  • Google Cloud『GKE RBAC Best Practices』(公式ドキュメント)— Workload Identity と RBAC を組み合わせた GKE 向けガイド

出典: Kubernetes Documentation(kubernetes.io/docs/reference/access-authn-authz/rbac)/ CIS Kubernetes Benchmark v1.8 / Kubernetes Security(Kaizhe Huang & Pranjal Jumde, 2021)/ Google Cloud『GKE RBAC Best Practices』