Skip to content
Published on

Kubernetes RBAC深掘りとOPA Gatekeeperによるポリシーのコード化運用ガイド

Authors
  • Name
    Twitter
Kubernetes RBAC深掘りとOPA Gatekeeperによるポリシーのコード化運用ガイド

なぜRBACだけでは不十分なのか

Kubernetes RBAC(Role-Based Access Control)は「誰がどのリソースに対してどの操作を実行できるか」を制御する中核的なメカニズムです。しかし、RBACだけでは以下のような要件を満たすことができません。

  • リソース内容に対する制約:RBACはPodを作成できるかどうかは制御しますが、そのPodがprivileged: trueかどうか、許可されたレジストリのイメージを使用しているかどうかは検証できません。
  • 命名規則の強制:特定のlabelやannotationが必ず存在しなければならないという組織ポリシーをRBACで表現することはできません。
  • 動的なポリシー変更:RBACの変更にはYAMLの修正とkubectl applyが必要です。数百のクラスターで一貫したポリシーデプロイを行うのは困難です。

このギャップを埋めるのがAdmission ControllerベースのPolicy-as-Codeアプローチであり、その代表的な実装がOPA Gatekeeperです。この記事ではRBACの高度な設計から始めて、Gatekeeperでポリシーをコード化し運用する全プロセスを解説します。

RBAC高度な設計原則

最小権限の原則の実践適用

RBAC設計の第一原則は必要最小限の権限のみ付与することです。以下のルールに従います。

  1. ClusterRoleBindingよりRoleBindingを優先使用:Namespaceスコープで十分な場合はClusterRoleBindingを使用しません。
  2. ワイルドカード禁止resources: ["*"]verbs: ["*"]は使用しません。
  3. system:mastersグループの使用禁止:このグループのメンバーはすべてのRBACチェックをバイパスします。break-glass手順用としてのみ別途管理します。
# bad-example: 過剰な権限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: too-permissive
rules:
  - apiGroups: ['*']
    resources: ['*']
    verbs: ['*']
---
# good-example: 必要な権限のみ明示
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-deployer
  namespace: production
rules:
  - apiGroups: ['apps']
    resources: ['deployments']
    verbs: ['get', 'list', 'watch', 'create', 'update', 'patch']
  - apiGroups: ['']
    resources: ['services', 'configmaps']
    verbs: ['get', 'list', 'watch', 'create', 'update']
  - apiGroups: ['']
    resources: ['pods']
    verbs: ['get', 'list', 'watch']
  - apiGroups: ['']
    resources: ['pods/log']
    verbs: ['get']

権限エスカレーション防止

Kubernetes RBAC APIはデフォルトで権限エスカレーションをブロックします。ユーザーがRoleやRoleBindingを作成・変更するには、そのRoleに含まれるすべての権限を既に保有している必要があります。ただし、以下の2つのverbはこの保護をバイパスできるため、特に注意が必要です。

危険なVerb説明対応策
escalateRoleに自分が持っていない権限を追加できるプラットフォーム管理者のみに付与、OPAで二重検証
bind自分が持っていない権限のRoleをバインドできるClusterRoleBinding作成権限自体を制限
impersonate他のユーザー・グループになりすませる監査ログの必須モニタリング、特定対象のみ許可
# 権限エスカレーション関連verbを監視する監査ポリシー
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  - level: RequestResponse
    verbs: ['escalate', 'bind', 'impersonate']
    resources:
      - group: 'rbac.authorization.k8s.io'
        resources: ['clusterroles', 'clusterrolebindings', 'roles', 'rolebindings']

Aggregated ClusterRoleを安全に使う

Aggregated ClusterRoleはラベルセレクターに基づいて複数のClusterRoleのルールを自動的に合算します。便利ですが、**意図しない権限の蓄積(Role Explosion)**が発生する可能性があります。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring-aggregate
  labels:
    rbac.example.com/aggregate-to-monitoring: 'true'
aggregationRule:
  clusterRoleSelectors:
    - matchLabels:
        rbac.example.com/aggregate-to-monitoring: 'true'
rules: [] # rulesは自動的に埋められる
---
# このClusterRoleのルールが上記aggregateに自動マージされる
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring-pods
  labels:
    rbac.example.com/aggregate-to-monitoring: 'true'
rules:
  - apiGroups: ['']
    resources: ['pods', 'pods/log']
    verbs: ['get', 'list', 'watch']

運用Tips:Aggregated ClusterRoleにどのルールが含まれているか定期的に確認しましょう。

# Aggregated ClusterRoleの実際のルール確認
kubectl get clusterrole monitoring-aggregate -o jsonpath='{.rules}' | jq .

# 特定ラベルを持つ全ClusterRoleを照会
kubectl get clusterrole -l rbac.example.com/aggregate-to-monitoring=true

ServiceAccount管理戦略

すべてのNamespaceにはdefault ServiceAccountが自動生成されます。これをそのまま使ってはいけません。

apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-app-sa
  namespace: production
automountServiceAccountToken: false # 不要ならトークンマウントを無効化
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: production
spec:
  template:
    spec:
      serviceAccountName: my-app-sa
      automountServiceAccountToken: false # Podレベルでも明示
      containers:
        - name: app
          image: registry.example.com/my-app:v2.1.0

RBAC vs ABAC vs OPA比較

ポリシーエンジンを選択する前に、各アプローチの違いを明確に理解する必要があります。

項目RBACABACOPA Gatekeeper
ポリシー単位ロール(Role)ベース属性(Attribute)ベースルール(Rego)ベース
設定方式Kubernetes APIオブジェクト静的ファイル(APIサーバー再起動必要)CRD(ConstraintTemplate + Constraint)
リソース内容検証不可限定的完全対応
動的更新kubectl applyAPIサーバー再起動kubectl apply(ゼロダウンタイム)
Mutation対応該当なし該当なし対応(Assign、AssignMetadata)
Audit機能なしなし既存リソースの監査可能
学習コスト低い中程度高い(Rego学習必要)
コミュニティ成熟度組み込み機能Deprecated(非推奨)CNCF Graduated(OPA)

キーポイント:RBACは「アクセス可否」を制御し、OPA Gatekeeperは「リソース内容の適合性」を検証します。両者は代替関係ではなく補完関係です。

OPA Gatekeeperアーキテクチャ

Admission Controllerの動作フロー

Kubernetes APIサーバーはリクエストを処理する際、以下の順序でAdmission Controllerを経由します。

APIリクエスト → AuthenticationAuthorization(RBAC)Mutating AdmissionValidating Admission → etcd保存
                                                              ↑                      ↑
                                                        Gatekeeper Mutation    Gatekeeper Validation

GatekeeperはValidatingAdmissionWebhookMutatingAdmissionWebhookの両方に登録され、APIサーバーがリソースを保存する前にポリシー検証を実行します。

コアコンポーネント

Gatekeeperは大きく3つのコンポーネントで構成されます。

  1. Controller Manager:ConstraintTemplateとConstraint CRDを管理し、Regoポリシーをコンパイルします。
  2. Audit Controller:定期的に既存リソースをスキャンしてポリシー違反を検知します(デフォルト60秒間隔)。
  3. Webhook Server:APIサーバーのAdmissionリクエストを受けてリアルタイムでポリシーを評価します。

Gatekeeperのインストール

# Helmでインストール(推奨)
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm repo update

helm install gatekeeper gatekeeper/gatekeeper \
  --namespace gatekeeper-system \
  --create-namespace \
  --set replicas=3 \
  --set audit.replicas=1 \
  --set audit.logLevel=INFO \
  --set logDenies=true \
  --set emitAdmissionEvents=true \
  --set emitAuditEvents=true

# インストール確認
kubectl get pods -n gatekeeper-system
kubectl get crd | grep gatekeeper

インストール後に確認すべきCRD一覧:

assign.mutations.gatekeeper.sh
assignmetadata.mutations.gatekeeper.sh
configs.config.gatekeeper.sh
constraintpodstatuses.status.gatekeeper.sh
constrainttemplatepodstatuses.status.gatekeeper.sh
constrainttemplates.templates.gatekeeper.sh
expansiontemplate.expansion.gatekeeper.sh
modifyset.mutations.gatekeeper.sh
mutatorpodstatuses.status.gatekeeper.sh
providers.externaldata.gatekeeper.sh

ConstraintTemplate作成の実践

ConstraintTemplateはポリシーのテンプレート(雛型)を定義し、Constraintはそのテンプレートにパラメータを埋めて実際のポリシーを有効化します。

例1:必須Label強制

すべてのDeploymentにapp.kubernetes.io/nameapp.kubernetes.io/ownerラベルが必ず存在しなければならないポリシーです。

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequiredlabels
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredLabels
      validation:
        openAPIV3Schema:
          type: object
          properties:
            labels:
              type: array
              description: '必ず存在しなければならないラベル名のリスト'
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredlabels

        violation[{"msg": msg, "details": {"missing_labels": missing}}] {
          provided := {label | input.review.object.metadata.labels[label]}
          required := {label | label := input.parameters.labels[_]}
          missing := required - provided
          count(missing) > 0
          msg := sprintf("リソースに必須ラベルがありません: %v", [missing])
        }
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: deployment-must-have-labels
spec:
  enforcementAction: deny
  match:
    kinds:
      - apiGroups: ['apps']
        kinds: ['Deployment']
    namespaces: ['production', 'staging']
    excludedNamespaces: ['kube-system', 'gatekeeper-system']
  parameters:
    labels:
      - 'app.kubernetes.io/name'
      - 'app.kubernetes.io/owner'

例2:許可されたコンテナレジストリのみ使用

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8sallowedrepos
spec:
  crd:
    spec:
      names:
        kind: K8sAllowedRepos
      validation:
        openAPIV3Schema:
          type: object
          properties:
            repos:
              type: array
              description: '許可されたコンテナレジストリのプレフィックスリスト'
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sallowedrepos

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          not startswith_any(container.image, input.parameters.repos)
          msg := sprintf("コンテナ '%v' のイメージ '%v' は許可されたレジストリに含まれていません。許可リスト: %v", [container.name, container.image, input.parameters.repos])
        }

        violation[{"msg": msg}] {
          container := input.review.object.spec.initContainers[_]
          not startswith_any(container.image, input.parameters.repos)
          msg := sprintf("initContainer '%v' のイメージ '%v' は許可されたレジストリに含まれていません。許可リスト: %v", [container.name, container.image, input.parameters.repos])
        }

        startswith_any(str, prefixes) {
          prefix := prefixes[_]
          startswith(str, prefix)
        }
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
  name: allowed-repos-production
spec:
  enforcementAction: deny
  match:
    kinds:
      - apiGroups: ['']
        kinds: ['Pod']
    namespaces: ['production']
  parameters:
    repos:
      - 'registry.example.com/'
      - 'gcr.io/my-project/'

例3:Privilegedコンテナのブロック

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8spspprivileged
spec:
  crd:
    spec:
      names:
        kind: K8sPSPPrivileged
      validation:
        openAPIV3Schema:
          type: object
          properties:
            exemptImages:
              type: array
              description: '例外として許可するイメージリスト'
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8spspprivileged

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          container.securityContext.privileged == true
          not is_exempt(container.image)
          msg := sprintf("Privilegedコンテナは許可されていません: '%v'", [container.name])
        }

        violation[{"msg": msg}] {
          container := input.review.object.spec.initContainers[_]
          container.securityContext.privileged == true
          not is_exempt(container.image)
          msg := sprintf("Privileged initContainerは許可されていません: '%v'", [container.name])
        }

        is_exempt(image) {
          exempt := input.parameters.exemptImages[_]
          image == exempt
        }

enforcementAction戦略:AuditからDenyまで

Gatekeeperの中核的な運用戦略は段階的ロールアウトです。最初からdenyで始めると、既存ワークロードが大量にブロックされる可能性があります。

段階的適用フロー

1段階: dryrun  →  第2段階: warn  →  第3段階: deny
(監査のみ)        (警告表示)       (実際のブロック)
enforcementAction動作使用タイミング
dryrun違反をAudit結果にのみ記録、リクエストは許可ポリシー初回デプロイ時、影響度把握段階
warn違反時に警告メッセージを返すがリクエストは許可開発チームに認知させる段階
deny違反時にリクエストを拒否十分なテスト後の本番適用

Audit結果の確認

# 特定Constraintの違反事項確認
kubectl get k8srequiredlabels deployment-must-have-labels -o yaml

# 違反リソースのみフィルタリング(jq使用)
kubectl get k8srequiredlabels deployment-must-have-labels -o json | \
  jq '.status.violations[] | {name: .name, namespace: .namespace, message: .message}'

# Gatekeeper auditログ確認
kubectl logs -n gatekeeper-system -l control-plane=audit-controller --tail=100 | \
  grep '"process":"audit"'

dryrunからdenyへの安全な移行方法

#!/bin/bash
# safe-enforcement-switch.sh
# dryrun → deny移行前の違反確認スクリプト

CONSTRAINT_KIND=$1
CONSTRAINT_NAME=$2

echo "=== 現在の違反件数確認 ==="
VIOLATIONS=$(kubectl get ${CONSTRAINT_KIND} ${CONSTRAINT_NAME} -o json | \
  jq '.status.totalViolations')

echo "総違反件数: ${VIOLATIONS}"

if [ "${VIOLATIONS}" -gt 0 ]; then
  echo ""
  echo "=== 違反リソース一覧 ==="
  kubectl get ${CONSTRAINT_KIND} ${CONSTRAINT_NAME} -o json | \
    jq -r '.status.violations[] | "\(.namespace)/\(.name): \(.message)"'
  echo ""
  echo "[WARNING] 違反リソースが存在します。denyに切り替えるとそれらのリソースの更新がブロックされます。"
  echo "先に違反リソースを修正してください。"
  exit 1
fi

echo ""
echo "違反事項なし。denyに切り替えます。"
kubectl patch ${CONSTRAINT_KIND} ${CONSTRAINT_NAME} --type=merge \
  -p '{"spec":{"enforcementAction":"deny"}}'
echo "切り替え完了。"

Gatekeeper vs Kyverno:ポリシーエンジン選択ガイド

OPA GatekeeperとKyvernoはKubernetesポリシーエンジンの二大巨頭です。プロジェクトに合った選択が重要です。

比較項目OPA GatekeeperKyverno
ポリシー言語Rego(専用言語)YAML(Kubernetes-native)
CNCFステージGraduated(OPA)Incubating
Validating Webhook対応対応
Mutating Webhook対応(Assign、AssignMetadata)対応(ネイティブ)
リソース生成(Generate)未対応対応
イメージ署名検証外部データ連携必要組み込み対応(Cosign、Notary)
Audit機能組み込み(定期スキャン)組み込み(Policy Report CRD)
外部データ連携External Data Provider APIAPI Call対応
マルチクラスターConfig Sync等の外部ツール自前対応は限定的
ValidatingAdmissionPolicy連携v3.22から統合対応
学習コスト高い(Rego)低い(YAML)
表現力非常に高い(複雑なロジック実装可能)中程度(CEL式で補完)
リソース使用量高い(複数Pod)中程度(単一Controller)

選択基準のまとめ:

  • Regoに既に慣れているチーム、複雑なクロスリソースポリシーが必要な場合:Gatekeeper
  • Kubernetes YAMLに慣れていて、Mutation/Generationがコアの場合:Kyverno
  • 大規模エンタープライズ環境でOPAエコシステム(Styra DAS等)を活用する場合:Gatekeeper

CI/CDパイプラインへのポリシー統合

Gitベースのポリシー管理構造

policies/
├── templates/
│   ├── k8s-required-labels.yaml
│   ├── k8s-allowed-repos.yaml
│   └── k8s-psp-privileged.yaml
├── constraints/
│   ├── production/
│   │   ├── required-labels.yaml
│   │   └── allowed-repos.yaml
│   └── staging/
│       └── required-labels.yaml
├── tests/
│   ├── required-labels_test.rego
│   └── allowed-repos_test.rego
└── Makefile

Rego単体テストの作成

Regoポリシーには必ず単体テストを書く必要があります。OPA CLIのopa testコマンドを使用します。

# tests/required-labels_test.rego
package k8srequiredlabels

test_violation_missing_label {
  input := {
    "review": {
      "object": {
        "metadata": {
          "labels": {
            "app.kubernetes.io/name": "myapp"
          }
        }
      }
    },
    "parameters": {
      "labels": ["app.kubernetes.io/name", "app.kubernetes.io/owner"]
    }
  }
  results := violation with input as input
  count(results) > 0
}

test_no_violation_all_labels_present {
  input := {
    "review": {
      "object": {
        "metadata": {
          "labels": {
            "app.kubernetes.io/name": "myapp",
            "app.kubernetes.io/owner": "team-platform"
          }
        }
      }
    },
    "parameters": {
      "labels": ["app.kubernetes.io/name", "app.kubernetes.io/owner"]
    }
  }
  results := violation with input as input
  count(results) == 0
}
# テスト実行
opa test ./policies/templates/ ./policies/tests/ -v

# CIで使用するMakefileターゲット
# Makefile
.PHONY: test-rego lint-rego apply-dryrun

test-rego:
	opa test ./policies/templates/ ./policies/tests/ -v --fail

lint-rego:
	opa check ./policies/templates/ --strict

apply-dryrun:
	kubectl apply -f ./policies/templates/ --dry-run=server
	kubectl apply -f ./policies/constraints/ --dry-run=server

GitHub Actions統合例

# .github/workflows/policy-ci.yaml
name: Policy CI
on:
  pull_request:
    paths:
      - 'policies/**'

jobs:
  test-and-validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup OPA
        uses: open-policy-agent/setup-opa@v2
        with:
          version: latest

      - name: Rego Lint
        run: |
          opa check ./policies/templates/ --strict

      - name: Rego Unit Tests
        run: |
          opa test ./policies/templates/ ./policies/tests/ -v --fail

      - name: Validate YAML syntax
        run: |
          for f in $(find policies/ -name '*.yaml'); do
            echo "Validating: $f"
            kubectl apply -f "$f" --dry-run=client 2>&1 || exit 1
          done

      - name: Conftest Policy Check
        uses: instrumenta/conftest-action@main
        with:
          files: policies/constraints/

トラブルシューティングガイド

症状1:Gatekeeper Webhookが応答せず全リクエストがブロックされる

これは最も致命的な障害シナリオです。Gatekeeper Podが全てダウンすると、failurePolicy設定によって動作が異なります。

# Webhook設定確認
kubectl get validatingwebhookconfiguration gatekeeper-validating-webhook-configuration -o yaml | \
  grep failurePolicy

# failurePolicy: Fail → Gatekeeper障害時に全リクエストブロック(危険!)
# failurePolicy: Ignore → Gatekeeper障害時にポリシー検証をスキップ

緊急対応手順:

# 1. Webhookを一時的に無効化(緊急時)
kubectl delete validatingwebhookconfiguration gatekeeper-validating-webhook-configuration

# 2. Gatekeeper Podの状態確認と復旧
kubectl get pods -n gatekeeper-system
kubectl describe pod -n gatekeeper-system -l control-plane=controller-manager

# 3. Pod復旧後にWebhook再登録(Helm再適用)
helm upgrade gatekeeper gatekeeper/gatekeeper \
  --namespace gatekeeper-system \
  --reuse-values

# 4. Webhook再登録確認
kubectl get validatingwebhookconfiguration | grep gatekeeper

運用上の推奨事項:本番環境ではfailurePolicy: Ignoreに設定して、Gatekeeper障害がクラスター全体の障害に拡大するのを防ぎます。ただし、Gatekeeperダウン時にはポリシー検証が一時的に無効化されるため、モニタリングアラートを必ず設定します。

症状2:ConstraintTemplate適用後のStatusが「Not Ready」

# ConstraintTemplateステータス確認
kubectl get constrainttemplate k8srequiredlabels -o yaml | grep -A 20 status

# 一般的な原因:Rego構文エラー
# Controller Managerログでコンパイルエラーを確認
kubectl logs -n gatekeeper-system -l control-plane=controller-manager --tail=50 | \
  grep -i "error\|compile\|template"

一般的なRego構文の間違い:

  • input.review.objectパスの誤字
  • セミコロンの代わりに改行を使う際のインデントエラー
  • violationルールの戻り値形式に{"msg": msg}が含まれていない

症状3:Auditが違反を検知しない

# Configにリソース同期が設定されているか確認
kubectl get config config -n gatekeeper-system -o yaml

# 監査対象リソースをConfigに登録する必要がある
# Gatekeeper Config:Auditで参照するリソースの登録
apiVersion: config.gatekeeper.sh/v1alpha1
kind: Config
metadata:
  name: config
  namespace: gatekeeper-system
spec:
  sync:
    syncOnly:
      - group: ''
        version: 'v1'
        kind: 'Namespace'
      - group: ''
        version: 'v1'
        kind: 'Pod'
      - group: 'apps'
        version: 'v1'
        kind: 'Deployment'

症状4:Constraintが特定のNamespaceを除外しない

# matchブロックでexcludedNamespacesを確認
spec:
  match:
    excludedNamespaces:
      - 'kube-system'
      - 'gatekeeper-system'
      - 'cert-manager' # システムコンポーネントのNamespace
      - 'monitoring' # モニタリングスタック

また、Gatekeeperのグローバル設定で除外Namespaceを指定できます。

# Helm valuesでグローバル除外設定
helm upgrade gatekeeper gatekeeper/gatekeeper \
  --namespace gatekeeper-system \
  --set 'exemptNamespaces={kube-system,gatekeeper-system}'

ValidatingAdmissionPolicy連携(Kubernetes 1.30以降)

Kubernetes 1.30からValidatingAdmissionPolicy(VAP)がGAになりました。Gatekeeper v3.22からはVAPとの統合が強化され、sync-vap-enforcement-scopeフラグでGatekeeperのenforcement範囲とVAPのenforcement範囲を一致させることができます。

# Gatekeeper 3.22以降でVAP連携を有効化
helm upgrade gatekeeper gatekeeper/gatekeeper \
  --namespace gatekeeper-system \
  --set 'controllerManager.extraArgs={--sync-vap-enforcement-scope=true}'

VAPは外部Webhook呼び出しなしにAPIサーバー内部でCEL式による検証を行うため、レイテンシが低くなります。単純なポリシーはVAPで、複雑なクロスリソースポリシーはGatekeeperで分担するハイブリッド戦略が効果的です。

運用チェックリスト

RBACチェックリスト

  • system:mastersグループに一般ユーザーが含まれていないか
  • escalatebindimpersonate verbの使用をモニタリングしているか
  • すべてのServiceAccountに最小権限のみ付与しているか
  • default ServiceAccountをワークロードで直接使用していないか
  • 不要なPodにautomountServiceAccountToken: falseを設定しているか
  • ClusterRoleBindingよりNamespaceスコープのRoleBindingを優先使用しているか
  • Aggregated ClusterRoleの実際のルールを定期的にレビューしているか
  • RBAC関連の監査ログ(Audit Log)を収集しているか

OPA Gatekeeperチェックリスト

  • Gatekeeper Podが3つ以上のレプリカで運用されているか
  • failurePolicy設定が環境に適しているか(本番:Ignore推奨)
  • すべてのConstraintTemplateにRego単体テストが書かれているか
  • 新しいポリシーは必ずdryrunまたはwarnで先にデプロイしているか
  • Audit Controllerが正常に動作し違反事項をモニタリングしているか
  • kube-systemgatekeeper-system等のシステムNamespaceが除外されているか
  • Gatekeeperのリソース(CPU/Memory)使用量をモニタリングしているか
  • Webhookの応答レイテンシをモニタリングしているか(P99レイテンシ)
  • ポリシー変更はGitベースのPRレビューを経ているか
  • 緊急時のWebhook無効化手順がドキュメント化されているか

障害対応優先順位

優先順位障害状況即時対応根本対応
P0Webhook障害で全デプロイがブロックWebhookを削除しサービス復旧failurePolicy変更、レプリカ増設
P1Audit未作動で違反未検知Audit Controller再起動Config sync設定確認、ログパイプライン点検
P2特定ポリシーの誤検知(False Positive)該当Constraintをdryrunに切り替えRegoロジック修正およびテスト強化
P3ポリシー漏れで違反リソースがデプロイ手動監査後リソース修正ConstraintTemplate追加、CIパイプライン強化

実践シナリオ:最初から最後まで

シナリオ:本番クラスターにイメージレジストリ制限ポリシーを適用

# Step 1: 現状把握 - どのレジストリのイメージが使われているか確認
kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}{"\t"}{range .spec.containers[*]}{.image}{"\n"}{end}{end}' | \
  sort | uniq -c | sort -rn | head -20

# Step 2: ConstraintTemplateデプロイ
kubectl apply -f policies/templates/k8s-allowed-repos.yaml

# Step 3: dryrunモードでConstraintデプロイ
cat <<EOF | kubectl apply -f -
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
  name: allowed-repos-production
spec:
  enforcementAction: dryrun
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
    namespaces: ["production"]
  parameters:
    repos:
      - "registry.example.com/"
      - "gcr.io/my-project/"
EOF

# Step 4: 1-2日後に違反事項確認
kubectl get k8sallowedrepos allowed-repos-production -o json | \
  jq '.status.totalViolations'

# Step 5: 違反リソース修正後にwarnに切り替え
kubectl patch k8sallowedrepos allowed-repos-production --type=merge \
  -p '{"spec":{"enforcementAction":"warn"}}'

# Step 6: 開発チームのフィードバック収集後にdenyに切り替え
kubectl patch k8sallowedrepos allowed-repos-production --type=merge \
  -p '{"spec":{"enforcementAction":"deny"}}'

# Step 7: ポリシー動作検証
kubectl run test-blocked --image=docker.io/nginx:latest -n production
# Error: admission webhook "validation.gatekeeper.sh" denied the request

おわりに

RBACとOPA GatekeeperはKubernetesセキュリティの異なるレイヤーを担当します。RBACは「誰がアクセスできるか」を制御し、Gatekeeperは「どのリソースが許可されるか」を検証します。この2つのレイヤーを併せて運用することで、初めて完全なポリシー体制が構築されます。

核心原則を改めて整理します。

  1. RBACは最小権限の原則を徹底的に適用する。ClusterRoleBindingよりRoleBindingを、ワイルドカードより明示的なリソース/verb指定を優先します。
  2. Gatekeeperポリシーは必ずコードとして管理する。Gitリポジトリでバージョン管理し、PRレビューを経て、CIでRegoテストを自動実行します。
  3. 段階的ロールアウト(dryrun、warn、deny)を必ず守る。本番環境に直接denyをデプロイするのはインシデントの始まりです。
  4. 障害対応手順を事前に準備する。Webhook削除コマンドをrunbookに含め、定期的に訓練します。

参考資料