Gatekeeper を使用して Pod レベルのカスタム セキュリティ ポリシーを適用する


このページでは、Gatekeeper アドミッション コントローラを使用して、Google Kubernetes Engine(GKE)クラスタに Pod レベルのセキュリティ管理を適用する方法を説明します。

概要

Gatekeeper は、Open Policy Agent(OPA)を使用して Kubernetes クラスタで Pod を作成および更新するリクエストを検証するためのアドミッション コントローラです。

Gatekeeper を使用すると、管理者は制約を使ってポリシーを定義できます。制約は、Kubernetes でのデプロイの動作を許可または拒否する一連の条件です。その後、ConstraintTemplate を使用してクラスタにこのポリシーを適用できます。このドキュメントには、Gatekeeper を使用してセキュリティ ポリシーの適用、テスト、監査を確実に行うために、ワークロードのセキュリティ機能を制限する例を記載します。

Gatekeeper は、次のことも行います。

  • ポリシーのロールアウト: 段階的に範囲を限定してポリシーを適用し、ワークロードの中断のリスクを抑えます。
  • テスト時のポリシーの変更: ポリシー適用前に影響と範囲をテストするためのメカニズムを提供します。
  • 既存のポリシーの監査: 新しいワークロードと既存のワークロードにセキュリティ管理の適用(監査制御)を行います。

コンセプト

Gatekeeper は、クラスタを強力かつ柔軟に制御する方法を管理者に提供するための 2 つのコンセプト、つまり制約制約テンプレートを導入しています。これはどちらも、Open Policy Agent の制約フレームワークから継承したコンセプトです。

制約はセキュリティ ポリシーを表現したもので、要件と適用範囲を定義します。制約テンプレートは再利用可能なステートメント(Rego で記述)で、制約で定義された要件に基づいて、Kubernetes オブジェクト内の特定のフィールドを評価するロジックを適用します。

たとえば、特定の名前空間の Pod に適用できる許容可能な seccomp プロファイルを宣言するための制約と、これらの値の抽出と処理を行うロジックに相当する同等の制約テンプレートがあるとします。

次の制約テンプレートは、Gatekeeper リポジトリの Pod 仕様に securityContext.privileged が存在するかどうかを確認します。

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8spspprivilegedcontainer
spec:
  crd:
    spec:
      names:
        kind: K8sPSPPrivilegedContainer
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8spspprivileged

        violation[{"msg": msg, "details": {}}] {
            c := input_containers[_]
            c.securityContext.privileged
            msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext])
        }
        input_containers[c] {
            c := input.review.object.spec.containers[_]
        }
        input_containers[c] {
            c := input.review.object.spec.initContainers[_]
        }

上記の制約テンプレートを拡張するために、次の制約では、dryrun モードでのこの制約テンプレート特有の適用範囲(kinds)を定義します。

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPPrivilegedContainer
metadata:
  name: psp-privileged-container
spec:
  enforcementAction: dryrun
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]

Gatekeeper を使用すると、独自の制約と制約テンプレートを作成して特定のニーズに対応できます。Gatekeeper リポジトリ基準となる一連の制約と制約テンプレートを使用して、迅速な導入とセキュリティ適用を実現することもできます。各制約には Pod 構成の例も含まれています。

Google Cloud では、オープンソースの Gatekeeper のマネージド バージョン(Policy Controller)を提供し、公式にサポートしています。Google は、オープンソースの Gatekeeper プロジェクトを公式にサポートしていません。

始める前に

作業を始める前に、次のことを確認してください。

  • Google Kubernetes Engine API を有効にします。
  • Google Kubernetes Engine API の有効化
  • このタスクに Google Cloud CLI を使用する場合は、gcloud CLI をインストールして初期化します。すでに gcloud CLI をインストールしている場合は、gcloud components update を実行して最新のバージョンを取得します。

Policy Controller を使用してクラスタで Gatekeeper を有効にする

Policy Controller は、Gatekeeper オープンソース プロジェクト上に構築されたポリシー エンジンです。Policy Controller はコードとしてのポリシー、マルチクラスタ サポート、Cloud Logging との統合、Google Cloud コンソールでのポリシーのステータスの確認などの機能を備えており、ポリシーを大規模に適用することができます。このため、Google では Policy Controller の使用をおすすめしています。Policy Controller は Google Kubernetes Engine(GKE)Enterprise エディションのライセンスで使用できますが、代わりにクラスタに Gatekeeper をインストールすることもできます。

クラスタで Policy Controller を有効にするには、Policy Controller のインストール ガイドの説明に従ってください。

制約と制約テンプレートを有効にする

Gatekeeper とその制約テンプレートは、既存のワークロードや新しいワークロードに悪影響を与えることなくインストールして有効にすることが可能です。このため、該当するすべての Pod セキュリティ制約テンプレートをクラスタに適用することをおすすめします。

さらに、Gatekeeper の制約を実装して、名前空間や Pod などの特定のオブジェクトに制御を適用することもできます。

次の例では、制約一致のステートメントで定義することで、スコープを production 名前空間に存在する Pod に制限しています。

...
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
    namespaces:
      - "production"

Constraint オブジェクトと ConstraintTemplate オブジェクトで使用可能なオプションの詳細については、Gatekeeper の使用方法をご覧ください。

ポリシーのテスト

既存のクラスタに新しいポリシーを導入すると、動作を悪化させることがあります(たとえば、既存のワークロードを制限する場合など)。Pod セキュリティに Gatekeeper を使用する利点の 1 つは、ドライラン モードを使用して、実際に変更を行わずにポリシーの効果と影響をテストできる点です。これにより、ポリシーを適用せずに、実行中のクラスタに対してポリシー構成をテストできます。ポリシー違反はログに記録されるので、操作を妨げることなく確認できます。

次の手順は、デベロッパー、オペレータ、または管理者が制約テンプレートと制約を利用して効果や潜在的な影響を判断する方法を示しています。

  1. 監査機能とドライラン機能用のデータを複製するために Gatekeeper 構成ファイルを適用します。

    kubectl create -f- <<EOF
    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"
    EOF
    
  2. 制約が適用されていない状態で、権限を引き上げてワークロードを実行します。

    kubectl create -f- <<EOF
    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        securityContext:
          privileged: true
    EOF
    
  3. 上述の k8spspprivilegedcontainer 制約テンプレートを読み込みます。

    kubectl create -f- <<EOF
    apiVersion: templates.gatekeeper.sh/v1beta1
    kind: ConstraintTemplate
    metadata:
      name: k8spspprivilegedcontainer
    spec:
      crd:
        spec:
          names:
            kind: K8sPSPPrivilegedContainer
      targets:
        - target: admission.k8s.gatekeeper.sh
          rego: |
            package k8spspprivileged
    
            violation[{"msg": msg, "details": {}}] {
                c := input_containers[_]
                c.securityContext.privileged
                msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext])
            }
            input_containers[c] {
                c := input.review.object.spec.containers[_]
            }
            input_containers[c] {
                c := input.review.object.spec.initContainers[_]
            }
    EOF
    
  4. 次に、この制約テンプレートを拡張する新しい制約を作成します。ここでは enforcementActiondryrun に設定します。

    kubectl create -f- <<EOF
    apiVersion: constraints.gatekeeper.sh/v1beta1
    kind: K8sPSPPrivilegedContainer
    metadata:
      name: psp-privileged-container
    spec:
      enforcementAction: dryrun
      match:
        kinds:
          - apiGroups: [""]
            kinds: ["Pod"]
    EOF
    
  5. Gatekeeper が実行中のオブジェクト データを同期し、違反を受動的にチェックすることで、制約の status をチェックすることにより、違反が見つかったかどうか確認できます。

    kubectl get k8spspprivilegedcontainer.constraints.gatekeeper.sh/psp-privileged-container -o yaml
    
    apiVersion: constraints.gatekeeper.sh/v1beta1
    kind: K8sPSPPrivilegedContainer
    metadata:
    ...
     name: psp-privileged-container
    ...
    spec:
     enforcementAction: dryrun
     match:
       kinds:
       - apiGroups:
         - ""
         kinds:
         - Pod
    status:
     auditTimestamp: "2019-12-15T22:19:54Z"
     byPod:
     - enforced: true
       id: gatekeeper-controller-manager-0
     violations:
     - enforcementAction: dryrun
       kind: Pod
       message: 'Privileged container is not allowed: nginx, securityContext: {"privileged":
         true}'
       name: nginx
       namespace: default
    
  6. 別の特権 Pod を実行して、ポリシーがデプロイの妨げになっていないことを確認します。

    kubectl create -f- <<EOF
    apiVersion: v1
    kind: Pod
    metadata:
      name: privpod
      labels:
        app: privpod
    spec:
      containers:
      - name: nginx
        image: nginx
        securityContext:
          privileged: true
    EOF
    

    この新しい Pod が正常にデプロイされます。

  7. このセクションで作成したリソースをクリーンアップするには、次のコマンドを実行します。

    kubectl delete k8spspprivilegedcontainer.constraints.gatekeeper.sh/psp-privileged-container
    kubectl delete constrainttemplate k8spspprivilegedcontainer
    kubectl delete pod/nginx
    kubectl delete pod/privpod
    

ポリシーの適用

既存のワークロードや新しいワークロードに影響を与えずにポリシーの有効性と影響を確認できるようになったので、ポリシーを完全に適用して実装してみましょう。

上記のポリシーの検証に使用した例に基づいて、デベロッパー、オペレータ、または管理者が、制約テンプレートと制約を利用してポリシーを適用する方法について説明します。

  1. 上述の k8spspprivilegedcontainer 制約テンプレートを読み込みます。

    kubectl create -f- <<EOF
    apiVersion: templates.gatekeeper.sh/v1beta1
    kind: ConstraintTemplate
    metadata:
      name: k8spspprivilegedcontainer
    spec:
      crd:
        spec:
          names:
            kind: K8sPSPPrivilegedContainer
      targets:
        - target: admission.k8s.gatekeeper.sh
          rego: |
            package k8spspprivileged
    
            violation[{"msg": msg, "details": {}}] {
                c := input_containers[_]
                c.securityContext.privileged
                msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext])
            }
            input_containers[c] {
                c := input.review.object.spec.containers[_]
            }
            input_containers[c] {
                c := input.review.object.spec.initContainers[_]
            }
    EOF
    
  2. 次に、この制約テンプレートを拡張する新しい制約を作成します。今回は enforcementAction キーを設定しません。デフォルトの enforcementAction キーは deny に設定されています。

    kubectl create -f- <<EOF
    apiVersion: constraints.gatekeeper.sh/v1beta1
    kind: K8sPSPPrivilegedContainer
    metadata:
      name: psp-privileged-container
    spec:
      match:
        kinds:
          - apiGroups: [""]
            kinds: ["Pod"]
    EOF
    
  3. 特権の承認を宣言したコンテナをデプロイします。

    kubectl create -f- <<EOF
    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        securityContext:
          privileged: true
    EOF
    

    次のエラー メッセージが表示されます。

    Error from server ([denied by psp-privileged-container] Privileged container is not allowed:
    nginx, securityContext: {"privileged": true}): error when creating "STDIN": admission webhook "validation.gatekeeper.sh" denied the request: [denied by psp-privileged-container]
    Privileged container is not allowed: nginx, securityContext: {"privileged": true}
    
  4. クリーンアップするには、次のコマンドを実行します。

    kubectl delete k8spspprivilegedcontainer.constraints.gatekeeper.sh/psp-privileged-container
    kubectl delete constrainttemplate k8spspprivilegedcontainer
    

Gatekeeper の代替

Gatekeeper を使用すると、カスタム Pod レベルのセキュリティ ポリシーを宣言して適用できます。Kubernetes の組み込みの PodSecurity アドミッション コントローラを使用して、事前に定義された Pod レベルのセキュリティ ポリシーを適用することもできます。これらの事前定義ポリシーは、Pod セキュリティ標準で定義されているレベルと一致しています。

次のステップ

Gatekeeper は、宣言型ポリシーを使用して GKE クラスタにセキュリティを適用し、検証する非常に強力な手段を提供します。Gatekeeper はセキュリティだけでなく、管理や運用などの面でも使用できます。