일반적인 문제 해결

이 페이지에서는 VPC 서비스 제어를 구성할 때 발생할 수 있는 여러 문제들을 보여줍니다.

예기치 않은 범위 지정된 정책 동작

범위 지정된 정책에서 허용되는 일부 VPC 서비스 제어 위반이 예기치 않게 발생할 수 있습니다. 이 문제는 조직 수준의 액세스 정책이 없을 때 범위 지정된 액세스 정책에서 일부 문제가 예기치 않게 발생할 수 있는 것으로 알려진 문제입니다.

이 문제를 해결하려면 다음 명령어를 사용해서 조직 수준에서 액세스 정책을 만듭니다.

gcloud access-context-manager policies create --organization <var>ORGANIZATION_ID</var> --title <var>POLICY_TITLE</var>

다음을 바꿉니다.

  • ORGANIZATION_ID: 조직 ID입니다.
  • POLICY_TITLE: 인간이 읽을 수 있는 액세스 정책 제목입니다.

자세한 내용은 액세스 정책 만들기를 참조하세요.

공유 VPC

공유 VPC를 사용할 경우 공유 VPC 네트워크에 속하는 프로젝트가 포함된 서비스 경계에는 네트워크를 호스팅하는 프로젝트도 포함되어야 합니다. 공유 VPC 네트워크에 속하는 프로젝트가 호스트 프로젝트와 같은 경계에 있지 않으면 서비스가 예상대로 작동하지 않거나 완전히 차단될 수 있습니다.

공유 VPC 네트워크 호스트가 네트워크에 연결된 프로젝트와 동일한 서비스 경계에 있는지 확인합니다.

경계 간 요청

일반적으로 액세스 수준을 사용하여 경계 에서 보호되는 리소스의 서비스 경계 외부에서 요청을 허용할 수 있습니다.

하지만 일반적으로 액세스 수준에서 요청을 허용하더라도 다른 경계에서 보호되는 리소스의 경계에 있는 프로젝트에서의 요청은 거부됩니다.

예를 들어 경계 1의 프로젝트 A가 프로젝트 B에서 리소스를 요청한다고 가정해보겠습니다. 프로젝트 B의 리소스는 경계 2로 보호됩니다. 프로젝트 A는 경계에 있으므로 일반적으로 경계 2의 액세스 수준에서 보호되는 리소스의 요청을 허용하더라도 요청은 거부됩니다.

경계 간 요청을 용이하게 하려면 다음 방법 중 하나를 사용합니다.

  • 이그레스 정책 및 인그레스 정책을 사용합니다. 다른 경계에서 경계에 있는 보호된 리소스에 대한 요청을 허용하려면 다른 경계에서 이그레스 정책을 사용해야 하며 경계에 인그레스 정책을 설정해야 합니다.

  • 경계 브리지를 사용합니다. 브릿지를 사용하면 서로 다른 경계에 있는 프로젝트 두 개 이상에서 프로젝트의 모든 서비스에 요청할 수 있습니다. 이러한 요청은 서비스가 해당 경계로 보호되는 경우에도 허용됩니다.

  • 요청 서비스와 대상 리소스 모두 경계로 보호되지 않는지 확인합니다. 이 시나리오에서는 보호되는 서비스가 없으므로 작업이 성공합니다.

이메일 주소가 잘못되었거나 존재하지 않음

삭제된 주 구성원을 포함하는 경계를 업데이트할 때 The email address is invalid or non-existent 오류 메시지가 발생할 수 있습니다.

이 문제를 해결하려면 다음 태스크 중 하나를 수행하면 됩니다.

  • 액세스 수준과 인그레스 및 이그레스 규칙을 포함하여 경계에서 잘못된 이메일 주소를 삭제합니다.

    Google Cloud 콘솔 또는 Google Cloud CLI를 사용할 수 있습니다.

  • 이메일 주소가 테스트 실행 및 시행 경계에 모두 포함되어 있으면 일괄 작업(Google Cloud CLI 전용)을 수행합니다.

    1. 모든 경계를 가져옵니다.
      gcloud access-context-manager perimeters list --format=list \
       --policy=<POLICY_NAME> > my-perimeters.yaml
    
    1. my-perimeters.yaml 파일에서 잘못된 이메일 주소를 삭제하고 이를 my-perimeters-updated.yaml로 저장합니다.

    2. 모든 경계를 대체합니다.

      gcloud access-context-manager perimeters replace-all --format=list \
        --source-file=my-perimeters-updated.yaml \
        --policy=<POLICY_NAME>
    

인그레스 및 이그레스 규칙 위반

감사 로그에는 경계 위반을 이해하는 데 도움이 되는 인그레스 및 이그레스 규칙 위반에 대한 정보가 포함되어 있습니다.

인그레스 규칙 위반

인그레스 규칙 위반은 경계 외부의 API 클라이언트가 경계 내부 리소스에 액세스하려고 했음을 나타냅니다. 일치하는 인그레스 규칙이나 액세스 수준이 없으므로 서비스 경계가 요청을 거부합니다.

감사 로그의 인그레스 규칙 위반에는 다음 세부정보가 포함됩니다.

  • 인그레스 규칙 위반이 발생한 경계의 이름입니다.
  • 경계 외부의 API 클라이언트가 액세스하려고 한 경계 내부의 리소스입니다.

다음 인그레스 규칙 위반 예시에서 경계 외부의 API 클라이언트가 경계 prod-perimeter 내부의 Cloud Storage 버킷 prod-protected-storage-bucket에 액세스하려고 합니다.

ingressViolations: [
  0: {
    targetResource: "projects/1234/buckets/prod-protected-storage-bucket"
    servicePerimeter: "accessPolicies/123456789/servicePerimeters/prod-perimeter"
  }
]

이그레스 규칙 위반

감사 로그에서 이그레스 규칙 위반은 다음 이벤트 중 하나를 나타냅니다.

  • 경계 내부의 API 클라이언트가 경계 외부의 리소스에 액세스하려고 했습니다.
  • 경계 내부의 리소스와 경계 외부의 리소스를 포함하는 API 요청입니다. 예를 들어 한 버킷이 경계 내부에 있고 다른 버킷이 경계 외부에 있는 복사 명령어를 호출하는 Cloud Storage 클라이언트입니다.

서비스 경계는 일치하는 이그레스 규칙이 없기 때문에 요청을 거부합니다. 감사 로그의 이그레스 규칙 위반에는 다음 세부정보가 포함됩니다.

  • 소스 유형(예: 네트워크나 리소스)
  • 경계에서 이그레스 위반이 발생한 리소스나 네트워크인 소스
  • 이그레스 위반이 발생한 경계
  • 요청에서 액세스하려고 한 경계 외부의 대상 리소스

다음 이그레스 규칙 위반 예시에서 API 요청에는 경계 prod-perimeter 내에 있는 projects/5678의 리소스와 경계 외부에 있는 Cloud Storage 버킷 external-storage-bucket의 객체가 포함됩니다.

egressViolations: [
  0: {
    sourceType: "Resource"
    source: "projects/5678"
    targetResource: "projects/4321/buckets/external-storage-bucket/objects/corp-resources.json"
    servicePerimeter: "accessPolicies/123456789/servicePerimeters/prod-perimeter"
  }
]

VPC 서비스 제어에서 차단되는 요청 디버깅

VPC 서비스 제어 감사 로그는 VPC 서비스 제어에서 차단한 요청을 디버깅할 수 있는 기본 도구입니다.

액세스가 예기치 않게 차단되었으면 서비스 경계로 보호되는 프로젝트의 감사 로그를 참조합니다. 이러한 로그에는 요청된 리소스에 대한 중요한 데이터와 요청이 거부된 이유가 나와 있습니다. 감사 로그에 대한 자세한 내용은 문제 해결 도구를 사용하여 문제 진단을 참조하세요.

다음 섹션에는 VPC 서비스 제어를 사용할 때 발생할 수 있는 violationReason 값이 나와 있습니다.

NETWORK_NOT_IN_SAME_SERVICE_PERIMETER

이 문제의 원인은 다음 중 하나일 수 있습니다.

  • 서비스 경계 내에서 VPC 네트워크의 클라이언트가 동일한 경계에 있지 않은 프로젝트에 액세스하려고 시도합니다. 이 요청으로 인해 이그레스 위반이 발생합니다. 이 문제를 해결하려면 이그레스 규칙을 만듭니다.
  • 서비스 경계 외부에 있는 VPC 네트워크의 클라이언트가 서비스 경계로 보호되는 프로젝트에 액세스하려고 시도합니다. 이 요청으로 인해 인그레스 위반이 발생합니다. 이 문제를 해결하려면 인그레스 규칙을 만듭니다.

클라이언트는 Compute Engine 또는 Google Kubernetes Engine VM에서 또는 Cloud Interconnect를 통한 온프레미스 네트워크에서 또는 VPC 네트워크를 사용하여 구성된 VPN에서 요청을 전송할 수 있습니다.

다음 다이어그램은 서비스 경계 내부에서 VPC 네트워크의 클라이언트가 경계 외부의 프로젝트에 액세스하려고 시도할 때 발생하는 이그레스 위반을 보여줍니다.

NETWORK_NOT_IN_SAME_SERVICE_PERIMETER로 인한 이그레스 위반

다음은 이그레스 위반의 예시입니다.

egressViolations: [
{
  servicePerimeter: "accessPolicies/<POLICY_NAME>/servicePerimeters/<PERIMETER_NAME>"
  source: "projects/<NETWORK_PROJECT_NUMBER>"
  sourceType: "Network"
  targetResource: "projects/<RESOURCE_PROJECT_NUMBER>"
}
]

각 항목의 의미는 다음과 같습니다.

  • <POLICY_NAME>는 액세스 정책의 숫자 이름입니다.
  • <PERIMETER_NAME>은 서비스 경계의 이름입니다.
  • <NETWORK_PROJECT_NUMBER>는 VPC 네트워크를 보존하는 Google Cloud 프로젝트의 프로젝트 번호입니다.
  • <RESOURCE_PROJECT_NUMBER>는 리소스를 포함하는 Google Cloud 프로젝트의 프로젝트 번호입니다.

다음 다이어그램은 경계 외부의 클라이언트가 경계 내부의 프로젝트에 액세스하려고 시도할 때 발생하는 인그레스 위반을 보여줍니다.

NETWORK_NOT_IN_SAME_SERVICE_PERIMETER로 인한 인그레스 위반

다음은 인그레스 위반의 예시입니다.

ingressViolations: [
{
          targetResource: "projects/<RESOURCE_PROJECT_NUMBER>",
      source: "projects/<NETWORK_PROJECT_NUMBER>"
          servicePerimeter: "accessPolicies/<POLICY_NAME>/servicePerimeters/<PERIMETER_NAME>"
}
]

각 항목의 의미는 다음과 같습니다.

  • <RESOURCE_PROJECT_NUMBER>는 리소스를 포함하는 Google Cloud 프로젝트의 프로젝트 번호입니다.
  • <NETWORK_PROJECT_NUMBER>는 VPC 네트워크를 보존하는 Google Cloud 프로젝트의 프로젝트 번호입니다.
  • <POLICY_NAME>는 액세스 정책의 숫자 이름입니다.
  • <PERIMETER_NAME>은 서비스 경계의 이름입니다.

해결 방법

이 오류를 해결하려면 경계에 대해 인그레스 또는 이그레스 규칙을 만듭니다.

RESOURCES_NOT_IN_SAME_SERVICE_PERIMETER

이 문제는 단일 요청이 여러 리소스에 액세스하지만 리소스가 동일한 서비스 경계에 없을 때 발생합니다. 이 문제는 클라이언트가 배치된 위치 및 클라이언트가 리소스에 액세스 권한이 있는지 여부에 관계없이 발생합니다.

다음 다이어그램은 경계 외부의 프로젝트 및 서비스 경계 내부의 프로젝트에서 리소스에 액세스하는 클라이언트를 보여줍니다.

경계 외부의 프로젝트에서 리소스에 액세스하는 클라이언트로 인한 이그레스 위반

다음 다이어그램은 2개의 서로 다른 서비스 경계에 있는 프로젝트의 리소스에 액세스하는 클라이언트를 보여주지만 경계가 서로 통신하지 않습니다.

2개의 서로 다른 서비스 경계에 있는 프로젝트의 리소스에 액세스하는 클라이언트로 인한 이그레스 위반

다음은 이그레스 위반의 예시입니다.

egressViolations: [
{
  servicePerimeter: "accessPolicies/<POLICY_NAME>/servicePerimeters/<PERIMETER_NAME>"
  source: "projects/<RESOURCE_PROJECT_INSIDE_THIS_PERIMETER>"
  sourceType: "Resource"
  targetResource: "projects/<RESOURCE_PROJECT_OUTSIDE_THIS-PERIMETER>"
}
]

각 항목의 의미는 다음과 같습니다.

  • <POLICY_NAME>는 액세스 정책의 숫자 이름입니다.
  • <PERIMETER_NAME>은 서비스 경계의 이름입니다.
  • <RESOURCE_PROJECT_INSIDE_THIS_PERIMETER>는 경계 내에 있는 Google Cloud 프로젝트의 프로젝트 번호입니다.
  • <RESOURCE_PROJECT_OUTSIDE_THIS_PERIMETER>는 경계 외부에 있는 Google Cloud 프로젝트의 프로젝트 번호입니다.

해결 방법

이 문제를 해결하려면 경계에 대해 이그레스 규칙을 만듭니다.

NO_MATCHING_ACCESS_LEVEL

이 문제는 IP 주소, 기기 요구사항 또는 사용자 ID가 경계에 할당된 인그레스 규칙이나 액세스 수준과 일치하지 않을 때 발생합니다. 즉, Google Cloud 네트워크에 속하지 않는 클라이언트가 경계 외부에서 Google Cloud 네트워크 리소스에 액세스하려고 시도하는 것을 의미합니다. 감사 레코드의 callerIp 필드에 해당하는 IP 주소가 서비스 경계의 액세스 수준에 정의된 CIDR 범위와 일치하지 않는 것을 예로 들 수 있습니다.

호출자 IP 주소가 없거나 내부 IP 주소로 간주되는 경우 이 위반은 VPC 서비스 제어와 통합되지 않은 Google Cloud 서비스 때문일 수 있습니다. 그 이유는 Google Cloud 서비스가 보호 서비스에 액세스를 시도했지만 예상대로 실패했기 때문일 수 있습니다.

인그레스 규칙이 세부적인 액세스 제어를 제공하기 때문에 이 문제를 해결하려면 액세스 수준 대신 인그레스 규칙을 만드는 것이 좋습니다.

다음 다이어그램은 경계 외부에서 리소스에 액세스하려고 시도하는 클라이언트를 보여줍니다.

NO_MATCHING_ACCESS_LEVEL로 인한 인그레스 위반

다음은 인그레스 위반의 예시입니다.

authenticationInfo: {
  principalEmail: "EMAIL"
}
requestMetadata: {
callerIp: "<PUBLIC_IP_ADDRESS>"
deviceState: "Cross Organization"
}
ingressViolations: [
        {
          targetResource: "projects/<RESOURCE_PROJECT_NUMBER>",
          servicePerimeter: "accessPolicies/<POLICY_NAME>/servicePerimeters/<PERIMETER-NAME>"
        }
  ]

각 항목의 의미는 다음과 같습니다.

  • <EMAIL>은 서비스 계정 또는 인증된 사용자의 이메일 주소입니다.

    VPC 서비스 제어가 지원하지 않는 Google Cloud 서비스를 사용하는 경우 google.com 도메인에 속한 이메일 주소가 수정되고 google-internal로 대체됩니다. google-internal은 Google이 소유한 내부 ID를 나타냅니다.

  • <PUBLIC_IP_ADDRESS>는 호출자의 IP 주소입니다. 인터넷 호출자에게는 공개 IPv4 또는 IPv6 주소입니다.

  • <RESOURCE_PROJECT_NUMBER>는 리소스를 포함하는 Google Cloud 프로젝트의 프로젝트 번호입니다.

  • <POLICY_NAME>는 액세스 정책의 숫자 이름입니다.

  • <PERIMETER_NAME>은 서비스 경계의 이름입니다.

이 경우 위반 액세스 경계에 액세스 수준이 지정되지 않았을 수 있으므로 metadata.accessLevels가 여전히 존재할 수 있습니다.

SERVICE_NOT_ALLOWED_FROM_VPC

이 문제는 클라이언트가 VPC 네트워크에서 Google Cloud 리소스에 액세스하려고 시도할 때 발생합니다. 클라이언트는 Compute Engine 또는 Google Kubernetes Engine VM에서 또는 Cloud Interconnect를 통한 온프레미스 네트워크에서 또는 VPC 네트워크를 사용하여 구성된 VPN에서 요청을 전송할 수 있습니다.

이 문제를 해결하려면 서비스 경계의 VPC 액세스 가능 서비스 구성에서 호출 중인 서비스가 허용되도록 보장합니다.

시나리오 예시

다음 예시에서는 VPC 서비스 제어를 사용하는 동안 발생할 수 있는 문제를 설명합니다.

온프레미스에서 Cloud Storage 액세스

이 예시에서 VPC 서비스 제어는 직원 워크스테이션(callerIp로 식별됨)에서 프로젝트 corp-storage의 Cloud Storage 버킷으로 전송되는 요청을 차단합니다.

이 요청은 다음과 같은 감사 로그 레코드를 생성합니다.

{
 insertId:  "222lvajc6f7"
 logName:  "projects/corp-storage/logs/cloudaudit.googleapis.com%2Fpolicy"
 protoPayload: {
  @type:  "type.googleapis.com/google.cloud.audit.AuditLog"
  authenticationInfo: {
   principalEmail:  "someone@google.com"
  }
  metadata: {
   @type:  "type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata"
   resourceNames: [
    0:  "projects/_"
   ]
   violationReason:  "NO_MATCHING_ACCESS_LEVEL"
  }
  methodName:  "google.storage.NoBillingOk"
  requestMetadata: {
   callerIp:  "b1d5:d26d:5b17:43fe:d358:586b:db59:9617"
   destinationAttributes: {
   }
   requestAttributes: {
   }
  }
  resourceName:  "projects/690885588241"
  serviceName:  "storage.googleapis.com"
  status: {
   code:  7
   details: [
    0: {
     @type:  "type.googleapis.com/google.rpc.PreconditionFailure"
     violations: [
      0: {
       type:  "VPC_SERVICE_CONTROLS"
      }
     ]
    }
   ]
   message:  "Request is prohibited by organization's policy"
  }
 }
 receiveTimestamp:  "2018-11-27T21:40:43.823209571Z"
 resource: {
  labels: {
   method:  "google.storage.NoBillingOk"
   project_id:  "corp-storage"
   service:  "storage.googleapis.com"
  }
  type:  "audited_resource"
 }
 severity:  "ERROR"
 timestamp:  "2018-11-27T21:40:42.973784140Z"
}

corp-storage 프로젝트는 서비스 경계에 포함되어 있습니다. 직원 워크스테이션은 해당 경계 내에 있는 네트워크에 있지 않습니다. 직원 워크스테이션이 경계 외부에 존재하므로 요청이 차단됩니다.

프로젝트 외부의 VM에서 BigQuery 액세스

이 예시에서 458854174376 프로젝트(data-collector)에 속한 VM이 798816221974 프로젝트(corp-resources-protected)의 데이터 세트에서 BigQuery 쿼리를 실행하려 했지만 거부되었습니다.

VM은 다음 쿼리를 사용합니다.

bq --project=corp-resources-protected query 'select count(*) from babynames.yob2000'

이 쿼리는 다음과 같은 출력을 반환합니다.

BigQuery error in query operation: VPC Service Controls: Request is
prohibited by organization's policy. Operation ID:
33643962-6a0f-4091-9283-bcdf7e9271f0

다음 감사 로그 레코드가 생성됩니다.

{
 insertId:  "1ei551d2pdq"
 logName:  "projects/corp-resources-protected/logs/cloudaudit.googleapis.com%2Fpolicy"
 protoPayload: {
  @type:  "type.googleapis.com/google.cloud.audit.AuditLog"
  authenticationInfo: {
   principalEmail:  "714877721106-compute@example.gserviceaccount.com"
  }
  metadata: {
   @type:  "type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata"
   resourceNames: [
    0:  "projects/1004338142803"
   ]
   violationReason:  "NETWORK_NOT_IN_SAME_SERVICE_PERIMETER"
  }
  methodName:  "bigquery.googleapis.com/bigquery.jobs.create"
  requestMetadata: {
   callerIp:  "10.105.0.2"
   callerNetwork:  "//compute.googleapis.com/projects/ameet-dataflow/global/networks/__unknown__"
   destinationAttributes: {
   }
   requestAttributes: {
   }
  }
  resourceName:  "projects/1004338142803"
  serviceName:  "bigquery.googleapis.com"
  status: {
   code:  7
   details: [
    0: {
     @type:  "type.googleapis.com/google.rpc.PreconditionFailure"
     violations: [
      0: {
       type:  "VPC_SERVICE_CONTROLS"
      }
     ]
    }
   ]
   message:  "Request is prohibited by organization's policy"
  }
 }
 receiveTimestamp:  "2018-11-28T23:06:13.579882505Z"
 resource: {
  labels: {
   method:  "bigquery.googleapis.com/bigquery.jobs.create"
   project_id:  "corp-resources-protected"
   service:  "bigquery.googleapis.com"
  }
  type:  "audited_resource"
 }
 severity:  "ERROR"
 timestamp:  "2018-11-28T23:06:12.799656975Z"
}

이 예시에서 violationReasonNETWORK_NOT_IN_SAME_SERVICE_PERIMETER입니다. callerNetworkcallerIp 다음에 추가됩니다. IP 주소는 비공개이며 정확한 네트워크 정보도 제공되었습니다. 문제와 관련된 리소스는 VpcServiceControlAuditMetadata.resourceNamesrequestMetadata.callerNetwork(네트워크를 소유한 프로젝트) 등 두 곳에 나와 있습니다.

문제는 corp-resources-protected 프로젝트는 서비스 경계 내부에 있지만 VM이 속한 네트워크가 포함된 프로젝트인 data-collector는 서비스 경계 외부에 있다는 점입니다. 이 경우 액세스가 예상대로 거부됩니다.

프로젝트 간 BigQuery 쿼리

이 예시에서 perimeter-network 프로젝트에 속한 VM은 perimeter-network와 동일한 서비스 경계에 있는 corp-resources-protected 프로젝트와 그렇지 않은 corp-resources-public 프로젝트 등 두 프로젝트의 BigQuery 인스턴스를 쿼리하려 합니다.

VM은 다음 명령어를 사용합니다.

bq query --use_legacy_sql=false \
  'select count(priv.name),count(pub.name) from \
  `corp-resources-protected.babynames.yob2000` as priv, \
  `corp-resources-public.babynames.yob2000` as pub'

이 쿼리는 다음과 같은 출력을 반환합니다.

BigQuery error in query operation: Error processing job
'example:bqjob_r211e6f6eec928ffb_000001675c996aa8_1': VPC Service Controls:
Request is prohibited by organization's policy. Operation ID:
dc4fc177-4850-4fc5-b2e7-8c33f302149a

다음 감사 로그 레코드가 생성됩니다.

{
 insertId:  "17kg4exd24ag"
 logName:  "projects/perimeter-network/logs/cloudaudit.googleapis.com%2Fpolicy"
 protoPayload: {
  @type:  "type.googleapis.com/google.cloud.audit.AuditLog"
  authenticationInfo: {
  }
  metadata: {
   @type:  "type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata"
   resourceNames: [
    0:  "projects/117961063178"
    1:  "projects/690885588241"
   ]
   violationReason:  "RESOURCES_NOT_IN_SAME_SERVICE_PERIMETER"
  }
  methodName:  "bigquery.googleapis.com/bigquery.tables.getData"
  requestMetadata: {
   callerIp:  "130.211.225.66"
   callerNetwork:  "//compute.googleapis.com/projects/perimeter-network/global/networks/__unknown__"
   destinationAttributes: {
   }
   requestAttributes: {
   }
  }
  resourceName:  "projects/927005422713"
  serviceName:  "bigquery.googleapis.com"
  status: {
   code:  7
   details: [
    0: {
     @type:  "type.googleapis.com/google.rpc.PreconditionFailure"
     violations: [
      0: {
       type:  "VPC_SERVICE_CONTROLS"
      }
     ]
    }
   ]
   message:  "Request is prohibited by organization's policy"
  }
 }
 receiveTimestamp:  "2018-11-28T20:48:51.384237810Z"
 resource: {
  labels: {
   method:  "bigquery.googleapis.com/bigquery.tables.getData"
   project_id:  "perimeter-network"
   service:  "bigquery.googleapis.com"
  }
  type:  "audited_resource"
 }
 severity:  "ERROR"
 timestamp:  "2018-11-28T20:48:50.561884949Z"
}

callerNetworkVpcServiceControlAuditMetadata.resourceNames를 확인하면 perimeter-network, 117961063178(corp-resources-public), 690885588241(corp-resources-protected) 등 프로젝트 세 개를 확인할 수 있습니다. corp-resources-publicperimeter-networkcorp-resources-protected와 같은 서비스 경계에 없다는 점을 기억하세요.

violationReason, RESOURCES_NOT_IN_SAME_SERVICE_PERIMETER는 요청의 일부 리소스가 요청에 적용되는 경계 외부에 있다는 의미입니다. 이 경우 리소스는 corp-resources-public입니다.

경계 내로 Cloud Storage 파일 이동

이 예시에서 perimeter-network 프로젝트의 VM은 명령어를 사용하여 corp-resources-protected 프로젝트에 있는 한 Cloud Storage 버킷에서 corp-resources-public 프로젝트에 있는 다른 버킷으로 파일을 이동합니다.

VM은 다음 명령어를 사용합니다.

gsutil mv gs://corp-resources-private-1/yob2000.txt gs://corp-resources-public-1/babynames/

이 명령어는 다음 출력을 반환합니다.

Copying gs://corp-resources-private-1/yob2000.txt [Content-Type=text/plain]...
AccessDeniedException: 403 Request violates VPC Service Controls.

다음 감사 로그 레코드가 생성됩니다.

{
 insertId:  "1xxnssmd2hqo"
 logName:  "projects/perimeter-network/logs/cloudaudit.googleapis.com%2Fpolicy"
 protoPayload: {
  @type:  "type.googleapis.com/google.cloud.audit.AuditLog"
  authenticationInfo: {
   principalEmail:  "storage-accessing@example.iam.gserviceaccount.com"
  }
  metadata: {
   @type:  "type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata"
   resourceNames: [
    0:  "projects/_/buckets/corp-resources-public-1"
   ]
   violationReason:  "NETWORK_NOT_IN_SAME_SERVICE_PERIMETER"
  }
  methodName:  "google.storage.BillingRequiredRead"
  requestMetadata: {
   callerIp:  "130.211.225.66"
   callerNetwork:  "//compute.googleapis.com/projects/perimeter-network/global/networks/__unknown__"
   destinationAttributes: {
   }
   requestAttributes: {
   }
  }
  resourceName:  "projects/927005422713"
  serviceName:  "storage.googleapis.com"
  status: {…}
 }
 receiveTimestamp:  "2018-11-28T00:45:31.531623485Z"
 resource: {
  labels: {
   method:  "google.storage.BillingRequiredRead"
   project_id:  "perimeter-network"
   service:  "storage.googleapis.com"
  }
  type:  "audited_resource"
 }
 severity:  "ERROR"
 timestamp:  "2018-11-28T00:45:31.351140381Z"
}

이 경우 나열된 메서드가 BillingRequiredRead이고 수행된 작업이 move이므로 로그가 다소 모호합니다. 이는 VPC 서비스 제어의 현재 감사 로그 기능에 적용되는 제한사항입니다.

이유는 다소 모호하지만 이 감사 로그 레코드는 요청의 일부 리소스가 요청에 적용되는 경계 외부에 있음을 나타냅니다. 이 경우 리소스는 corp-resources-public입니다.

경계 외부로 Cloud Storage 파일 이동

이 예시에서 public-network 프로젝트의 VM은 명령어를 사용하여 corp-resources-protected 프로젝트에 있는 한 Cloud Storage 버킷에서 corp-resources-public 프로젝트에 있는 다른 버킷으로 파일을 이동합니다.

corp-resources-protected 프로젝트는 서비스 경계로 보호됩니다. public-networkcorp-resources-public 프로젝트는 경계 외부에 존재합니다.

VM은 다음 명령어를 사용합니다.

gsutil mv gs://corp-resources-private-1/yob2000.txt gs://corp-resources-public-1/babynames/

이 명령어는 다음 출력을 반환합니다.

Copying gs://corp-resources-private-1/yob2000.txt [Content-Type=text/plain]...
AccessDeniedException: 403 Request violates VPC Service Controls.

다음 감사 로그 레코드가 생성됩니다.

{
 insertId:  "10moqhsch9v"
 logName:  "projects/corp-resources-private/logs/cloudaudit.googleapis.com%2Fpolicy"
 protoPayload: {
  @type:  "type.googleapis.com/google.cloud.audit.AuditLog"
  authenticationInfo: {
   principalEmail:  "user@example.biz"
  }
  metadata: {
   @type:  "type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata"
   resourceNames: [
    0:  "projects/_/buckets/corp-resources-private-1/objects/yob2000.txt"
    1:  "projects/_/buckets/corp-resources-public-1/objects/out.txt"
   ]
   violationReason:  "RESOURCES_NOT_IN_SAME_SERVICE_PERIMETER"
  }
  methodName:  "google.storage.Write"
  requestMetadata: {
   callerIp:  "2620:15c:2c4:203:63d6:5eb8:418d:c034"
   destinationAttributes: {
   }
   requestAttributes: {
   }
  }
  resourceName:  "projects/1004338142803"
  serviceName:  "storage.googleapis.com"
  status: {
   code:  7
   details: [
    0: {
     @type:  "type.googleapis.com/google.rpc.PreconditionFailure"
     violations: [
      0: {
       type:  "VPC_SERVICE_CONTROLS"
      }
     ]
    }
   ]
   message:  "Request is prohibited by organization's policy"
  }
 }
 receiveTimestamp:  "2018-11-30T16:34:46.948010626Z"
 resource: {
  labels: {
   method:  "google.storage.Write"
   project_id:  "corp-resources-private"
   service:  "storage.googleapis.com"
  }
  type:  "audited_resource"
 }
 severity:  "ERROR"
 timestamp:  "2018-11-30T16:34:46.898098978Z"
}

이 예시에서 감사 로그는 서비스 경계 범위 외부로 데이터를 복사할 수 없음을 나타냅니다(두 리소스 모두 감사 로그 레코드에 있음). 요청이 경계 외부(public-network에 있는 VM)에서 발생하고 버킷 중 하나가 경계(corp-resources-public-1) 외부에 있다는 점을 기억하세요.

경계 외부의 VM은 corp-resources-public-1 버킷에 데이터를 쓸 수 있으므로 이전 예시에서 실패한 검사가 성공합니다. 그러나 실제로 데이터를 복사하는 후속 검사는 실패합니다.

이 예시에서는 단일 사용자 작업으로 인해 VPC 서비스 제어 적용을 통과해야 하는 내부 작업이 여러 번 발생하는 경우를 보여줍니다.

경계 내부의 VM에서 BigQuery 데이터 세트 사본

이 예시에서 927005422713 프로젝트(perimeter-network)의 VM은 BigQuery 데이터 세트를 corp-resources-private 프로젝트에서 corp-resources-public 프로젝트(117961063178)로 복사하려 합니다. perimeter-networkcorp-resources-private는 경계를 공유하지만 corp-resources-public은 경계 외부에 있습니다.

VM은 다음 명령어를 사용합니다.

bq cp corp-resources-private:babynames.yob2000 \
  corp-resources-public:babynames.yob2000

이 명령어는 다음 출력을 반환합니다.

BigQuery error in cp operation: VPC Service Controls: Request is prohibited by
organization's policy. Operation ID: c00dbc44-460f-4bd0-9d09-cda98ac800f9

다음 감사 로그 레코드가 생성됩니다.

{
 insertId:  "146o5fd2hbp"
 logName:  "projects/perimeter-network/logs/cloudaudit.googleapis.com%2Fpolicy"
 protoPayload: {
  @type:  "type.googleapis.com/google.cloud.audit.AuditLog"
  authenticationInfo: {
  }
  metadata: {
   @type:  "type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata"
   resourceNames: [
    0:  "projects/117961063178"
   ]
   violationReason:  "RESOURCES_NOT_IN_SAME_SERVICE_PERIMETER"
  }
  methodName:  "bigquery.googleapis.com/bigquery.tables.get"
  requestMetadata: {
   callerIp:  "131.201.221.16"
   callerNetwork:  "//compute.googleapis.com/projects/perimeter-network/global/networks/__unknown__"
   destinationAttributes: {
   }
   requestAttributes: {
   }
  }
  resourceName:  "projects/927005422713"
  serviceName:  "bigquery.googleapis.com"
  status: {
   code:  7
   details: [
    0: {
     @type:  "type.googleapis.com/google.rpc.PreconditionFailure"
     violations: [
      0: {
       type:  "VPC_SERVICE_CONTROLS"
      }
     ]
    }
   ]
   message:  "Request is prohibited by organization's policy"
  }
 }
 receiveTimestamp:  "2018-11-28T00:27:05.688803777Z"
 resource: {
  labels: {
   method:  "bigquery.googleapis.com/bigquery.tables.get"
   project_id:  "perimeter-network"
   service:  "bigquery.googleapis.com"
  }
  type:  "audited_resource"
 }
 severity:  "ERROR"
 timestamp:  "2018-11-28T00:27:05.378584819Z"
}

이 예시에는 로깅 메커니즘의 제한사항과 BigQuery의 분산 아키텍처로 인해 이 요청과 관련된 모든 리소스를 보여주는 단일 기본 API 작업이 없습니다.

감사 로그 레코드는 데이터를 복사하기 위해 BigQuery가 perimeter-network 프로젝트(요청 소스)의 네트워크를 사용하여 대상 프로젝트(corp-resources-public)에 액세스해야 하므로 작업이 실패했음을 나타냅니다. corp-resources-publicperimeter-network를 보호하는 경계 외부에 있다는 점을 기억하세요. 이 요청은 데이터를 corp-resources-public으로 추출하려는 시도로 간주되어 거부됩니다.

이 예시에서는 데이터 복사와 같은 개념 작업이 Cloud Storage, BigQuery, Bigtable과 같은 서로 다른 스토리지 시스템의 데이터에 액세스하려고 여러 번 시도할 수 있음을 보여줍니다. 작업이 수행되는 방법에 따라 생성된 감사 로그 레코드가 원본 사용자 명령어와 다릅니다. 또한 지정된 서비스 내에서 여러 검사가 수행되고 잠재적으로 실패할 때, 생성된 감사 로그 레코드는 원본 사용자 명령어와 다르게 보입니다.

프로젝트에서 Dataproc 작업 읽기

이 예시에서는 Dataproc과 같은 데이터 처리 서비스를 사용할 때 발생하는 간접 VPC 서비스 제어 오류를 디버깅하는 방법을 보여줍니다.

이 예시에서 Dataproc 클러스터는 VPC 서비스 제어로 보호되는 프로젝트에서 실행되고 있습니다. Hello-world.py는 경계 내에 있는 Cloud Storage 버킷의 데이터에 액세스한 후 경계 외부에 있는 다른 버킷에 쓰려고 시도하는 pyspark 작업입니다. VPC 서비스 제어는 경계 외부의 버킷에 데이터를 쓰는 작업을 차단합니다.

다음 명령어는 Hello-world.py를 실행하는 데 사용됩니다.

gcloud dataproc jobs submit pyspark hello-world.py --cluster test-cluster-new2

이 명령어는 다음 출력을 반환합니다.

Job [50f16ca8-5102-442b-a545-eed5e4f5f5da] submitted.
Waiting for job output...
18/11/29 00:31:34 INFO org.spark_project.jetty.util.log: Logging initialized @2552ms
18/11/29 00:31:34 INFO org.spark_project.jetty.server.Server: jetty-9.3.z-SNAPSHOT
18/11/29 00:31:34 INFO org.spark_project.jetty.server.Server: Started @2640ms
18/11/29 00:31:34 INFO org.spark_project.jetty.server.AbstractConnector: Started ServerConnector@1f1c18ec{HTTP/1.1,[http/1.1]}{0.0.0.0:4040}
18/11/29 00:31:34 INFO com.google.cloud.hadoop.fs.gcs.GoogleHadoopFileSystemBase: GHFS version: 1.6.4-hadoop2
18/11/29 00:31:35 INFO org.apache.hadoop.yarn.client.RMProxy: Connecting to ResourceManager at test-cluster-new2-m/10.246.0.3:8032
18/11/29 00:31:37 INFO org.apache.hadoop.yarn.client.api.impl.YarnClientImpl: Submitted application application_1522454176466_0005
Traceback (most recent call last):
  File "/tmp/50f16ca8-5102-442b-a545-eed5e4f5f5da/hello-world.py", line 8, in <module>
    lear.saveAsTextFile("gs://corp-resources-public-1/out.txt")
  File "/usr/lib/spark/python/lib/pyspark.zip/pyspark/rdd.py", line 1553, in saveAsTextFile
  File "/usr/lib/spark/python/lib/py4j-0.10.4-src.zip/py4j/java_gateway.py", line 1133, in __call__
  File "/usr/lib/spark/python/lib/py4j-0.10.4-src.zip/py4j/protocol.py", line 319, in get_return_value
py4j.protocol.Py4JJavaError: An error occurred while calling o49.saveAsTextFile.
: java.io.IOException: Error accessing: bucket: corp-resources-public-1, object: out.txt
    at com.google.cloud.hadoop.gcsio.GoogleCloudStorageImpl.wrapException(GoogleCloudStorageImpl.java:1767)
$sp(PairRDDFunctions.scala:961)

 (truncated)

Caused by: com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden
{
  "code" : 403,
  "errors" : [ {
    "domain" : "global",
    "message" : "Request violates VPC Service Controls.",
    "reason" : "vpcServiceControls"
  } ],
  "message" : "Request violates VPC Service Controls."
}
    at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:145)

 (truncated)

18/11/29 00:31:43 INFO org.spark_project.jetty.server.AbstractConnector: Stopped Spark@1f1c18ec{HTTP/1.1,[http/1.1]}{0.0.0.0:4040}
ERROR: (gcloud.dataproc.jobs.submit.pyspark) Job [50f16ca8-5102-442b-a545-eed5e4f5f5da] entered state [ERROR] while waiting for [DONE].

saveAsTextFile 메서드가 호출될 때 발생하는 IO 예외에 유의하세요. Cloud Storage는 Request violates VPC Service Controls 메시지와 함께 403 오류를 반환합니다. 이 오류는 Cloud Storage 감사 로그 작업을 검토해야 함을 나타냅니다.

명령어가 실행된 perimeter-network 프로젝트의 감사 로그에는 saveAsTextFile 작업의 감사 로그 레코드가 있습니다.

{
 insertId:  "qdj1o9d1run"
 logName:  "projects/corp-resources-private/logs/cloudaudit.googleapis.com%2Fpolicy"
 protoPayload: {
  @type:  "type.googleapis.com/google.cloud.audit.AuditLog"
  authenticationInfo: {
   principalEmail:  "1004338142803-compute@example.gserviceaccount.com"
  }
  metadata: {
   @type:  "type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata"
   resourceNames: [
    0:  "projects/_/buckets/corp-resources-public-1/objects/out.txt"
   ]
   violationReason:  "RESOURCES_NOT_IN_SAME_SERVICE_PERIMETER"
  }
  methodName:  "google.storage.BillingRequiredRead"
  requestMetadata: {
   callerIp:  "10.246.0.3"
   callerNetwork:  "//compute.googleapis.com/projects/corp-resources-private/global/networks/__unknown__"
   destinationAttributes: {
   }
   requestAttributes: {
   }
  }
  resourceName:  "projects/1004338142803"
  serviceName:  "storage.googleapis.com"
  status: {
   code:  7
   details: [
    0: {
     @type:  "type.googleapis.com/google.rpc.PreconditionFailure"
     violations: [
      0: {
       type:  "VPC_SERVICE_CONTROLS"
      }
     ]
    }
   ]
   message:  "Request is prohibited by organization's policy"
  }
 }
 receiveTimestamp:  "2018-11-29T00:31:43.666227930Z"
 resource: {
  labels: {
   method:  "google.storage.BillingRequiredRead"
   project_id:  "corp-resources-private"
   service:  "storage.googleapis.com"
  }
  type:  "audited_resource"
 }
 severity:  "ERROR"
 timestamp:  "2018-11-29T00:31:43.608250320Z"
}

감사 로그 제한사항으로 인해 Cloud Storage의 methodName은 실제로 write 작업이지만 Read로 나열됩니다. 감사 로그 레코드는 corp-resources-private 프로젝트의 네트워크가 corp-resources-public-1 버킷에 있는 리소스의 데이터에 액세스하려 했으므로(이 경우 쓰기) 작업이 실패했음을 나타냅니다. Cloud Storage 감사 로그의 제한사항으로 인해 프로젝트 버킷 corp-resources-public-1이 어디에 속하는지 명확하지 않습니다.

corp-resources-public-1이 포함된 프로젝트를 식별하려면 다음 명령어를 사용합니다.

gsutil --debug ls -L -b gs://corp-resources-public-1 2>&1 | grep projectNumber

이 명령어는 다음 출력을 반환합니다.

projectNumber: u'117961063178'

117961063178은 경계 외부에 있는 corp-resources-public 프로젝트입니다. 따라서 실패가 예상됩니다.

지원되지 않는 서비스로 인한 오류

일부 Google Cloud 서비스는 구현 중 다른 Google Cloud 서비스에 다라 달라집니다. App Engine과 같은 지원되지 않는 서비스가 경계로 보호되는 프로젝트 내에 사용되는 경우에는 서비스의 리소스에 액세스하지 못할 수 있습니다.

알려진 문제점에 대한 자세한 내용은 알려진 서비스 제한사항을 참조하세요.

제한된 VIP를 사용하는 미지원 서비스

VPC 서비스 제어의 제한된 VIP가 지원되지 않는 API에 액세스하려고 하면 403 오류가 발생합니다. 예를 들어 VPC 서비스 제어는 App Engine을 지원하지 않으므로 제한된 VIP를 사용할 때 App Engine Admin API를 사용할 수 없습니다.

예를 들어 다음 명령어를 사용해서 서비스 경계 내의 모든 App Engine 서비스를 나열한다고 가정해 보세요.

gcloud app services list

이 명령어는 다음 출력을 반환합니다.

ERROR: (gcloud.app.services.list) User [***] does not have permission to access apps instance [***] (or it may not exist): <!DOCTYPE html>
<html lang=en>
  <meta charset=utf-8>
  <meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
  <title>Error 403 (Forbidden)!!1</title>
  <style>
    *{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){ #logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){ #logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
  </style>
  <a href=//www.google.com/><span id=logo aria-label=Google></span></a>
  <p><b>403.</b> <ins>That's an error.</ins>
  <p>Your client does not have permission to get URL <code>/v1/apps/***/services</code> from this server.  <ins>That's all we know.</ins>

VPC 서비스 제어에서 지원하지 않으며 제한된 VIP에서 사용할 수 없는 서비스의 경우 이러한 유형의 오류가 발생할 수 있습니다. VPC 서비스 제어에서 지원하는 서비스에서 이 오류가 발생할 경우 서비스의 알려진 서비스 제한사항을 통해 알려진 제한사항인지 확인하는 것이 좋습니다. 그렇지 않으면 이 문제를 보고해야 합니다.

경계 외부의 프로젝트로 로그 내보내기

이 예시에서는 VPC 서비스 제어가 로그 내보내기를 차단합니다. 내보내기 대상인 corp-resources-public 프로젝트는 VPC 서비스 제어 경계 외부에 있으며 perimeter-network 프로젝트에서 생성된 싱크는 경계 내부에 있습니다.

예를 들어 다음 명령어를 사용한다고 가정해 보겠습니다.

gcloud logging sinks describe example-sink

이 명령어는 다음 출력을 반환합니다.

destination: bigquery.googleapis.com/projects/corp-resources-public/datasets/logs
filter: |-
  resource.type="audited_resource"
  resource.labels.service="bigquery.googleapis.com"
name: example-sink
outputVersionFormat: V2
writerIdentity: serviceAccount:p927005422713-439672@gcp-sa-logging.iam.gserviceaccount.com

다음 감사 로그 레코드가 생성됩니다.

{
 insertId:  "e5i2i8cbqw"
 logName:  "projects/perimeter-network/logs/cloudaudit.googleapis.com%2Fpolicy"
 protoPayload: {
  @type:  "type.googleapis.com/google.cloud.audit.AuditLog"
  authenticationInfo: {
   principalEmail:  "p927005422713-439672@gcp-sa-logging.iam.gserviceaccount.com"
  }
  metadata: {
   @type:  "type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata"
   resourceNames: [
    0:  "corp-resources-public"
   ]
   violationReason:  "RESOURCES_NOT_IN_SAME_SERVICE_PERIMETER"
  }
  methodName:  "google.cloud.bigquery.v2.TableDataService.InsertAll"
  requestMetadata: {
   callerIp:  "2002:a49:8c51::"
   destinationAttributes: {
   }
   requestAttributes: {
   }
  }
  resourceName:  "projects/927005422713"
  serviceName:  "bigquery.googleapis.com"
  status: {
   code:  7
   details: [
    0: {
     @type:  "type.googleapis.com/google.rpc.PreconditionFailure"
     violations: [
      0: {
       type:  "VPC_SERVICE_CONTROLS"
      }
     ]
    }
   ]
   message:  "Request is prohibited by organization's policy"
  }
 }
 receiveTimestamp:  "2018-11-29T17:32:19.287138882Z"
 resource: {
  labels: {
   method:  "google.cloud.bigquery.v2.TableDataService.InsertAll"
   project_id:  "perimeter-network"
   service:  "bigquery.googleapis.com"
  }
  type:  "audited_resource"
 }
 severity:  "ERROR"
 timestamp:  "2018-11-29T17:32:19.054662413Z"
}

Logging이 아닌 BigQuery에 대한 감사 로그 레코드가 생성됩니다. Logging이 데이터를 쓰려고 시도하는 싱크 서비스가 BigQuery이기 때문입니다.

corp-resources-publicperimeter-network를 보호하는 경계 외부에 있으므로 내보내기가 실패합니다.

이 예시에서는 Google Cloud 서비스가 p927005422713-439672@gcp-sa-logging.iam.gserviceaccount.com과 같은 Google Cloud 내부에 있는 관리형 서비스 계정을 사용하여 다른 Google Cloud 서비스를 호출할 경우 요청의 '네트워크 프로젝트'(이 경우 perimeter-network)가 해당 ID에서 파생됩니다. 동일한 ID는 로그 내보내기 리소스 자체를 나타냅니다.

이 패턴은 Google Cloud에서 일반적이며 서비스 간 상호작용의 수많은 사례에 적용됩니다.

Cloud Storage로 BigQuery 추출

이 예시에서는 실패한 BigQuery 압축을 Cloud Storage로 디버깅하는 방법을 설명합니다.

이 예시에서 corp-resources-privateperimeter-network는 서비스 경계로 보호되는 프로젝트입니다. corp-resources-public은 경계 외부에 있는 프로젝트입니다.

다음 명령어를 사용했다고 가정해 보겠습니다.

bq extract babynames.yob2000

이 명령어는 다음 출력을 반환합니다.

gs://corp-resources-public-1/export.txt
Waiting on bqjob_r47ee34109d02b41_000001676b27157c_1 ... (1s) Current status: DONE
BigQuery error in extract operation: Error processing job 'corp-resources-private:bqjob_r47ee34109d02b41_000001676b27157c_1': Access
Denied: BigQuery BigQuery: Permission denied while writing data.

이 경우 오류가 특별히 VPC 서비스 제어와 연관된 것으로 나타나지 않습니다. Identity and Access Management 오류가 있으면 비슷한 오류가 표시됩니다.

다음 감사 로그 레코드가 생성됩니다.

{
 insertId:  "4gbh6pe8jld7"
 logName:  "projects/corp-resources-private/logs/cloudaudit.googleapis.com%2Fdata_access"
 protoPayload: {
  @type:  "type.googleapis.com/google.cloud.audit.AuditLog"
  authenticationInfo: {
   principalEmail:  "storage-accessing@example.iam.gserviceaccount.com"
  }
  methodName:  "jobservice.jobcompleted"
  requestMetadata: {
   callerIp:  "10.5.0.4"
   callerNetwork:  "//compute.googleapis.com/projects/perimeter-network/global/networks/__unknown__"
   callerSuppliedUserAgent:  "google-api-python-client/1.6.5 (gzip),gzip(gfe)"
   destinationAttributes: {
   }
   requestAttributes: {
   }
  }
  resourceName:  "projects/corp-resources-private/jobs/bqjob_r47ee34109d02b41_000001676b27157c_1"
  serviceData: {
   @type:  "type.googleapis.com/google.cloud.bigquery.logging.v1.AuditData"
   jobCompletedEvent: {
    eventName:  "extract_job_completed"
    job: {
     jobConfiguration: {
      extract: {
       destinationUris: [
        0:  "gs://corp-resources-public-1/export.txt"
       ]
       sourceTable: {
        datasetId:  "babynames"
        projectId:  "corp-resources-private"
        tableId:  "yob2000"
       }
      }
     }
     jobName: {
      jobId:  "bqjob_r47ee34109d02b41_000001676b27157c_1"
      location:  "US"
      projectId:  "corp-resources-private"
     }
     jobStatistics: {
      createTime:  "2018-12-01T19:03:03.908Z"
      endTime:  "2018-12-01T19:03:05.494Z"
      startTime:  "2018-12-01T19:03:04.013Z"
     }
     jobStatus: {
      additionalErrors: [
       0: {
        code:  7
        message:  "Access Denied: BigQuery BigQuery: Permission denied while writing data."
       }
      ]
      error: {
       code:  7
       message:  "Access Denied: BigQuery BigQuery: Permission denied while writing data."
      }
      state:  "DONE"
     }
    }
   }
  }
  serviceName:  "bigquery.googleapis.com"
  status: {
   code:  7
   message:  "Access Denied: BigQuery BigQuery: Permission denied while writing data."
  }
 }
 receiveTimestamp:  "2018-12-01T19:03:05.532169998Z"
 resource: {
  labels: {
   project_id:  "corp-resources-private"
  }
  type:  "bigquery_resource"
 }
 severity:  "ERROR"
 timestamp:  "2018-12-01T19:03:05.503Z"
}

이 감사 로그 레코드에서 storage-accessing@example.iam.gserviceaccount.com은 작업을 실행하려고 시도하는 ID로 식별됩니다. 이 예시에서는 storage-accessing@example.iam.gserviceaccount.com에 명령어를 실행하는 데 필요한 IAM 권한이 있다고 가정합니다.

IAM 권한이 문제가 아니므로 다음 단계는 VPC 서비스 제어 오류를 확인하는 것입니다.

대상 서비스(Cloud Storage)의 감사 로그 레코드에는 실패의 자세한 이유가 있습니다.

{
 insertId:  "1bq397kcfj1"
 logName:  "projects/corp-resources-private/logs/cloudaudit.googleapis.com%2Fpolicy"
 protoPayload: {
  @type:  "type.googleapis.com/google.cloud.audit.AuditLog"
  authenticationInfo: {
   principalEmail:  "storage-accessing@example.iam.gserviceaccount.com"
  }
  metadata: {
   @type:  "type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata"
   resourceNames: [
    0:  "projects/1004338142803"
    1:  "projects/_/buckets/corp-resources-public-1"
   ]
   violationReason:  "RESOURCES_NOT_IN_SAME_SERVICE_PERIMETER"
  }
  methodName:  "google.storage.BillingRequiredRead"
  requestMetadata: {
   callerIp:  "10.5.0.4"
   callerNetwork:  "//compute.googleapis.com/projects/perimeter-network/global/networks/__unknown__"
   destinationAttributes: {
   }
   requestAttributes: {
   }
  }
  resourceName:  "projects/1004338142803"
  serviceName:  "storage.googleapis.com"
  status: {
   code:  7
   details: [
    0: {
     @type:  "type.googleapis.com/google.rpc.PreconditionFailure"
     violations: [
      0: {
       type:  "VPC_SERVICE_CONTROLS"
      }
     ]
    }
   ]
   message:  "Request is prohibited by organization's policy"
  }
 }
 receiveTimestamp:  "2018-12-01T19:03:05.617451586Z"
 resource: {
  labels: {
   method:  "google.storage.BillingRequiredRead"
   project_id:  "corp-resources-private"
   service:  "storage.googleapis.com"
  }
  type:  "audited_resource"
 }
 severity:  "ERROR"
 timestamp:  "2018-12-01T19:03:05.420005215Z"
}

이 로그에서 1004338142803(corp-resources-private-1) 및 corp-resources-public이라는 두 프로젝트는 명령어를 완성하는 데 사용된 것이 분명합니다. 이러한 프로젝트는 경계를 공유하지 않으므로 추출 작업이 실패합니다.

이 예시에서는 복잡한 다중 서비스 작업에서 소스 서비스와 대상 서비스의 감사 로그에 유용한 디버깅 데이터가 포함될 수 있음을 보여줍니다.

다음 단계