エラーの概要

Kubernetes の 403 エラーは「Forbidden」を意味し、リクエスト認証は成功しているものの、そのリソースに対する操作権限(RBAC: Role-Based Access Control)がないことを示します。Pod の実行、リソースの取得・更新・削除など、特定の操作がセキュリティポリシーにより拒否された状態です。API サーバーマニフェスト適用時、kubectl コマンド実行時に頻繁に発生します。

実際のエラーメッセージ例

Error from server (Forbidden): pods "my-pod" is forbidden: User "system:serviceaccount:default:app" cannot get resource "pods" in API group "" in the namespace "default"
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "deployments.apps \"nginx\" is forbidden: User \"system:serviceaccount:kube-system:default\" cannot create resource \"deployments\" in API group \"apps\" in the namespace \"kube-system\"",
  "reason": "Forbidden",
  "details": {
    "name": "nginx",
    "group": "apps",
    "kind": "deployments"
  },
  "code": 403
}

よくある原因と解決手順

原因1: ServiceAccount に適切な Role が割り当てられていない

ServiceAccount は Kubernetes 内のアカウントであり、Pod がリソースにアクセスする際に使用されます。このアカウントに必要な権限を持つ Role が紐付けられていない場合、403 エラーが発生します。

Before(エラーが起きるコード):

apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-account
  namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: default
spec:
  template:
    spec:
      serviceAccountName: app-account
      containers:
      - name: app
        image: myapp:latest

After(修正後):

apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-account
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-role
  namespace: default
rules:
- apiGroups: [""]
  resources: ["pods", "pods/logs"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-rolebinding
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: app-role
subjects:
- kind: ServiceAccount
  name: app-account
  namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: default
spec:
  template:
    spec:
      serviceAccountName: app-account
      containers:
      - name: app
        image: myapp:latest

原因2: Namespace が異なる RoleBinding を参照している

RoleBinding は特定の Namespace に紐付きます。Pod が存在する Namespace と、RoleBinding が定義されている Namespace が異なる場合、権限が認識されず 403 エラーが発生します。

Before(エラーが起きるコード):

# namespace: kube-system に RoleBinding を定義
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-rolebinding
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: app-role
subjects:
- kind: ServiceAccount
  name: app-account
  namespace: default  # 異なる namespace の ServiceAccount を指定
---
# Deployment は default namespace に存在
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: default
spec:
  template:
    spec:
      serviceAccountName: app-account

After(修正後):

# RoleBinding を正しい namespace に定義
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-rolebinding
  namespace: default  # Pod と同じ namespace に統一
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: app-role
subjects:
- kind: ServiceAccount
  name: app-account
  namespace: default

原因3: 必要な API グループが Role に指定されていない

Kubernetes リソースは API グループ(例: apps, batch, networking.k8s.io)に属しており、Role で適切な API グループを指定しなければアクセスできません。apiGroups: [""] は core API グループのみを対象とするため、拡張リソースには無効です。

Before(エラーが起きるコード):

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-role
  namespace: default
rules:
- apiGroups: [""]  # core API グループのみ
  resources: ["pods"]
  verbs: ["get", "list"]
- apiGroups: [""]
  resources: ["deployments"]  # ❌ deployments は "apps" グループ
  verbs: ["get", "list"]

After(修正後):

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-role
  namespace: default
rules:
- apiGroups: [""]
  resources: ["pods", "pods/logs", "configmaps", "services"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
  resources: ["deployments", "statefulsets", "daemonsets"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["batch"]
  resources: ["jobs", "cronjobs"]
  verbs: ["get", "list", "create"]

原因4: デフォルト ServiceAccount が使用されている

Pod 定義で serviceAccountName を明示的に指定しない場合、デフォルトの default ServiceAccount が使用されます。この default アカウントには通常、リソースへのアクセス権限がないため、403 エラーが発生します。

Before(エラーが起きるコード):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: default
spec:
  template:
    spec:
      # serviceAccountName を指定していないため、
      # デフォルトの "default" ServiceAccount が使用される
      containers:
      - name: app
        image: myapp:latest

After(修正後):

apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-account
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-role
  namespace: default
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-rolebinding
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: app-role
subjects:
- kind: ServiceAccount
  name: app-account
  namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: default
spec:
  template:
    spec:
      serviceAccountName: app-account  # 明示的に指定
      containers:
      - name: app
        image: myapp:latest

Kubernetes 固有の注意点

Cluster 全体に適用する権限が必要な場合は ClusterRole を使用

Namespace を超えて全体的な権限が必要な場合、Role と RoleBinding ではなく ClusterRole と ClusterRoleBinding を使用します。例えば、全 Namespace の Pod を監視する監視エージェントやログ収集エージェントは ClusterRole が必須です。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: log-collector
rules:
- apiGroups: [""]
  resources: ["pods", "pods/logs"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources: ["namespaces"]
  verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: log-collector-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: log-collector
subjects:
- kind: ServiceAccount
  name: log-collector
  namespace: kube-system

リソースの詳細なパーミッション制御(特定の Pod のみアクセス許可)

Role では特定の Pod 名を直接指定することはできませんが、Label Selector を活用することで粒度の細かい制御が可能です。ただし RBAC では Label ベースのフィルタリングが直接機能しないため、webhook ベースの認可ポリシーを検討してください。

標準的な Role テンプレート(view, edit, admin)

Kubernetes は組み込みの ClusterRole を提供しており、これらを参考にすることで適切な権限構成を決定できます。

kubectl get clusterrole

vieweditadmin といった標準ロールが存在し、これらを RoleBinding で参照する方法も有効です。

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-view
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: view
subjects:
- kind: ServiceAccount
  name: app-account
  namespace: default

それでも解決しない場合

kubectl auth can-i コマンドで権限を確認

特定の ServiceAccount が特定のアクションを実行可能か事前に確認できます。

kubectl auth can-i create deployments --as=system:serviceaccount:default:app-account -n default

成功時は yes が、失敗時は no が返されます。

API サーバーのログを確認

Cluster 管理者は API サーバーログを調査して詳細な拒否理由を確認できます。

kubectl logs -n kube-system -l component=kube-apiserver | grep "Forbidden"

kubectl describe で Role / RoleBinding を確認

定義されている Role や RoleBinding が正しく参照されているか確認します。

kubectl describe rolebinding app-rolebinding -n default
kubectl describe role app-role -n default

公式ドキュメント


免責事項:本記事の内容は、執筆時点の公開情報をもとに作成したものです。ソフトウェアの仕様は予告なく変更されることがあります。最新の情報は各ツールの公式サポートページをご確認ください。本記事の情報を利用した結果生じたいかなる損害についても、著者および運営者は責任を負いかねます。