Gatekeeper を使用した Pod セキュリティ ポリシーの適用


このページでは、Pod レベルのセキュリティ管理を Google Kubernetes Engine(GKE)クラスタに適用する方法について説明します。

概要

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

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

Gatekeeper は、Kubernetes PodSecurityPolicies と同じ機能のほかに、次のことも可能です。

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

Kubernetes オープンソース ソフトウェア(OSS)では、Kubernetes PodSecurityPolicy の非推奨を進めているところで、使用することは推奨されていません。

コンセプト

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 構成の例も含まれています。

始める前に

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

次のいずれかの方法で gcloud のデフォルトの設定を指定します。

  • gcloud init。デフォルトの設定全般を確認する場合に使用します。
  • gcloud config。プロジェクト ID、ゾーン、リージョンを個別に設定する場合に使用します。

gcloud init の使用

エラー One of [--zone, --region] must be supplied: Please specify location を受信した場合は、このセクションの内容を実施します。

  1. gcloud init を実行して、次の操作を行います。

    gcloud init

    リモート サーバーで SSH を使用している場合は、--console-only フラグを指定して、コマンドがブラウザを起動しないようにします。

    gcloud init --console-only
  2. 手順に従って gcloud を承認し、Google Cloud アカウントを使用します。
  3. 新しい構成を作成するか、既存の構成を選択します。
  4. Google Cloud プロジェクトを選択します。
  5. ゾーンクラスタの場合はデフォルトの Compute Engine ゾーン、リージョン クラスタまたは Autopilot クラスタの場合はデフォルトの Compute Engine リージョンを選択します。

gcloud config の使用

  • デフォルトのプロジェクト ID を設定します。
    gcloud config set project PROJECT_ID
  • ゾーンクラスタを使用する場合は、デフォルトのコンピューティング ゾーンを設定します。
    gcloud config set compute/zone COMPUTE_ZONE
  • Autopilot クラスタまたはリージョン クラスタを使用する場合は、デフォルトのコンピューティング リージョンを設定します。
    gcloud config set compute/region COMPUTE_REGION
  • gcloud を最新バージョンに更新します。
    gcloud components update

クラスタでの Gatekeeper の有効化

Gatekeeper は、次のいずれかの方法で GKE クラスタで有効にできます。これらの方法は、同じクラスタで共存できないため、いずれか 1 つのみを選択してください。

Anthos Config Management には、Gatekeeper のオープンソース プロジェクト上に構築されたポリシー エンジンである Policy Controller が用意されています。Google では、Anthos Config Management の使用を推奨しています。これを使用すると、Policy as Code、マルチクラスタのサポート、Cloud Logging との統合、構成の同期化機能など、ポリシーを大規模に適用する際によく起きる問題が解決されるためです。

クラスタで Policy Controller を有効にするには、Anthos Config Management のインストール ガイドに沿って操作してください。

Google Cloud Marketplace の使用

Gatekeeper は、Google Cloud Marketplace の Kubernetes アプリケーションとしても利用できます。Gatekeeper は既存のクラスタ、または新しく作成されたクラスタにインストールできます。

Cloud Marketplace から Gatekeeper をデプロイするには、Google Cloud Marketplace からのアプリケーションのデプロイをご覧ください。

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

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

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

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

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

ポリシーのテスト

既存のクラスタに新しいポリシーを導入すると、動作を悪化させることがあります(たとえば、既存のワークロードを制限する場合など)。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 は、宣言型ポリシーを使用して GKE クラスタにセキュリティを適用し、検証する非常に強力な手段を提供します。Gatekeeper はセキュリティだけでなく、管理や運用などの面でも使用できます。