문제 해결

이 페이지에는 VPC 서비스 제어를 구성하려 할 때 발생할 수 있는 다양한 문제가 나와 있습니다.

VPC 서비스 제어 오류 찾기

이 섹션에서는 감사 로그에서 VPC 오류를 찾는 두 가지 방법, 즉 오류의 고유 ID를 사용하거나 메타데이터를 사용하여 오류를 식별하는 방법을 자세하게 설명합니다.

오류의 고유 ID 사용

VPC 서비스 제어에서 생성된 오류에는 관련 감사 로그를 식별하는 데 사용되는 고유 ID가 포함됩니다.

이 절차에서는 Logging 문서의 용어를 사용합니다. 자세한 내용은 기본 로그 필터를 참조하세요.

고유 ID를 사용하여 오류에 대한 정보를 가져오려면 다음 안내를 따르세요.

  1. Cloud Console에서 오류를 트리거한 서비스 경계 내 프로젝트의 Stackdriver Logging 페이지로 이동합니다.

    Stackdriver Logging 페이지로 이동

  2. 검색 필터 상자에 오류의 고유 ID를 입력합니다.

메타데이터를 사용하여 로그 필터링

VPC 서비스 제어와 관련된 오류를 찾으려면 Cloud Logging을 사용합니다.

Console

이 절차에서는 Logging 문서의 용어를 사용합니다. 자세한 내용은 기본 로그 필터를 참조하세요.

Logging에서 지난 24시간 동안의 VPC 서비스 제어 오류를 확인하려면 다음 안내를 따르세요.

  1. Google Cloud Console에서 서비스 경계 내 프로젝트의 Stackdriver Logging 페이지로 이동합니다.

    Stackdriver Logging 페이지로 이동

  2. 검색 필터 상자에 다음을 입력합니다.

    protoPayload.metadata.@type:"type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata"
    
  3. 리소스 기본 선택기 메뉴에서 감사를 받은 리소스를 선택합니다.

  4. 시간 범위 선택기 드롭다운 메뉴에서 지난 24시간을 선택합니다.

  5. (선택사항) 다른 시간대에 발생한 VPC 서비스 제어 오류를 찾으려면 시간 범위 선택기 드롭다운 메뉴를 사용하여 범위를 변경하세요.

gcloud

지난 24시간 동안의 VPC 서비스 제어 오류를 확인하려면 다음 명령어를 사용하세요.

gcloud logging read 'protoPayload.metadata.@type:"type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata"'

기본적으로 read 명령어는 지난 24시간으로 제한됩니다. 다른 기간의 VPC 서비스 제어 로그를 확인하려면 다음 명령어 중 하나를 사용하세요.

현재 날짜를 기준으로 로그를 제한하려면 다음을 사용하세요.

gcloud logging read \
  'protoPayload.metadata.@type:"type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata"' \
  --freshness=DURATION

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

  • DURATION은 형식이 지정된 기간입니다. 형식 지정에 대한 자세한 내용은 gcloud상대적 기간/시간 형식을 참조하세요.

예를 들어 지난주 동안 발생한 모든 VPC 서비스 제어 오류를 확인하려면 다음을 사용하세요.

gcloud logging read \
  'protoPayload.metadata.@type:"type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata"' \
  --freshness=7d

로그를 특정 시간 프레임으로 제한하려면 다음을 사용하세요.

gcloud logging read \
  'protoPayload.metadata.@type:"type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata" AND
  timestamp>="DATETIME" AND
  timestamp<="DATETIME"'

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

  • DATETIME은 형식이 지정된 날짜/시간 문자열입니다. 형식 지정에 대한 자세한 내용은 gcloud절대적 날짜/시간 형식을 참조하세요.

예를 들어 2019년 3월 22일부터 2019년 3월 26일까지 발생한 모든 VPC 서비스 제어 오류를 확인하려면 다음을 사용하세요.

gcloud logging read \
  'protoPayload.metadata.@type:"type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata" AND
  timestamp>="2019-03-22T23:59:59Z" AND
  timestamp<="2019-03-26T00:00:00Z"'

미지원 서비스

VPC 서비스 제어가 지원하는 제품 및 서비스에 대한 자세한 내용은 지원되는 제품 페이지를 참조하세요.

gcloud 명령줄 도구 또는 Access Context Manager API를 사용하여 미지원 서비스를 제한하려는 경우 오류가 발생합니다.

지원되는 서비스 데이터에 대한 프로젝트 간 액세스는 VPC 서비스 제어에서 차단합니다. 또한 제한된 VIP는 미지원 서비스를 호출하는 워크로드의 기능을 차단하는 데 사용할 수 있습니다.

공유 VPC

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

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

경계 간 요청

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

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

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

경계 간 요청을 용이하게 하는 두 가지 방법은 다음과 같습니다.

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

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

VPC 서비스 제어로 인한 오류인지 확인

VPC 서비스 제어는 Google Cloud의 특정 저수준 속성을 수정하므로 어떤 서비스를 찾아야 하는지 알지 못하면 서비스에서 디버깅하기 어려울 수 있습니다.

VPC 서비스 제어와 관련된 오류인지 파악하려면 VPC 서비스 제어를 사용 설정한 후 사용하려는 프로젝트 및 서비스에 적용했는지 확인하세요. 이 작업은 gcloud 명령줄 도구 또는 Cloud Console을 통해 수행됩니다.

서비스 경계 내에 있는 프로젝트에서 VPC 서비스 제어가 제한된 서비스로 표시한 서비스를 (다른 서비스를 통해 간접적으로) 사용하는 경우 VPC 서비스 제어에 결함이 있을 수 있습니다.

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

일반적으로 서비스는 종속 항목의 오류 메시지를 전파합니다. 다음 오류 중 하나가 발생하면 VPC 서비스 제어에 문제가 있다는 의미입니다.

  • Cloud Storage: 403: Request violates VPC Service Controls.

  • BigQuery: 403: VPC Service Controls: Request is prohibited by organization's policy.

  • 기타 서비스: 403: Request is prohibited by organization's policy.

VPC 서비스 제어가 예기치 않은 이유로 차단한 요청 디버깅

VPC 서비스 제어 감사 로그는 VPC 서비스 제어가 요청을 차단한 이유를 디버깅하는 데 사용되는 기본 도구입니다.

예기치 않게 액세스가 차단되었다면 대부분의 경우에는 요청 소스인 프로젝트의 감사 로그를 참조하면 됩니다. 이 로그에는 요청된 리소스에 대한 중요한 데이터와 요청이 거부된 이유가 나와 있습니다.

로그를 보는 방법에 대한 자세한 내용은 로그 보기를 참조하세요.

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

violationReason 설명
RESOURCES_NOT_IN_SAME_SERVICE_PERIMETER 감사 로그 레코드의 resourceNames 아래에 나열된 리소스가 동일한 서비스 경계에 없습니다.
NETWORK_NOT_IN_SAME_SERVICE_PERIMETER 감사 로그 레코드에서 callerNetworkresourceNames에 해당하는 리소스가 동일한 서비스 경계에 없습니다.
NO_MATCHING_ACCESS_LEVEL

감사 로그 레코드의 callerIp에 해당하는 IP 주소가 서비스 경계에 할당된 액세스 수준의 IP 범위와 일치하지 않습니다.

시나리오 예시

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

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

이 예시에서 corp-storage 프로젝트의 Cloud Storage 버킷에 대한 직원 워크스테이션(callerIp로 식별)에서의 요청은 VPC 서비스 제어에 의해 차단됩니다.

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

{
 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입니다.

경계 내부의 VM에서 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입니다.

경계 외부의 VM에서 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 프로젝트입니다. 따라서 실패가 예상됩니다.

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

VPC 서비스 제어의 제한된 VIP가 지원되지 않는 API에 액세스하려고 하면 404 오류가 발생합니다. 예를 들어 현재 VPC 서비스 제어에서는 Cloud DNS가 지원되지 않으므로 제한된 VIP를 사용하는 경우 Cloud DNS API를 사용할 수 없습니다.

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

gcloud dns managed-zones list

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

ERROR: (gcloud.dns.managed-zones.list) Project [corp-resources-private] not found: <!DOCTYPE html>
<html lang=en>
  <meta charset=utf-8>
  <meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
  <title>Error 404 (Not Found)!!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>404.</b> <ins>That's an error.</ins>
  <p>The requested URL <code>/dns/v1/projects/corp-resources-private/managedZones</code> was not found on 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를 보호하는 경계 외부에 있으므로 내보내기가 실패합니다.

이 예시에서는 GCP 서비스가 p927005422713-439672@gcp-sa-logging.iam.gserviceaccount.com과 같은 GCP 내부에 있는 GCP 관리형 서비스 계정을 사용하여 다른 GCP 서비스를 호출할 경우 요청의 '네트워크 프로젝트'(이 경우 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 서비스 제어를 암시하지 않습니다(ID 및 액세스 관리 오류가 발생한 경우 유사한 오류가 표시됨).

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

{
 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이라는 두 프로젝트는 명령어를 완성하는 데 사용된 것이 분명합니다. 이러한 프로젝트는 경계를 공유하지 않으므로 추출 작업이 실패합니다.

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