정책을 준수하는 Google Cloud 리소스 만들기


이 튜토리얼에서는 플랫폼 관리자가 정책 컨트롤러 정책을 사용하여 구성 커넥터를 통해 Google Cloud 리소스를 만드는 방법을 관리하는 방법을 보여줍니다.

이 튜토리얼의 지침은 Kubernetes 또는 Google Kubernetes Engine(GKE)에 대한 기본 지식을 갖춘 개발자를 대상으로 작성되었습니다. 이 튜토리얼에서는 Cloud Storage 버킷에 허용되는 위치를 제한하는 정책을 정의합니다.

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

구성 커넥터는 Kubernetes 커스텀 리소스Google Cloud 리소스를 설명하여 수명 주기를 만들고 관리합니다. Google Cloud 리소스를 만들려면 구성 커넥터가 관리하는 네임스페이스에 Kubernetes 리소스를 만듭니다. 다음 예시에서는 구성 커넥터를 사용하여 Cloud Storage 버킷을 설명하는 방법을 보여줍니다.

apiVersion: storage.cnrm.cloud.google.com/v1beta1
kind: StorageBucket
metadata:
  name: my-bucket
spec:
  location: us-east1

구성 커넥터를 사용하여 Google Cloud 리소스를 관리하면 Google Kubernetes Engine(GKE) Enterprise 버전 클러스터에서 리소스를 만들 때 정책 컨트롤러 정책을 해당 리소스에 적용할 수 있습니다. 이러한 정책을 사용하면 정책을 위반하는 방식으로 리소스를 만들거나 수정하는 작업을 방지하거나 보고할 수 있습니다. 예를 들어 Cloud Storage 버킷의 위치를 제한하는 정책을 적용할 수 있습니다.

Kubernetes 리소스 모델(KRM)을 기반으로 하는 접근 방식을 사용하면 일관된 도구 및 워크플로 모음을 사용해서 Kubernetes 및 Google Cloud 리소스를 모두 관리할 수 있습니다. 이 튜토리얼에서는 다음 작업을 완료하는 방법을 보여줍니다.

  • Google Cloud 리소스를 관리하는 정책을 정의합니다.
  • 개발자와 관리자가 정책을 위반하는 Google Cloud 리소스를 만들지 못하도록 하는 제어를 구현합니다.
  • 구성 커넥터 외부에서 리소스를 만든 경우에도 기존 Google Cloud 리소스를 정책에 대해 감사하는 제어를 구현합니다.
  • 리소스 정의를 만들고 업데이트할 때 개발자와 관리자에게 빠른 피드백을 제공합니다.
  • Kubernetes 클러스터에 정의를 적용하기 전에 정책에 대한 Google Cloud 리소스 정의의 유효성을 검사합니다.

목표

  • 구성 커넥터 부가기능이 포함된 Google Kubernetes Engine(GKE) Enterprise 버전을 만듭니다.
  • 정책 컨트롤러를 설치합니다.
  • 정책을 만들어 허용된 Cloud Storage 버킷 위치를 제한합니다.
  • 정책이 허용되지 않은 위치에 Cloud Storage 버킷을 생성하지 않게 하는지 확인하기
  • 개발 중에 Cloud Storage 버킷 정의의 정책 준수를 평가합니다.
  • 기존 Cloud Storage 버킷의 정책 준수를 감사하기

비용

이 문서에서는 비용이 청구될 수 있는 다음과 같은 Google Cloud 구성요소를 사용합니다.

프로젝트 사용량을 기준으로 예상 비용을 산출하려면 가격 계산기를 사용하세요. Google Cloud를 처음 사용하는 사용자는 무료 체험판을 사용할 수 있습니다.

시작하기 전에

  1. Google Cloud Console의 프로젝트 선택기 페이지에서 Google Cloud 프로젝트를 선택하거나 만듭니다.

    프로젝트 선택기로 이동

  2. Google Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다.

  3. Google Cloud 콘솔에서 Cloud Shell을 활성화합니다.

    Cloud Shell 활성화

  4. Cloud Shell에서 이 튜토리얼에 사용할 Google Cloud 프로젝트를 설정합니다.

    gcloud config set project PROJECT_ID
    

    PROJECT_ID를 프로젝트의 Google Cloud 프로젝트 ID로 바꿉니다. 이 명령어를 실행하면 Cloud Shell에서 프로젝트 ID가 포함된 GOOGLE_CLOUD_PROJECT라고 하는 내보낸 환경 변수를 만듭니다. Cloud Shell을 사용하지 않는 경우 다음 명령어를 사용하여 환경 변수를 만들 수 있습니다.

    export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value core/project)
    
  5. GKE API를 사용 설정합니다.

    gcloud services enable container.googleapis.com
    
  6. 이 튜토리얼용으로 만든 파일을 저장할 디렉터리를 만듭니다.

    mkdir -p ~/cnrm-gatekeeper-tutorial
    
  7. 만든 디렉터리로 이동합니다.

    cd ~/cnrm-gatekeeper-tutorial
    

GKE 클러스터 만들기

  1. Cloud Shell에서 구성 커넥터 부가기능워크로드 아이덴티티가 포함된 GKE 클러스터를 만듭니다.

    gcloud container clusters create CLUSTER_NAME \
      --addons ConfigConnector \
      --enable-ip-alias \
      --num-nodes 4 \
      --release-channel regular \
      --scopes cloud-platform \
      --workload-pool $GOOGLE_CLOUD_PROJECT.svc.id.goog \
      --zone ZONE
    

    다음을 바꿉니다.

    • CLUSTER_NAME: 이 프로젝트에 사용할 클러스터의 이름입니다(예: cnrm-gatekeeper-tutorial).
    • ZONE: 위치와 가까운 Compute Engine 영역입니다(예: asia-southeast1-b).

    구성 커넥터 부가기능은 GKE 클러스터에 있는 Google Cloud 리소스커스텀 리소스 정의(CRD)를 설치합니다.

  2. (선택사항) 고유한 환경에서 비공개 클러스터를 사용하는 경우 GKE 클러스터 제어 영역이 정책 컨트롤러 웹훅에 연결하도록 허용하는 방화벽 규칙을 추가합니다.

    gcloud compute firewall-rules create allow-cluster-control-plane-tcp-8443 \
      --allow tcp:8443 \
      --network default \
      --source-ranges CONTROL_PLANE_CIDR \
      --target-tags NODE_TAG
    

    다음을 바꿉니다.

    • CONTROL_PLANE_CIDR: GKE 클러스터 제어 영역의 IP 범위(예: 172.16.0.16/28)입니다.
    • NODE_TAG: GKE 클러스터의 모든 노드에 적용되는 태그입니다.

    이 선택적 방화벽 규칙은 클러스터가 비공개 노드를 사용할 때 정책 컨트롤러 웹훅이 작동하도록 하는 데 필요합니다.

Config Connector 설정

구성 커넥터를 설치하는 Google Cloud 프로젝트를 호스트 프로젝트라고 합니다. 구성 커넥터를 사용하여 리소스를 관리하는 프로젝트를 관리 프로젝트라고 합니다. 이 튜토리얼에서는 구성 커넥터를 사용하여 호스트 프로젝트와 관리형 프로젝트가 동일한 프로젝트가 되도록 GKE 클러스터와 동일한 프로젝트에 Google Cloud 리소스를 만듭니다.

  1. Cloud Shell에서 구성 커넥터의 Google 서비스 계정을 만듭니다.

    gcloud iam service-accounts create SERVICE_ACCOUNT_NAME \
      --display-name "Config Connector Gatekeeper tutorial"
    

    SERVICE_ACCOUNT_NAME을 이 서비스 계정에 사용할 이름, 예를 들어 cnrm-gatekeeper-tutorial로 바꿉니다. 구성 커넥터는 이 Google 서비스 계정을 사용하여 관리형 프로젝트에 리소스를 만듭니다.

  2. Google 서비스 계정에 스토리지 관리자 역할을 부여합니다.

    gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT \
      --member "serviceAccount:SERVICE_ACCOUNT_NAME@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com" \
      --role roles/storage.admin
    

    이 튜토리얼에서는 구성 커넥터를 사용하여 Cloud Storage 버킷을 만들기 때문에 스토리지 관리자 역할을 사용합니다. 사용자 환경에서 구성 커넥터용으로 만들려는 Google Cloud 리소스를 관리하는 데 필요한 역할을 부여합니다. 사전 정의된 역할에 대한 자세한 내용은 IAM 문서의 역할 이해를 참조하세요.

  3. 이 튜토리얼에서 만든 구성 커넥터 리소스의 Kubernetes 네임스페이스를 만듭니다.

    kubectl create namespace NAMESPACE
    

    NAMESPACE를 튜토리얼에 사용할 Kubernetes 네임스페이스로 바꿉니다(예: tutorial).

  4. Google Cloud 리소스(관리형 프로젝트)를 만드는 데 사용할 프로젝트 구성 커넥터를 지정하기 위해 네임스페이스에 주석을 추가합니다.

    kubectl annotate namespace NAMESPACE \
        cnrm.cloud.google.com/project-id=$GOOGLE_CLOUD_PROJECT
    
  5. Kubernetes 네임스페이스에 대해 구성 커넥터를 사용 설정하고 만든 Google 서비스 계정과 연결하는 ConfigConnectorContext 리소스를 만듭니다.

    cat << EOF | kubectl apply -f -
    apiVersion: core.cnrm.cloud.google.com/v1beta1
    kind: ConfigConnectorContext
    metadata:
      name: configconnectorcontext.core.cnrm.cloud.google.com
      namespace: NAMESPACE
    spec:
      googleServiceAccount: SERVICE_ACCOUNT_NAME@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com
    EOF
    

    네임스페이스에서 구성 커넥터 리소스를 관리하도록 하기 위해 ConfigConnectorContext 리소스를 만들 때 구성 커넥터는 cnrm-system 네임스페이스에 Kubernetes 서비스 계정StatefulSet를 만듭니다.

  6. 네임스페이스의 구성 커넥터 컨트롤러 포드를 기다립니다.

    kubectl wait --namespace cnrm-system --for=condition=Ready pod \
      -l cnrm.cloud.google.com/component=cnrm-controller-manager,cnrm.cloud.google.com/scoped-namespace=NAMESPACE
    

    포드가 준비되면 Cloud Shell 메시지가 표시됩니다. error: no matching resources found 메시지가 표시되면 1분 동안 기다린 후 다시 시도하세요.

  7. IAM 정책 binding을 만들어 구성 커넥터 Kubernetes 서비스 계정을 Google 서비스 계정에 결합합니다.

    gcloud iam service-accounts add-iam-policy-binding \
      SERVICE_ACCOUNT_NAME@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com \
      --member "serviceAccount:$GOOGLE_CLOUD_PROJECT.svc.id.goog[cnrm-system/cnrm-controller-manager-NAMESPACE]" \
      --role roles/iam.workloadIdentityUser
    

    이 binding을 사용하면 cnrm-system 네임스페이스의 cnrm-controller-manager-NAMESPACE Kubernetes 서비스 계정을 생성된 Google 서비스 계정으로 사용할 수 있습니다.

정책 컨트롤러 설치

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

감사 간격으로 60초를 사용합니다.

구성 커넥터를 사용하여 Google Cloud 리소스 만들기

  1. Cloud Shell에서 us-central1 리전의 Cloud Storage 버킷을 나타내는 구성 커넥터 매니페스트를 만듭니다.

    cat << EOF > tutorial-storagebucket-us-central1.yaml
    apiVersion: storage.cnrm.cloud.google.com/v1beta1
    kind: StorageBucket
    metadata:
      name: tutorial-us-central1-$GOOGLE_CLOUD_PROJECT
      namespace: NAMESPACE
    spec:
      location: us-central1
      uniformBucketLevelAccess: true
    EOF
    
  2. Cloud Storage 버킷을 만들려면 매니페스트를 적용하세요.

    kubectl apply -f tutorial-storagebucket-us-central1.yaml
    
  3. 구성 커넥터가 Cloud Storage 버킷을 생성했는지 확인합니다.

    gsutil ls | grep tutorial
    

    출력은 다음과 비슷합니다.

    gs://tutorial-us-central1-PROJECT_ID/
    

    이 출력에는 Google Cloud 프로젝트 ID인 PROJECT_ID가 포함됩니다.

    이 출력이 표시되지 않으면 잠시 기다렸다가 단계를 다시 수행합니다.

정책 만들기

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

  1. Cloud Shell에서 Cloud Storage 버킷 위치를 제한하는 제약조건 템플릿을 만듭니다.

    cat << EOF > tutorial-storagebucket-location-template.yaml
    apiVersion: templates.gatekeeper.sh/v1beta1
    kind: ConstraintTemplate
    metadata:
      name: gcpstoragelocationconstraintv1
    spec:
      crd:
        spec:
          names:
            kind: GCPStorageLocationConstraintV1
          validation:
            openAPIV3Schema:
              properties:
                locations:
                  type: array
                  items:
                    type: string
                exemptions:
                  type: array
                  items:
                    type: string
      targets:
      - target: admission.k8s.gatekeeper.sh
        rego: |
          package gcpstoragelocationconstraintv1
    
          allowedLocation(reviewLocation) {
              locations := input.parameters.locations
              satisfied := [ good | location = locations[_]
                                    good = lower(location) == lower(reviewLocation)]
              any(satisfied)
          }
    
          exempt(reviewName) {
              input.parameters.exemptions[_] == reviewName
          }
    
          violation[{"msg": msg}] {
              bucketName := input.review.object.metadata.name
              bucketLocation := input.review.object.spec.location
              not allowedLocation(bucketLocation)
              not exempt(bucketName)
              msg := sprintf("Cloud Storage bucket <%v> uses a disallowed location <%v>, allowed locations are %v", [bucketName, bucketLocation, input.parameters.locations])
          }
    
          violation[{"msg": msg}] {
              not input.parameters.locations
              bucketName := input.review.object.metadata.name
              msg := sprintf("No permitted locations provided in constraint for Cloud Storage bucket <%v>", [bucketName])
          }
    EOF
    
  2. 템플릿을 적용하여 Cloud Storage 버킷을 만듭니다.

    kubectl apply -f tutorial-storagebucket-location-template.yaml
    
  3. 싱가포르와 자카르타 리전(asia-southeast1asia-southeast2)의 버킷만 허용하는 제약 조건을 만듭니다. 이전에 만든 네임스페이스에 제약조건이 적용됩니다. Cloud Build의 기본 Cloud Storage 버킷은 제외됩니다.

    cat << EOF > tutorial-storagebucket-location-constraint.yaml
    apiVersion: constraints.gatekeeper.sh/v1beta1
    kind: GCPStorageLocationConstraintV1
    metadata:
      name: singapore-and-jakarta-only
    spec:
      enforcementAction: deny
      match:
        kinds:
        - apiGroups:
          - storage.cnrm.cloud.google.com
          kinds:
          - StorageBucket
        namespaces:
        - NAMESPACE
      parameters:
        locations:
        - asia-southeast1
        - asia-southeast2
        exemptions:
        - ${GOOGLE_CLOUD_PROJECT}_cloudbuild
    EOF
    
  4. 버킷이 존재할 수 있는 영역을 제한하려면 제약조건을 적용하세요.

    kubectl apply -f tutorial-storagebucket-location-constraint.yaml
    

정책 확인

  1. 허용되지 않는 위치(us-west1)의 Cloud Storage 버킷을 나타내는 매니페스트를 만듭니다.

    cat << EOF > tutorial-storagebucket-us-west1.yaml
    apiVersion: storage.cnrm.cloud.google.com/v1beta1
    kind: StorageBucket
    metadata:
      name: tutorial-us-west1-$GOOGLE_CLOUD_PROJECT
      namespace: NAMESPACE
    spec:
      location: us-west1
      uniformBucketLevelAccess: true
    EOF
    
  2. Cloud Storage 버킷을 만들려면 매니페스트를 적용하세요.

    kubectl apply -f tutorial-storagebucket-us-west1.yaml
    

    출력은 다음과 비슷합니다.

    Error from server ([singapore-and-jakarta-only] Cloud Storage bucket
    <tutorial-us-west1-PROJECT_ID> uses a disallowed location
    <us-west1>, allowed locations are ["asia-southeast1",
    "asia-southeast2"]): error when creating
    "tutorial-storagebucket-us-west1.yaml": admission webhook
    "validation.gatekeeper.sh" denied the request: [singapore-and-jakarta-only]
    Cloud Storage bucket <tutorial-us-west1-PROJECT_ID> uses a
    disallowed location <us-west1>, allowed locations are
    ["asia-southeast1", "asia-southeast2"]
    
  3. (선택사항) Cloud 감사 로그에서 요청 거부를 위한 결정에 대한 기록을 확인할 수 있습니다. 다음과 같이 프로젝트의 관리자 활동 로그를 쿼리합니다.

    gcloud logging read --limit=1 \
        "logName=\"projects/$GOOGLE_CLOUD_PROJECT/logs/cloudaudit.googleapis.com%2Factivity\""'
        resource.type="k8s_cluster"
        resource.labels.cluster_name="CLUSTER_NAME"
        resource.labels.location="ZONE"
        protoPayload.authenticationInfo.principalEmail!~"system:serviceaccount:cnrm-system:.*"
        protoPayload.methodName:"com.google.cloud.cnrm."
        protoPayload.status.code=7'
    

    출력은 다음과 비슷합니다.

    insertId: 3c6940bb-de14-4d18-ac4d-9a6becc70828
    labels:
      authorization.k8s.io/decision: allow
      authorization.k8s.io/reason: ''
      mutation.webhook.admission.k8s.io/round_0_index_0: '{"configuration":"mutating-webhook.cnrm.cloud.google.com","webhook":"container-annotation-handler.cnrm.cloud.google.com","mutated":true}'
      mutation.webhook.admission.k8s.io/round_0_index_1: '{"configuration":"mutating-webhook.cnrm.cloud.google.com","webhook":"management-conflict-annotation-defaulter.cnrm.cloud.google.com","mutated":true}'
    logName: projects/PROJECT_ID/logs/cloudaudit.googleapis.com%2Factivity
    operation:
      first: true
      id: 3c6940bb-de14-4d18-ac4d-9a6becc70828
      last: true
      producer: k8s.io
    protoPayload:
      '@type': type.googleapis.com/google.cloud.audit.AuditLog
      authenticationInfo:
        principalEmail: user@example.com
      authorizationInfo:
      - permission: com.google.cloud.cnrm.storage.v1beta1.storagebuckets.create
        resource: storage.cnrm.cloud.google.com/v1beta1/namespaces/NAMESPACE/storagebuckets/tutorial-us-west1-PROJECT_ID
      methodName: com.google.cloud.cnrm.storage.v1beta1.storagebuckets.create
      requestMetadata:
        callerIp: 203.0.113.1
        callerSuppliedUserAgent: kubectl/v1.21.1 (linux/amd64) kubernetes/5e58841
      resourceName: storage.cnrm.cloud.google.com/v1beta1/namespaces/NAMESPACE/storagebuckets/tutorial-us-west1-PROJECT_ID
      serviceName: k8s.io
      status:
        code: 7
        message: Forbidden
    receiveTimestamp: '2021-05-21T06:56:24.940264678Z'
    resource:
      labels:
        cluster_name: CLUSTER_NAME
        location: CLUSTER_ZONE
        project_id: PROJECT_ID
      type: k8s_cluster
    timestamp: '2021-05-21T06:56:09.060635Z'
    

    methodName 필드는 시도된 작업을 나타내고 resourceName은 구성 커넥터 리소스의 전체 이름을 표시하며 status 섹션은 요청이 실패했음을 나타냅니다(오류 코드 7Forbidden 메시지).

  4. 허용된 위치(asia-southeast1)의 Cloud Storage 버킷을 나타내는 매니페스트를 만듭니다.

    cat << EOF > tutorial-storagebucket-asia-southeast1.yaml
    apiVersion: storage.cnrm.cloud.google.com/v1beta1
    kind: StorageBucket
    metadata:
      name: tutorial-asia-southeast1-$GOOGLE_CLOUD_PROJECT
      namespace: NAMESPACE
    spec:
      location: asia-southeast1
      uniformBucketLevelAccess: true
    EOF
    
  5. Cloud Storage 버킷을 만들려면 매니페스트를 적용하세요.

    kubectl apply -f tutorial-storagebucket-asia-southeast1.yaml
    

    출력은 다음과 비슷합니다.

    storagebucket.storage.cnrm.cloud.google.com/tutorial-asia-southeast1-PROJECT_ID created
    

    이 출력에는 Google Cloud 프로젝트 ID인 PROJECT_ID가 포함됩니다.

  6. 구성 커넥터가 Cloud Storage 버킷을 생성했는지 확인합니다.

    gsutil ls | grep tutorial
    

    출력은 다음과 비슷합니다.

    gs://tutorial-asia-southeast1-PROJECT_ID/
    gs://tutorial-us-central1-PROJECT_ID/
    

    이 출력이 표시되지 않으면 잠시 기다렸다가 이 단계를 다시 수행합니다.

감사 제약조건

정책 컨트롤러의 감사 컨트롤러는 제약조건을 기준으로 리소스를 정기적으로 평가합니다. 컨트롤러는 제약조건 이전에 생성된 리소스와 구성 커넥터 외부에서 생성된 리소스의 정책 위반을 감지합니다.

  1. Cloud Shell에서 GCPStorageLocationConstraintV1 제약조건 템플릿을 사용하는 모든 제약조건에 대한 위반을 확인합니다.

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

    출력은 다음과 비슷합니다.

    [
      {
        "enforcementAction": "deny",
        "kind": "StorageBucket",
        "message": "Cloud Storage bucket <tutorial-us-central1-PROJECT_ID>
        uses a disallowed location <us-central1>, allowed locations are
        \"asia-southeast1\", \"asia-southeast2\"",
        "name": "tutorial-us-central1-PROJECT_ID",
        "namespace": "NAMESPACE"
      }
    ]
    

    제약조건을 만들기 전에 us-central1에서 만든 Cloud Storage 버킷을 확인합니다.

개발 중 리소스 유효성 검사

개발 및 지속적 통합 빌드 중에 GKE 클러스터에 해당 리소스를 적용하기 전에 제약조건을 기준으로 리소스의 유효성을 검사하는 것이 좋습니다. 유효성 검사는 빠른 피드백을 제공하며 리소스 및 제약조건 관련 문제를 조기에 발견할 수 있도록 합니다. 이 단계에서는 kpt를 사용하여 리소스를 검증하는 방법을 보여줍니다. kpt 명령줄 도구를 사용하면 Kubernetes 리소스 매니페스트를 관리하고 적용할 수 있습니다.

  1. Cloud Shell에서 kpt를 사용하여 gatekeeper KRM 함수를 실행합니다.

    kpt fn eval . --image=gcr.io/kpt-fn/gatekeeper:v0.2 --truncate-output=false
    

    KRM 함수는 로컬 파일 시스템에 YAML 파일로 저장된 Kubernetes 리소스를 변형 또는 검사할 수 있는 프로그램입니다. gatekeeper KRM 함수는 Gatekeeper 정책에 대해 구성 커넥터 Cloud Storage 버킷 리소스를 검사합니다. gatekeeper KRM 함수는 Artifact Registry에서 사용할 수 있는 컨테이너 이미지로 패키징됩니다.

    이 함수는 us-central1us-west1 리전에 있는 Cloud Storage 버킷의 매니페스트 파일의 제약조건 위반을 보고합니다.

    출력은 다음과 비슷합니다.

    [RUNNING] "gcr.io/kpt-fn/gatekeeper:v0.2"
    [FAIL] "gcr.io/kpt-fn/gatekeeper:v0.2"
      Results:
        [ERROR] Cloud Storage bucket <tutorial-us-central1-PROJECT_ID> uses a disallowed location <us-central1>, allowed locations are ["asia-southeast1", "asia-southeast2"] violatedConstraint: singapore-and-jakarta-only in object "storage.cnrm.cloud.google.com/v1beta1/StorageBucket/tutorial/tutorial-us-central1-GOOGLE_CLOUD_PROJECT" in file "tutorial-storagebucket-us-central1.yaml"
        [ERROR] Cloud Storage bucket <tutorial-us-west1-PROJECT_ID> uses a disallowed location <us-west1>, allowed locations are ["asia-southeast1", "asia-southeast2"] violatedConstraint: singapore-and-jakarta-only in object "storage.cnrm.cloud.google.com/v1beta1/StorageBucket/tutorial/tutorial-us-west1-GOOGLE_CLOUD_PROJECT" in file "tutorial-storagebucket-us-west1.yaml"
      Stderr:
        "[error] storage.cnrm.cloud.google.com/v1beta1/StorageBucket/test/tutorial-us-central1-PROJECT_ID : Cloud Storage bucket <tutorial-us-central1-PROJECT_ID> uses a disallowed location <us-central1>, allowed locations are [\"asia-southeast1\", \"asia-southeast2\"]"
        "violatedConstraint: singapore-and-jakarta-only"
        ""
        "[error] storage.cnrm.cloud.google.com/v1beta1/StorageBucket/test/tutorial-us-west1-PROJECT_IDT : Cloud Storage bucket <tutorial-us-west1-PROJECT_IDgt; uses a disallowed location <us-west1>, allowed locations are [\"asia-southeast1\", \"asia-southeast2\"]"
        "violatedConstraint: singapore-and-jakarta-only"
        ""
      Exit code: 1
    

구성 커넥터 외부에서 생성된 리소스 유효성 검사

리소스를 내보내 구성 커넥터 외부에서 생성된 Google Cloud 리소스의 유효성을 확인할 수 있습니다. 리소스를 내보낸 후 다음 옵션 중 하나를 사용하여 내보낸 리소스에 대해 정책 컨트롤러 정책을 평가합니다.

  • gatekeeper KRM 함수를 사용하여 리소스 검사

  • 리소스를 구성 커넥터로 가져옵니다.

리소스를 내보내려면 Cloud 애셋 인벤토리를 사용합니다.

  1. Cloud Shell에서 Cloud Asset API를 사용 설정합니다.

    gcloud services enable cloudasset.googleapis.com
    
  2. us-central1us-west1에서 Cloud Storage 버킷에 대해 Kubernetes 리소스 매니페스트 파일을 삭제합니다.

    rm tutorial-storagebucket-us-*.yaml
    
  3. 현재 프로젝트의 모든 Cloud Storage 리소스를 내보내고 출력을 export.yaml이라는 파일에 저장합니다.

    gcloud beta resource-config bulk-export \
      --project $GOOGLE_CLOUD_PROJECT \
      --resource-format krm \
      --resource-types StorageBucket > export.yaml
    

    출력은 다음과 비슷합니다.

    Exporting resource configurations to stdout...
    
    Export complete.
    
  4. KRM 함수를 함께 연결하여 kpt 파이프라인을 만듭니다. 이 파이프라인은 현재 디렉터리의 리소스를 Cloud Storage 버킷 위치 정책에 대해 검사합니다.

    kpt fn source . \
      | kpt fn eval - --image=gcr.io/kpt-fn/set-namespace:v0.1 -- namespace=NAMESPACE \
      | kpt fn eval - --image=gcr.io/kpt-fn/gatekeeper:v0.2 --truncate-output=false
    

    내보낸 리소스에는 namespace 메타데이터 속성 값이 없습니다. 이 파이프라인은 set-namespace라는 KRM 함수를 사용하여 모든 리소스의 namespace 값을 설정합니다.

    출력은 다음과 비슷하며 내보낸 리소스에 대한 위반사항을 보여줍니다.

    [RUNNING] "gcr.io/kpt-fn/set-namespace:v0.1"
    [PASS] "gcr.io/kpt-fn/set-namespace:v0.1"
    [RUNNING] "gcr.io/kpt-fn/gatekeeper:v0.2"
    [FAIL] "gcr.io/kpt-fn/gatekeeper:v0.2"
      Results:
        [ERROR] Cloud Storage bucket <tutorial-us-central1-PROJECT_ID> uses a disallowed location <us-central1>, allowed locations are ["asia-southeast1", "asia-southeast2"] violatedConstraint: singapore-and-jakarta-only in object "storage.cnrm.cloud.google.com/v1beta1/StorageBucket/tutorial/tutorial-us-central1-GOOGLE_CLOUD_PROJECT" in file "export.yaml"
      Stderr:
        "[error] storage.cnrm.cloud.google.com/v1beta1/StorageBucket/test/tutorial-us-central1-PROJECT_ID : Cloud Storage bucket <tutorial-us-central1-PROJECT_ID> uses a disallowed location <us-central1>, allowed locations are [\"asia-southeast1\", \"asia-southeast2\"]"
        "violatedConstraint: singapore-and-jakarta-only"
        ""
      Exit code: 1
    

    이 튜토리얼을 수행하기 전에 만든 Cloud Storage 버킷이 Google Cloud 프로젝트에 포함되어 있고 해당 위치가 제약조건을 위반하면 이전에 만든 버킷이 출력에 표시됩니다.

수고하셨습니다. Cloud Storage 버킷의 허용 위치를 관리하는 정책을 성공적으로 설정했습니다. 튜토리얼이 완료되었습니다. 이제 다른 Google Cloud 리소스에 대해 고유한 정책을 계속 추가할 수 있습니다.

문제 해결

구성 커넥터가 예상 Google Cloud 리소스를 만들지 않는 경우 Cloud Shell에서 다음 명령어를 사용하여 구성 커넥터 컨트롤러 관리자의 로그를 확인하세요.

kubectl logs --namespace cnrm-system --container manager \
  --selector cnrm.cloud.google.com/component=cnrm-controller-manager,cnrm.cloud.google.com/scoped-namespace=NAMESPACE

정책 컨트롤러가 정책을 올바르게 적용하지 않는 경우 다음 명령어를 사용하여 컨트롤러 관리자 로그를 확인하세요.

kubectl logs deployment/gatekeeper-controller-manager \
  --namespace gatekeeper-system

정책 컨트롤러가 제약조건 객체의 status 필드에서 위반을 보고하지 않으면 다음 명령어를 사용하여 감사 컨트롤러의 로그를 확인하세요.

kubectl logs deployment/gatekeeper-audit --namespace gatekeeper-system

이 튜토리얼에서 문제가 발생하면 다음 문서를 검토하세요.

정리

이 튜토리얼에서 사용된 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 리소스가 포함된 프로젝트를 삭제하거나 프로젝트를 유지하고 개별 리소스를 삭제하세요.

프로젝트 삭제

  1. Google Cloud 콘솔에서 리소스 관리 페이지로 이동합니다.

    리소스 관리로 이동

  2. 삭제하려는 프로젝트가 조직에 연결되어 있으면 이름 열에서 조직 목록을 확장합니다.
  3. 프로젝트 목록에서 삭제할 프로젝트를 선택하고 삭제를 클릭합니다.
  4. 대화상자에서 프로젝트 ID를 입력한 후 종료를 클릭하여 프로젝트를 삭제합니다.

리소스 삭제

이 튜토리얼에서 사용된 Google Cloud 프로젝트를 유지하려면 개별 리소스를 삭제합니다.

  1. Cloud Shell에서 Cloud Storage 버킷 위치 제약조건을 삭제합니다.

    kubectl delete -f tutorial-storagebucket-location-constraint.yaml
    
  2. 구성 커넥터에서 관리하는 네임스페이스의 모든 storagebucket 리소스에 문자열 값이 truecnrm.cloud.google.com/force-destroy 주석을 추가합니다.

    kubectl annotate storagebucket --all --namespace NAMESPACE \
      cnrm.cloud.google.com/force-destroy=true
    

    이 주석은 GKE 클러스터에서 해당 storagebucket 리소스를 삭제할 때 버킷에 객체가 포함되어 있더라도 구성 커넥터가 Cloud Storage 버킷을 삭제할 수 있도록 하는 지시문입니다.

  3. Cloud Storage 버킷을 나타내는 구성 커넥터 리소스를 삭제하세요.

    kubectl delete --namespace NAMESPACE storagebucket --all
    
  4. GKE 클러스터를 삭제합니다.

    gcloud container clusters delete CLUSTER_NAME \
      --zone ZONE --async --quiet
    
  5. IAM에서 워크로드 아이덴티티 정책 binding을 삭제합니다.

    gcloud iam service-accounts remove-iam-policy-binding \
      SERVICE_ACCOUNT_NAME@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com \
      --member "serviceAccount:$GOOGLE_CLOUD_PROJECT.svc.id.goog[cnrm-system/cnrm-controller-manager-NAMESPACE]" \
      --role roles/iam.workloadIdentityUser
    
  6. Google 서비스 계정의 Cloud Storage 관리자 역할 결합을 삭제합니다.

    gcloud projects remove-iam-policy-binding $GOOGLE_CLOUD_PROJECT \
      --member "serviceAccount:SERVICE_ACCOUNT_NAME@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com" \
      --role roles/storage.admin
    
  7. 구성 커넥터용으로 만든 Google 서비스 계정을 삭제합니다.

    gcloud iam service-accounts delete --quiet \
      SERVICE_ACCOUNT_NAME@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com
    

다음 단계