Security Command Center での Policy Controller 監査違反の報告

Last reviewed 2023-04-17 UTC

このチュートリアルでは、プラットフォーム セキュリティ管理者が Security Command Center で、Kubernetes リソースのポリシー違反を、他の脆弱性やセキュリティの検出結果とともに確認して管理する方法について説明します。このチュートリアルでは、Policy Controller または Open Policy Agent(OPA)Gatekeeper を使用できます。

アーキテクチャ

Policy Controller は、セキュリティ、規制、ビジネスルールに関連するポリシーの Kubernetes クラスタ リソースによる遵守状況を確認して、監査とポリシーの適用を行います。Policy Controller は、OPA Gatekeeper オープンソース プロジェクトから構築されています。

Policy Controller と OPA Gatekeeper の監査機能を使用すると、ポリシーに基づいてリソースを定期的に評価する発見的コントロールを実装できます。問題が発見されると、このコントロールにより、ポリシーを遵守していないリソースの違反情報が作成されます。この違反情報はクラスタ内に保存され、kubectl などの Kubernetes ツールを使用して検索できます。

Security Command Center は、こうした違反情報を可視化して対応できるようにするために使用できます。Security Command Center は、Google Cloud リソース、Kubernetes リソース、ハイブリッドまたはマルチクラウド リソースに対する組織全体にわたるセキュリティおよびデータリスクを顕在化、把握、修正するためのダッシュボードと API を備えています。

Security Command Center では、危険性のあるセキュリティ リスクとポリシー違反(検出結果という)が表示されます。検出結果は、ソースから取得されます。ソースは、リスクや違反を検出して報告できる仕組みです。Security Command Center には、組み込みサービスが含まれており、サードパーティのソースと独自のソースを追加できます。

このチュートリアルと関連ソースコードでは、Policy Controller および OPA Gatekeeper ポリシー違反に対するソースと検出結果を Security Command Center で作成する方法を示しています。

次の図は、このチュートリアルで実装されるアーキテクチャを示しています。

ソース、コントローラ、同期機能を備えたアーキテクチャ。

上の図に示すように、このチュートリアルでは、コマンドライン ツールを使用して Security Command Center でソースを作成します。コントローラを Google Kubernetes Engine(GKE)クラスタにデプロイし、Policy Controller および OPA Gatekeeper の制約違反を Security Command Center の検出結果に同期します。

Google Cloud リソースのポリシー違反を同期する方法については、Config Connector と Policy Controller を使用してポリシーを遵守した Google Cloud リソースを作成する方法のチュートリアルをご覧ください。

目標

  • ポリシーとそのポリシーに違反するリソースを作成します。
  • Security Command Center にソースを作成します。
  • コマンドライン ツールを使用して、OPA Gatekeeper ポリシー違反からの検出結果を Security Command Center に作成します。
  • コントローラを GKE クラスタにデプロイし、OPA Gatekeeper ポリシー違反からの検出結果を Security Command Center に定期的に同期します。
  • ターミナルと Google Cloud コンソールに検出結果を表示します。

費用

このドキュメントでは、Google Cloud の次の課金対象のコンポーネントを使用します。

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。 新しい Google Cloud ユーザーは無料トライアルをご利用いただける場合があります。

このドキュメントに記載されているタスクの完了後、作成したリソースを削除すると、それ以上の請求は発生しません。詳細については、クリーンアップをご覧ください。

始める前に

  1. Google Cloud Console の [プロジェクト セレクタ] ページで、Google Cloud プロジェクトを選択または作成します。

    プロジェクト セレクタに移動

  2. Google Cloud プロジェクトで課金が有効になっていることを確認します

  3. このチュートリアルを最後まで進めるには、セキュリティ センター管理者編集者など、Security Command Center の組織レベルの適切な編集者ロールが必要です。組織管理者がこのロールを付与できます。
  4. Google Cloud コンソールで、「Cloud Shell をアクティブにする」をクリックします。

    Cloud Shell をアクティブにする

環境の準備

  1. Cloud Shell で、このチュートリアルで使用する Google Cloud プロジェクトを設定します。

    gcloud config set project PROJECT_ID
    

    PROJECT_ID は、実際の Google Cloud プロジェクト ID に置き換えます。このコマンドを実行すると、Cloud Shell は、プロジェクト ID を含むエクスポートされた GOOGLE_CLOUD_PROJECT という環境変数を作成します。

  2. Resource Manager、GKE、Security Command Center、Service Usage API を有効にします。

    gcloud services enable \
        cloudresourcemanager.googleapis.com \
        container.googleapis.com \
        securitycenter.googleapis.com \
        serviceusage.googleapis.com
    

GKE クラスタの作成

  1. Cloud Shell で、Workload Identity を有効にした GKE クラスタを作成します。

    gcloud container clusters create gatekeeper-securitycenter-tutorial \
        --enable-ip-alias \
        --release-channel regular \
        --workload-pool $GOOGLE_CLOUD_PROJECT.svc.id.goog \
        --zone us-central1-f
    

    このコマンドによって、us-central1-f ゾーンにクラスタが作成されます。別のゾーンやリージョンも使用できます。

  2. 自分に cluster-admin クラスタロールを付与します。

    kubectl create clusterrolebinding cluster-admin-binding \
        --clusterrole cluster-admin \
        --user $(gcloud config get-value core/account)
    

    このロールは、のちにコントローラで使用するいくつかの Kubernetes リソースを作成する際に必要になります。また、オープンソースの OPA Gatekeeper ディストリビューションをインストールする場合にも必要です。

ポリシーツールのインストール

マネージド GKE クラスタがある場合は、手順に沿って Policy Controller をインストールします。マネージド GKE クラスタがない場合は、OPA Gatekeeper ディストリビューションをインストールします。

Policy Controller

インストールの手順に沿って Policy Controller をインストールします。

監査間隔は 60 秒にします。

OPA Gatekeeper

  1. Cloud Shell で、インストールする OPA Gatekeeper バージョンを定義します。

    GATEKEEPER_VERSION=v3.10.0
    
  2. OPA Gatekeeper をインストールします。

    kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/$GATEKEEPER_VERSION/deploy/gatekeeper.yaml
    
  3. OPA Gatekeeper がインストールされたことを確認します。

    kubectl rollout status deploy gatekeeper-controller-manager \
        -n gatekeeper-system
    

    インストールが完了すると、deployment "gatekeeper-controller-manager" successfully rolled out と表示されます。

ポリシーの作成

Policy Controller と OPA Gatekeeper のポリシーは、制約テンプレート制約から構成されます。制約テンプレートには、ポリシーのロジックが含まれています。制約は、ポリシーを適用する場所を指定し、ポリシー ロジックの入力パラメータを指定します。

このセクションでは、Kubernetes Pod に対するポリシーと、ポリシーに違反する Pod を作成します。

  1. Cloud Shell で、OPA Gatekeeper ライブラリのリポジトリのクローンを作成し、リポジトリのディレクトリに移動して、既知の commit を確認します。

    git clone https://github.com/open-policy-agent/gatekeeper-library.git \
        ~/gatekeeper-library
    
    cd ~/gatekeeper-library
    
    git checkout 1da0facae99658accb73c291cb79f497fcddf641
    
  2. nginx-disallowed という Pod を default Namespace に作成します。

    kubectl apply -f library/general/allowedrepos/samples/repo-must-be-openpolicyagent/example_disallowed.yaml
    

    Pod を作成するために適用するマニフェストは、次のとおりです。

    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx-disallowed
    spec:
      containers:
        - name: nginx
          image: nginx
          resources:
            limits:
              cpu: "100m"
              memory: "30Mi"
    

    この Pod では、ポリシーによって承認されていないリポジトリのコンテナ イメージを使用します。

  3. k8sallowedrepos という制約テンプレートを作成します。

    kubectl apply -f library/general/allowedrepos/template.yaml
    

    制約テンプレートのマニフェストは、次のとおりです。

    apiVersion: templates.gatekeeper.sh/v1beta1
    kind: ConstraintTemplate
    metadata:
      name: k8sallowedrepos
      annotations:
        description: >-
          Requires container images to begin with a string from the specified list.
    spec:
      crd:
        spec:
          names:
            kind: K8sAllowedRepos
          validation:
            # Schema for the `parameters` field
            openAPIV3Schema:
              type: object
              properties:
                repos:
                  description: The list of prefixes a container image is allowed to have.
                  type: array
                  items:
                    type: string
      targets:
        - target: admission.k8s.gatekeeper.sh
          rego: |
            package k8sallowedrepos
    
            violation[{"msg": msg}] {
              container := input.review.object.spec.containers[_]
              satisfied := [good | repo = input.parameters.repos[_] ; good = startswith(container.image, repo)]
              not any(satisfied)
              msg := sprintf("container <%v> has an invalid image repo <%v>, allowed repos are %v", [container.name, container.image, input.parameters.repos])
            }
    
            violation[{"msg": msg}] {
              container := input.review.object.spec.initContainers[_]
              satisfied := [good | repo = input.parameters.repos[_] ; good = startswith(container.image, repo)]
              not any(satisfied)
              msg := sprintf("container <%v> has an invalid image repo <%v>, allowed repos are %v", [container.name, container.image, input.parameters.repos])
            }
    
  4. repo-is-openpolicyagent という制約を作成します。

    kubectl apply -f library/general/allowedrepos/samples/repo-must-be-openpolicyagent/constraint.yaml
    

    制約のマニフェストは次のとおりです。

    apiVersion: constraints.gatekeeper.sh/v1beta1
    kind: K8sAllowedRepos
    metadata:
      name: repo-is-openpolicyagent
    spec:
      match:
        kinds:
          - apiGroups: [""]
            kinds: ["Pod"]
        namespaces:
          - "default"
      parameters:
        repos:
          - "openpolicyagent/"
    

制約の監査

Policy Controller と OPA Gatekeeper の監査コントローラは、制約に対してリソースを定期的に評価します。この監査では、制約を作成する前に作成されたポリシーに違反するリソースを検出できます。

  1. Cloud Shell で、constraint カテゴリを使用してクエリを実行することで、すべての制約に対する違反を表示します。

    kubectl get constraint -o json | jq '.items[].status.violations'
    

    次のような出力が表示されます。

    [
      {
        "enforcementAction": "deny",
        "kind": "Pod",
        "message": "container <nginx> has an invalid image repo <nginx>, allowed repos are [\"openpolicyagent\"]",
        "name": "nginx-disallowed",
        "namespace": "default"
      }
    ]
    

    制約を作成する前に作成した Pod に対して違反があります。上の出力の代わりに null が見つかった場合は、制約の作成後に Policy Controller または OPA Gatekeeper の監査が実施されていません。デフォルトでは、監査は 1 分ごとに実施されます。1 分ほど待ってからもう一度試してください。

Security Command Center のソースの作成

Security Command Center では、ソースに対する検出結果を記録します。Policy Controller と OPA Gatekeeper からの検出結果のソースを作成する手順は次のとおりです。

  1. Cloud Shell で、Google サービス アカウントを作成し、そのサービス アカウント名を環境変数に保存します。

    SOURCES_ADMIN_SA=$(gcloud iam service-accounts create \
        securitycenter-sources-admin \
        --display-name "Security Command Center sources admin" \
        --format 'value(email)')
    

    この Google サービス アカウントは、Security Command Center のソースの管理に使用します。

  2. Google Cloud 組織 ID を保持する環境変数を定義します。

    ORGANIZATION_ID=$(gcloud projects get-ancestors $GOOGLE_CLOUD_PROJECT \
        --format json | jq -r '.[] | select (.type=="organization") | .id')
    
  3. セキュリティ センターのソース管理者ロールを、組織レベルでソース管理者の Google サービス アカウントに付与します。

    gcloud organizations add-iam-policy-binding $ORGANIZATION_ID \
        --member "serviceAccount:$SOURCES_ADMIN_SA" \
        --role roles/securitycenter.sourcesAdmin
    

    このロールにより、ソースの管理に必要な securitycenter.sources.* 権限が付与されます。

  4. 組織レベルで、ソース管理者の Google サービス アカウントに Service Usage ユーザーロールを付与します。

    gcloud organizations add-iam-policy-binding $ORGANIZATION_ID \
        --member "serviceAccount:$SOURCES_ADMIN_SA" \
        --role roles/serviceusage.serviceUsageConsumer
    

    このロールにより、割り当てや請求を行うために組織内でプロジェクトを使用する serviceusage.services.use 権限が付与されます。

  5. ソース管理者の Google サービス アカウントに対するサービス アカウント トークン作成者ロールを自分自身に付与します。

    gcloud iam service-accounts add-iam-policy-binding \
        $SOURCES_ADMIN_SA \
        --member "user:$(gcloud config get-value account)" \
        --role roles/iam.serviceAccountTokenCreator
    

    このロールを使用すると、自分のユーザー ID で Google サービス アカウントの権限を借用する、つまり Google サービス アカウントとして操作できます。

  6. ご利用のプラットフォーム用の gatekeeper-securitycenter コマンドライン ツールの最新バージョンをダウンロードして実行可能にします。

    VERSION=v0.4.0
    
    curl -Lo gatekeeper-securitycenter "https://github.com/GoogleCloudPlatform/gatekeeper-securitycenter/releases/download/${VERSION}/gatekeeper-securitycenter_$(uname -s)_$(uname -m)"
    
    chmod +x gatekeeper-securitycenter
    
  7. gatekeeper-securitycenter ツールを使用して、組織の Security Command Center ソースを作成します。完全なソース名を環境変数に取り込みます。

    export SOURCE_NAME=$(./gatekeeper-securitycenter sources create \
        --organization $ORGANIZATION_ID \
        --display-name "Gatekeeper" \
        --description "Reports violations from Policy Controller audits" \
        --impersonate-service-account $SOURCES_ADMIN_SA | jq -r '.name')
    

    このコマンドにより、表示名が Gatekeeper であるソースが作成されます。この表示名は Security Command Center に表示されます。別の表示名と説明を使用することもできます。

    The caller does not have permission」というエラー メッセージが表示される場合は、少し待ってから再度試してください。このエラーは、Identity and Access Management(IAM)のバインディングが有効になる前に発生する場合があります。

コマンドラインを使用した検出結果の作成

Security Command Center の検出結果は、ビルド パイプラインまたはスケジュールされたタスクの一部として gatekeeper-securitycenter ツールを使用して、Policy Controller と OPA Gatekeeper の制約違反から作成できます。

  1. Cloud Shell で、Google サービス アカウントを作成し、そのサービス アカウント名を環境変数に保存します。

    FINDINGS_EDITOR_SA=$(gcloud iam service-accounts create \
        gatekeeper-securitycenter \
        --display-name "Security Command Center Gatekeeper findings editor" \
        --format 'value(email)')
    

    この Google サービス アカウントを使用して、Security Command Center のソースに対する検出結果を作成します。

  2. ソース用の Google サービス アカウントに、セキュリティ センターの検出編集者ロールを付与します。

    ./gatekeeper-securitycenter sources add-iam-policy-binding \
        --source $SOURCE_NAME \
        --member "serviceAccount:$FINDINGS_EDITOR_SA" \
        --role roles/securitycenter.findingsEditor \
        --impersonate-service-account $SOURCES_ADMIN_SA
    

    このロールは、検出結果を作成および編集するために必要な securitycenter.findings.* 権限を付与します。このコマンドを実行すると、ソース管理者の Google サービス アカウントの権限を借用できます。

  3. 組織レベルで、検出編集者の Google サービス アカウントに Service Usage ユーザーロールを付与します。

    gcloud organizations add-iam-policy-binding $ORGANIZATION_ID \
        --member "serviceAccount:$FINDINGS_EDITOR_SA" \
        --role roles/serviceusage.serviceUsageConsumer
    
  4. 自分のユーザー ID に、検出編集者の Google サービス アカウントのサービス アカウント トークン作成者ロールを付与します。

    gcloud iam service-accounts add-iam-policy-binding \
        $FINDINGS_EDITOR_SA \
        --member "user:$(gcloud config get-value account)" \
        --role roles/iam.serviceAccountTokenCreator
    
  5. 検出結果を Security Command Center で作成するのではなく、ターミナルに出力します。

    ./gatekeeper-securitycenter findings sync --dry-run=true
    

    このコマンドはデフォルトで、現在の kubeconfig コンテキストを使用します。別の kubeconfig ファイルを使用する場合は、--kubeconfig フラグを使用します。

    出力は次のようになります。

    [
      {
        "finding_id": "0be44bcf181ef03162eed40126a500a0",
        "finding": {
          "resource_name": "https://API_SERVER/api/v1/namespaces/default/pods/nginx-disallowed",
          "state": 1,
          "category": "K8sAllowedRepos",
          "external_uri": "https://API_SERVER/apis/constraints.gatekeeper.sh/v1beta1/k8sallowedrepos/repo-is-openpolicyagent",
          "source_properties": {
            "Cluster": "",
            "ConstraintName": "repo-is-openpolicyagent",
            "ConstraintSelfLink": "https://API_SERVER/apis/constraints.gatekeeper.sh/v1beta1/k8sallowedrepos/repo-is-openpolicyagent",
            "ConstraintTemplateSelfLink": "https://API_SERVER/apis/templates.gatekeeper.sh/v1beta1/constrainttemplates/k8sallowedrepos",
            "ConstraintTemplateUID": "e35b1c39-15f7-4a7a-afae-1637b44e81b2",
            "ConstraintUID": "b904dddb-0a23-4f4f-81bb-0103de838d3e",
            "Explanation": "container \u003cnginx\u003e has an invalid image repo \u003cnginx\u003e, allowed repos are [\"openpolicyagent\"]",
            "ProjectId": "",
            "ResourceAPIGroup": "",
            "ResourceAPIVersion": "v1",
            "ResourceKind": "Pod",
            "ResourceName": "nginx-disallowed",
            "ResourceNamespace": "default",
            "ResourceSelfLink": "https://API_SERVER/api/v1/namespaces/default/pods/nginx-disallowed",
            "ResourceStatusSelfLink": "",
            "ResourceUID": "8ddd752f-e620-43ea-b966-4ae2ae507c67",
            "ScannerName": "GATEKEEPER"
          },
          "event_time": {
            "seconds": 1606287680
          }
        }
      }
    ]
    

    上の出力で、API_SERVER は、ご利用の GKE クラスタ API サーバーの IP アドレスまたはホスト名です。

    各フィールドの意味については、Security Command Center API の Finding リソースのページをご覧ください。

  6. Security Command Center に検出結果を作成します。

    ./gatekeeper-securitycenter findings sync \
        --source $SOURCE_NAME \
        --impersonate-service-account $FINDINGS_EDITOR_SA
    

    このコマンドを実行すると、検出編集者エディタの Google サービス アカウントの権限を借用できます。

    出力には create finding が含まれます。これは、gatekeeper-securitycenter コマンドライン ツールによって検出結果が作成されたことを意味します。その出力の findingID 属性には、検出結果の完全な名前が次の形式で含まれます。

    organizations/ORGANIZATION_ID/sources/SOURCE_ID/findings/FINDING_ID
    

    この出力で:

    • ORGANIZATION_ID は、Google Cloud 組織 ID です。
    • SOURCE_ID は、Security Command Center のソース ID です。
    • FINDING_ID は、検出結果 ID です。

    検出結果を確認するには、検出結果の表示セクションをご覧ください。

    The caller does not have permission」というエラー メッセージが表示される場合は、少し待ってから再度試してください。このエラーは、Identity and Access Management(IAM)のバインディングが有効になる前に発生する場合があります。

Kubernetes コントローラを使用して検出結果を作成する

gatekeeper-securitycenter は、GKE クラスタでコントローラとしてデプロイできます。コントローラは制約違反の有無を定期的にチェックし、違反ごとに Security Command Center で検出結果を作成します。

リソースが違反ではなくなると、コントローラは既存の検出結果の状態を INACTIVE に設定します。

  1. Cloud Shell で、Workload Identity IAM ポリシー バインディングを作成して、gatekeeper-securitycenter Namespace の gatekeeper-securitycenter-controller Kubernetes サービス アカウントに、結果編集者 Google サービス アカウントの権限借用を許可します。

    gcloud iam service-accounts add-iam-policy-binding \
        $FINDINGS_EDITOR_SA \
        --member "serviceAccount:$GOOGLE_CLOUD_PROJECT.svc.id.goog[gatekeeper-securitycenter/gatekeeper-securitycenter-controller]" \
        --role roles/iam.workloadIdentityUser
    

    Kubernetes サービス アカウントと Namespace は、コントローラをデプロイするときに作成します。

  2. gatekeeper-securitycenter コントローラの kpt パッケージを取得します。

    VERSION=v0.4.0
    
    kpt pkg get https://github.com/GoogleCloudPlatform/gatekeeper-securitycenter.git/manifests@$VERSION manifests
    

    このコマンドは、コントローラのリソース マニフェスト ファイルが含まれる manifests という名前のディレクトリを作成します。

    kpt は、Kubernetes リソースに対する管理、操作、カスタマイズ、適用を可能にするコマンドライン ツールです。このチュートリアルでは kpt を使用して、ご利用の環境のリソース マニフェストをカスタマイズします。

  3. Security Command Center のソース名を設定します。

    kpt fn eval manifests \
        --image gcr.io/kpt-fn/apply-setters:v0.2 -- \
        "source=$SOURCE_NAME"
    
  4. クラスタ名を設定します。

    kpt fn eval manifests \
        --image gcr.io/kpt-fn/apply-setters:v0.2 -- \
        "cluster=$(kubectl config current-context)"
    

    コントローラは、クラスタ名をソース プロパティとして Security Command Center で作成する検出結果に追加します。複数のクラスタがある場合、この名前を使用して検出結果が属するクラスタを見つけられます。

  5. コントローラの Kubernetes サービス アカウントを検出編集者の Google サービス アカウントにバインドするには、Workload Identity アノテーションを追加します。

    kpt fn eval manifests \
        --image gcr.io/kpt-fn/set-annotations:v0.1.4 \
        --match-kind ServiceAccount \
        --match-name gatekeeper-securitycenter-controller \
        --match-namespace gatekeeper-securitycenter -- \
        "iam.gke.io/gcp-service-account=$FINDINGS_EDITOR_SA"
    
  6. コントローラ パッケージを初期化します。

    kpt live init manifests
    
  7. コントローラ リソースをクラスタに適用します。

    kpt live apply manifests --reconcile-timeout 3m --output table
    

    このコマンドにより、クラスタに次のリソースが作成されます。

    • gatekeeper-securitycenter という Namespace。
    • gatekeeper-securitycenter-controller というサービス アカウント。
    • すべての API グループの全リソースに対する getlist のアクセス権を付与するクラスタロール。このロールは、コントローラがポリシー違反の原因となったリソースを取得するために必要なものです。
    • サービス アカウントにクラスタロールを付与するクラスタロールのバインディング。
    • gatekeeper-securitycenter-controller-manager という名前の Deployment。
    • Deployment の構成値を含む gatekeeper-securitycenter-config という名前の ConfigMap。

    このコマンドは、リソースの準備ができるまで待機もします。

  8. コントローラがコントローラ ログを追跡して制約違反を読み取り、Security Command Center API と通信できることを確認します。

    kubectl logs deployment/gatekeeper-securitycenter-controller-manager \
        --namespace gatekeeper-securitycenter --follow --all-containers
    

    メッセージ「syncing findings」を含むログエントリが表示されます。

    ログの追跡を停止するには、Ctrl+C を押します。

  9. コントローラが新しい検出結果を作成できることを確認するには、ポリシーと、そのポリシーに違反するリソースを作成します。Pod は、イメージ ダイジェストを使用して、コンテナ イメージを参照します。

    OPA Gatekeeper ライブラリ リポジトリ ディレクトリに移動します。

    cd ~/gatekeeper-library
    
  10. opa-disallowed という Pod を default Namespace に作成します。

    kubectl apply --namespace default -f \
        library/general/imagedigests/samples/container-image-must-have-digest/example_disallowed.yaml
    

    Pod を作成するために適用するマニフェストは、次のとおりです。

    apiVersion: v1
    kind: Pod
    metadata:
      name: opa-disallowed
    spec:
      containers:
        - name: opa
          image: openpolicyagent/opa:0.9.2
          args:
            - "run"
            - "--server"
            - "--addr=localhost:8080"
    

    この Pod 仕様は、ダイジェストではなくタグ別にコンテナ イメージを参照します。

  11. k8simagedigests という制約テンプレートを作成します。

    kubectl apply -f library/general/imagedigests/template.yaml
    

    制約テンプレートのマニフェストは、次のとおりです。

    apiVersion: templates.gatekeeper.sh/v1beta1
    kind: ConstraintTemplate
    metadata:
      name: k8simagedigests
      annotations:
        description: >-
          Requires container images to contain a digest.
    
          https://kubernetes.io/docs/concepts/containers/images/
    spec:
      crd:
        spec:
          names:
            kind: K8sImageDigests
      targets:
        - target: admission.k8s.gatekeeper.sh
          rego: |
            package k8simagedigests
    
            violation[{"msg": msg}] {
              container := input.review.object.spec.containers[_]
              satisfied := [re_match("@[a-z0-9]+([+._-][a-z0-9]+)*:[a-zA-Z0-9=_-]+", container.image)]
              not all(satisfied)
              msg := sprintf("container <%v> uses an image without a digest <%v>", [container.name, container.image])
            }
    
            violation[{"msg": msg}] {
              container := input.review.object.spec.initContainers[_]
              satisfied := [re_match("@[a-z0-9]+([+._-][a-z0-9]+)*:[a-zA-Z0-9=_-]+", container.image)]
              not all(satisfied)
              msg := sprintf("initContainer <%v> uses an image without a digest <%v>", [container.name, container.image])
            }
    
  12. container-image-must-have-digest という制約を作成します。

    kubectl apply -f library/general/imagedigests/samples/container-image-must-have-digest/constraint.yaml
    

    制約のマニフェストは次のとおりです。

    apiVersion: constraints.gatekeeper.sh/v1beta1
    kind: K8sImageDigests
    metadata:
      name: container-image-must-have-digest
    spec:
      match:
        kinds:
          - apiGroups: [""]
            kinds: ["Pod"]
        namespaces:
          - "default"
    

    この制約は、default Namespace にのみ適用されます。

  13. コントローラのログを追跡します。

    kubectl logs deployment/gatekeeper-securitycenter-controller-manager \
        --namespace gatekeeper-securitycenter --follow --all-containers
    

    数分後、create finding というメッセージを含むログエントリが表示されます。このメッセージは、gatekeeper-securitycenter コントローラが検出結果を作成したことを意味しています。

    ログの追跡を停止するには、Ctrl+C を押します。

  14. Policy Controller や OPA Gatekeeper による違反の報告がなくなったときに、コントローラが検出結果のステータスを INACTIVE に設定できることを確認するには、default Namespace で opa-disallowed という Pod を削除します。

    kubectl delete pod opa-disallowed --namespace default
    
  15. コントローラのログを追跡します。

    kubectl logs deployment/gatekeeper-securitycenter-controller-manager \
        --namespace gatekeeper-securitycenter --follow --all-containers
    

    数分後に、updating finding state というメッセージと、属性 "state":"INACTIVE" を含むログエントリが表示されます。このメッセージは、コントローラが検出結果の状態を INACTIVE に設定したことを意味します。

    ログの追跡を停止するには、Ctrl+C を押します。

検出結果の表示

Security Command Center の検出結果は、ターミナルと Google Cloud コンソールに表示できます。

  1. Cloud Shell で、gcloud CLI を使用して、組織とソースの検出結果を一覧表示します。

    gcloud scc findings list $ORGANIZATION_ID \
        --source $(basename $SOURCE_NAME) \
        --format json
    

    basename コマンドを使用して、完全なソース名から数値のソース ID を取得します。

    出力は次のようになります。

    [
      {
        "finding": {
          "category": "K8sAllowedRepos",
          "createTime": "2020-11-25T06:58:47.213Z",
          "eventTime": "2020-11-25T06:58:20Z",
          "externalUri": "https://API_SERVER/apis/constraints.gatekeeper.sh/v1beta1/k8sallowedrepos/repo-is-openpolicyagent",
          "name": "organizations/ORGANIZATION_ID/sources/SOURCE_ID/findings/FINDING_ID",
          "parent": "organizations/ORGANIZATION_ID/sources/SOURCE_ID",
          "resourceName": "https://API_SERVER/api/v1/namespaces/default/pods/nginx-disallowed",
          "securityMarks": {
            "name": "organizations/ORGANIZATION_ID/sources/SOURCE_ID/findings/FINDING_ID/securityMarks"
          },
          "sourceProperties": {
            "Cluster": "cluster-name",
            "ConstraintName": "repo-is-openpolicyagent",
            "ConstraintSelfLink": "https://API_SERVER/apis/constraints.gatekeeper.sh/v1beta1/k8sallowedrepos/repo-is-openpolicyagent",
            "ConstraintTemplateSelfLink": "https://API_SERVER/apis/templates.gatekeeper.sh/v1beta1/constrainttemplates/k8sallowedrepos",
            "ConstraintTemplateUID": "e35b1c39-15f7-4a7a-afae-1637b44e81b2",
            "ConstraintUID": "b904dddb-0a23-4f4f-81bb-0103de838d3e",
            "Explanation": "container <nginx> has an invalid image repo <nginx>, allowed repos are [\"openpolicyagent\"]",
            "ProjectId": "",
            "ResourceAPIGroup": "",
            "ResourceAPIVersion": "v1",
            "ResourceKind": "Pod",
            "ResourceName": "nginx-disallowed",
            "ResourceNamespace": "default",
            "ResourceSelfLink": "https://API_SERVER/api/v1/namespaces/default/pods/nginx-disallowed",
            "ResourceStatusSelfLink": "",
            "ResourceUID": "8ddd752f-e620-43ea-b966-4ae2ae507c67",
            "ScannerName": "GATEKEEPER"
          },
          "state": "ACTIVE"
        },
        "resource": {
          "name": "https://API_SERVER/api/v1/namespaces/default/pods/nginx-disallowed"
        }
      },
      {
        "finding": {
          "category": "K8sImageDigests",
          [...]
      }
    ]
    

    この出力で:

    • API_SERVER は、GKE クラスタ API サーバーの IP アドレスまたはホスト名です。
    • ORGANIZATION_ID は、Google Cloud 組織 ID です。
    • SOURCE_ID は、Security Command Center のソース ID です。
    • FINDING_ID は、検出結果 ID です。

    検出結果の属性の意味については、Security Command Center API の Finding リソースをご覧ください。

  2. 検出結果を Google Cloud コンソールで表示するには、Security Command Center の [検出結果] タブに移動します。

    [検出結果] に移動

  3. 組織を選択し、[選択] をクリックします。

  4. [ソースタイプ別の表示] をクリックします。

  5. [ソースタイプ] リストで [Gatekeeper] をクリックします。[Gatekeeper] が [ソースタイプ] リストに含まれていない場合は、検出結果のリストでフィルタをクリアします。

  6. 検出結果のリストで、検出結果をクリックして属性とソース プロパティを表示します。

    リソースまたはポリシーの変更が原因でリソースが違反状態になった場合、コントローラは検出結果の状態を INACTIVE に設定します。この変更が Security Command Center に表示されるまでに数分かかることがあります。

    デフォルトでは、Security Command Center には ACTIVE の検出結果が表示されます。非アクティブな検出結果を表示するには、[その他のオプション] をクリックし、[非アクティブな検出結果を含める] を選択して [OK] をクリックします。

トラブルシューティング

  • Policy Controller または OPA Gatekeeper が制約オブジェクトの status フィールドで違反を報告しない場合は、Cloud Shell を使用して監査コントローラのログを表示します。

    kubectl logs deployment/gatekeeper-audit --namespace gatekeeper-system \
        --all-containers
    
  • gatekeeper-securitycenter コントローラが Security Command Center の検出結果を作成しない場合は、コントローラ マネージャーのログで確認できます。

    kubectl logs deployment/gatekeeper-securitycenter-controller-manager \
        --namespace gatekeeper-securitycenter --all-containers
    
  • gatekeeper-securitycenter コマンドライン ツールでエラーが返される場合は、DEBUG 環境変数を true に設定してログ出力の詳細度を高めた後、gatekeeper-securitycenter コマンドを実行します。

    export DEBUG=true
    
  • gatekeeper-securitycenter コマンドライン ツールを使用して Security Command Center でソースを作成すると、次のテキストで終わるエラー メッセージが表示されることがあります。

    oauth2: cannot fetch token: 400 Bad Request
    Response: {
      "error": "invalid_grant",
      "error_description": "Bad Request"
    }
    

    この場合は、アプリケーションのデフォルト認証情報で使用する新しい認証情報を取得します。

    gcloud auth application-default login
    

    新しい認証情報を使用してソースを再度作成してみてください。

このチュートリアルでその他の問題が発生した場合は、次のドキュメントを確認することをおすすめします。

設定の自動化

将来のデプロイでは、gatekeeper-securitycenter GitHub リポジトリの手順に沿って操作することで、このチュートリアルの手順を自動化できます。

クリーンアップ

このチュートリアルで使用したリソースについて、Google Cloud アカウントに料金を請求されないようにするには、個々のリソースを削除します。

個々のリソースの削除

  1. Cloud Shell で、GKE クラスタを削除します。

    gcloud container clusters delete gatekeeper-securitycenter-tutorial \
        --zone us-central1-f --async --quiet
    
  2. gatekeeper-library ファイルを削除します。

    rm -rf ~/gatekeeper-library
    
  3. IAM ポリシーのバインディングを削除します。

    GOOGLE_CLOUD_PROJECT=$(gcloud config get-value core/project)
    
    ORGANIZATION_ID=$(gcloud projects get-ancestors $GOOGLE_CLOUD_PROJECT \
        --format json | jq -r '.[] | select (.type=="organization") | .id')
    
    SOURCE_NAME=$(./gatekeeper-securitycenter sources list \
        --organization "$ORGANIZATION_ID" \
        --impersonate-service-account "securitycenter-sources-admin@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com" \
        | jq -r ".[] | select (.display_name==\"Gatekeeper\") | .name")
    
    ./gatekeeper-securitycenter sources remove-iam-policy-binding \
        --source $SOURCE_NAME \
        --member "serviceAccount:gatekeeper-securitycenter@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com" \
        --role roles/securitycenter.findingsEditor \
        --impersonate-service-account securitycenter-sources-admin@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com
    
    gcloud iam service-accounts remove-iam-policy-binding \
        gatekeeper-securitycenter@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com \
        --member "serviceAccount:$GOOGLE_CLOUD_PROJECT.svc.id.goog[gatekeeper-securitycenter/gatekeeper-securitycenter-controller]" \
        --role roles/iam.workloadIdentityUser
    
    gcloud iam service-accounts remove-iam-policy-binding \
        gatekeeper-securitycenter@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com \
        --member "user:$(gcloud config get-value account)" \
        --role roles/iam.serviceAccountTokenCreator
    
    gcloud organizations remove-iam-policy-binding $ORGANIZATION_ID \
        --member "serviceAccount:gatekeeper-securitycenter@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com" \
        --role roles/serviceusage.serviceUsageConsumer
    
    gcloud iam service-accounts remove-iam-policy-binding \
        securitycenter-sources-admin@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com \
        --member "user:$(gcloud config get-value account)" \
        --role roles/iam.serviceAccountTokenCreator
    
    gcloud organizations remove-iam-policy-binding $ORGANIZATION_ID \
        --member "serviceAccount:securitycenter-sources-admin@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com" \
        --role roles/serviceusage.serviceUsageConsumer
    
    gcloud organizations remove-iam-policy-binding $ORGANIZATION_ID \
        --member "serviceAccount:securitycenter-sources-admin@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com" \
        --role roles/securitycenter.sourcesAdmin
    
  4. Google サービス アカウントを削除します。

    gcloud iam service-accounts delete --quiet \
        gatekeeper-securitycenter@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com
    
    gcloud iam service-accounts delete --quiet \
        securitycenter-sources-admin@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com
    

次のステップ