Security Command Center에서 정책 컨트롤러 감사 위반 보고

Last reviewed 2023-04-17 UTC

이 가이드에서는 플랫폼 보안 관리자가 Security Command Center다른 취약점 및 보안 발견 항목과 함께 Kubernetes 리소스의 정책 위반을 보고 관리하는 방법을 보여줍니다. 이 가이드에서는 정책 컨트롤러Open Policy Agent(OPA) Gatekeeper를 사용할 수 있습니다.

아키텍처

정책 관리자는 보안, 규정 또는 비즈니스 규칙과 관련된 정책을 통해 Kubernetes 클러스터 리소스의 규정 준수를 검사, 감사, 시행합니다. 정책 컨트롤러는 OPA Gatekeeper 오픈소스 프로젝트에서 빌드됩니다.

정책 컨트롤러와 OPA Gatekeeper의 감사 기능을 사용하면 정기적으로 정책에 따라 리소스를 평가하는 탐지 제어를 구현할 수 있습니다. 문제가 감지되면 제어는 정책을 준수하지 않는 리소스의 위반을 만듭니다. 이러한 위반사항은 클러스터에 저장되며 kubectl과 같은 Kubernetes 도구를 사용하여 쿼리할 수 있습니다.

이러한 위반에 대한 조치를 취할 수 있도록 표시하려면 Security Command Center를 사용하면 됩니다. Security Command Center는 Google Cloud 리소스, Kubernetes 리소스, 하이브리드 또는 멀티 클라우드 리소스의 조직에서 보안 및 데이터 위험을 표시, 파악, 해결하는 데 사용되는 대시보드와 API를 제공합니다.

Security Command Center에는 발생할 수 있는 보안 위험과 발견 항목이라고 하는 정책 위반이 표시됩니다. 위험과 위반을 감지하고 보고할 수 있는 메커니즘인 소스에서 발견 항목을 발견합니다. Security Command Center에는 기본 제공 서비스가 포함되어 있으며 타사 소스와 자체 소스를 추가할 수 있습니다.

이 가이드 및 관련 소스 코드에서는 소스와 정책 컨트롤러용 Security Command Center 및 OPA Gatekeeper 정책 위반을 만드는 방법을 보여줍니다.

다음 다이어그램에서는 이 가이드에서 구현된 아키텍처를 보여줍니다.

소스, 컨트롤러, 동기화가 포함된 아키텍처

앞의 다이어그램과 같이 이 가이드에서는 명령줄 도구를 사용하여 Security Command Center에서 소스를 만듭니다. Google Kubernetes Engine(GKE) 클러스터에 컨트롤러를 배포하여 정책 컨트롤러와 OPA Gatekeeper 제한조건 위반을 Security Command Center의 발견 항목에 동기화합니다.

Google Cloud 리소스의 정책 위반을 동기화하는 방법을 확인하려면 구성 커넥터와 정책 컨트롤러를 사용하여 정책을 준수하는 Google Cloud 리소스를 만드는 방법에 대한 가이드를 사용해 보세요.

목표

  • 정책과 정책을 위반하는 리소스를 만듭니다.
  • Security Command Center에서 소스를 만듭니다.
  • 명령줄 도구를 사용하여 Security Command Center에서 OPA Gatekeeper 정책 위반 발견 항목을 만듭니다.
  • 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, 서비스 사용량 API를 다음과 같이 사용 설정합니다.

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

GKE 클러스터 만들기

  1. Cloud Shell에서 워크로드 아이덴티티가 사용 설정된 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 클러스터가 있으면 안내를 따라 정책 컨트롤러를 설치하거나 OPA Gatekeeper 배포를 설치합니다.

정책 컨트롤러

설치 안내를 따라 정책 컨트롤러를 설치합니다.

감사 간격으로 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이 표시됩니다.

정책 만들기

정책 컨트롤러 및 OPA Gatekeeper의 정책은 제약조건 템플릿제약조건으로 구성됩니다. 제약조건 템플릿에는 정책 로직이 포함됩니다. 제약조건은 정책이 적용되는 위치와 정책 로직의 입력 매개변수를 지정합니다.

이 섹션에서는 Kubernetes 포드와 정책을 위반하는 포드에 대한 정책을 만듭니다.

  1. Cloud Shell에서 OPA Gatekeeper 라이브러리 저장소를 클론하고 저장소 디렉터리로 이동한 후 알려진 커밋을 확인합니다.

    git clone https://github.com/open-policy-agent/gatekeeper-library.git \
        ~/gatekeeper-library
    
    cd ~/gatekeeper-library
    
    git checkout 1da0facae99658accb73c291cb79f497fcddf641
    
  2. default 네임스페이스에 nginx-disallowed라는 포드를 만듭니다.

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

    다음은 포드를 만들기 위해 적용하는 매니페스트입니다.

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

    이 포드에서는 정책에서 승인하지 않은 저장소의 컨테이너 이미지를 사용합니다.

  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/"
    

제약조건 감사

정책 컨트롤러와 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"
      }
    ]
    

    제약조건을 만들기 전에 만든 포드에 대한 위반이 있습니다. 앞의 출력 대신 null이 표시되면 제약조건을 만든 후 정책 컨트롤러나 OPA Gatekeeper 감사가 실행되지 않은 것입니다. 기본적으로 감사는 1분마다 실행됩니다. 1분 후에 다시 시도합니다.

Security Command Center 소스 만들기

Security Command Center는 소스에서 발견 항목을 기록합니다. 다음 단계를 수행하여 정책 컨트롤러와 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 서비스 계정에 조직 수준의 서비스 사용량 소비자 역할을 부여합니다.

    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 서비스 계정을 가장하거나 작업할 수 있습니다.

  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이 포함된 응답을 받으면 1분 정도 기다린 후 다시 시도합니다. 이 오류는 Identity and Access Management(IAM) 바인딩이 아직 적용되지 않은 경우에 발생할 수 있습니다.

명령줄을 사용하여 발견 항목 만들기

빌드 파이프라인이나 예약 태스크의 일부로 gatekeeper-securitycenter 도구를 사용하여 정책 컨트롤러 및 OPA Gatekeeper 제약조건 위반에서 Security Command Center 발견 항목을 만들 수 있습니다.

  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 서비스 계정에 서비스 사용량 소비자 역할을 부여합니다.

    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 리소스 찾기 페이지를 참조하세요.

  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이 포함된 응답을 받으면 1분 정도 기다린 후 다시 시도합니다. 이 오류는 Identity and Access Management(IAM) 바인딩이 아직 적용되지 않은 경우에 발생할 수 있습니다.

Kubernetes 컨트롤러를 사용하여 발견 항목 만들기

gatekeeper-securitycenter를 GKE 클러스터의 컨트롤러로 배포할 수 있습니다. 이 컨트롤러는 주기적으로 제약조건 위반을 확인하고 Security Command Center에서 각 위반의 발견 항목을 만듭니다.

리소스가 규정을 준수하면 컨트롤러는 기존 발견 항목 상태를 INACTIVE로 설정합니다.

  1. Cloud Shell에서 워크로드 아이덴티티 IAM 정책 바인딩을 만들어 gatekeeper-securitycenter 네임스페이스의 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 서비스 계정과 네임스페이스를 만듭니다.

  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 서비스 계정에 바인딩하려면 워크로드 아이덴티티 주석을 추가합니다.

    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라는 네임스페이스
    • gatekeeper-securitycenter-controller라는 서비스 계정
    • 모든 API 그룹의 모든 리소스에 getlist 액세스 권한을 부여하는 클러스터 역할 컨트롤러에서 정책 위반의 원인이 되는 리소스를 검색하므로 이 역할이 필요합니다.
    • 클러스터 역할을 서비스 계정에 부여하는 클러스터 역할 바인딩
    • gatekeeper-securitycenter-controller-manager라는 배포
    • 배포 구성 값이 포함된 gatekeeper-securitycenter-config라고 하는 구성 맵입니다.

    이 명령어도 리소스가 준비될 때까지 기다립니다.

  8. 컨트롤러가 다음 컨트롤러 로그를 따라 제약조건 위반을 읽고 Security Command Center API와 통신할 수 있는지 확인합니다.

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

    syncing findings 메시지가 포함된 로그 항목이 표시됩니다.

    로그를 따르지 않으려면 Ctrl+C를 누릅니다.

  9. 컨트롤러가 새 발견 항목을 만들 수 있는지 확인하려면 정책을 위반하는 정책과 리소스를 만듭니다. 포드는 이미지 다이제스트를 사용하여 컨테이너 이미지를 참조합니다.

    OPA Gatekeeper 라이브러리 저장소 디렉터리로 이동합니다.

    cd ~/gatekeeper-library
    
  10. default 네임스페이스에 opa-disallowed라는 포드를 만듭니다.

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

    다음은 포드를 만들기 위해 적용하는 매니페스트입니다.

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

    이 포드 사양은 다이제스트가 아닌 태그별로 컨테이너 이미지를 참조합니다.

  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 네임스페이스에만 적용됩니다.

  13. 컨트롤러 로그를 따릅니다.

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

    몇 분 후 create finding 메시지가 있는 로그 항목이 표시됩니다. 이 메시지는 gatekeeper-securitycenter 컨트롤러가 발견 항목을 생성했음을 의미합니다.

    로그를 따르지 않으려면 Ctrl+C를 누릅니다.

  14. 정책 컨트롤러나 OPA Gatekeeper에서 더 이상 위반을 보고하지 않는 경우 컨트롤러에서 발견 항목 상태를 INACTIVE로 설정할 수 있는지 확인하려면 default 네임스페이스에서 opa-disallowed라는 포드를 삭제합니다.

    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" 속성이 있는 로그 항목이 표시됩니다. 이 메시지는 컨트롤러에서 발견 항목 상태를 비활성으로 설정했음을 의미합니다.

    로그를 따르지 않으려면 Ctrl+C를 누릅니다.

발견 항목 보기

터미널과 Google Cloud 콘솔에 Security Command Center 발견 항목이 표시됩니다.

  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의 발견 항목 리소스를 참조하세요.

  2. Google Cloud 콘솔에서 발견 항목을 보려면 Security Command Center의 발견 항목 탭으로 이동합니다.

    발견 항목으로 이동

  3. 조직을 선택하고 선택을 클릭합니다.

  4. 보기 기준 소스 유형을 클릭합니다.

  5. 소스 유형 목록에서 Gatekeeper를 클릭합니다. Gatekeeper소스 유형 목록에 없으면 발견 항목 목록에서 필터를 지웁니다.

  6. 발견 항목 목록에서 발견 항목을 클릭하여 발견 항목 속성과 소스 속성을 확인합니다.

    리소스나 정책의 변경사항으로 인해 리소스에서 더 이상 위반이 발생하지 않으면 컨트롤러는 발견 항목 상태를 비활성으로 설정합니다. Security Command Center에 이러한 변경사항이 표시되는 데 몇 분 정도 걸릴 수 있습니다.

    기본적으로 Security Command Center에는 활성 상태의 발견 항목이 표시됩니다. 비활성 발견 항목을 보려면 추가 옵션을 클릭하고 비활성 발견 항목 포함을 선택한 후 확인을 클릭합니다.

문제 해결

  • 정책 컨트롤러나 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 명령줄 도구에서 오류를 보고하는 경우 gatekeeper-securitycenter 명령어를 실행하기 전에 DEBUG 환경 변수를 true로 설정하면 로그 출력의 세부정보 수준을 높일 수 있습니다.

    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
    

다음 단계