一般的な問題のトラブルシューティング

このページでは、VPC Service Controls の構成時に発生する可能性のあるさまざまな問題について説明します。

スコープが設定されたポリシーの予期しない動作

スコープが設定されたポリシーで許可されている予期しない VPC Service Controls 違反が表示されることがあります。組織レベルのアクセス ポリシーがない場合、スコープを持つアクセス ポリシーに予期しない問題が発生する可能性があることは既知の問題です。

この問題を解決するには、次のコマンドを使用して、組織レベルでアクセス ポリシーを作成します。

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 ネットワーク ホストが、ネットワークに接続されているプロジェクトと同じサービス境界にあることを確認してください。

VPC ネットワークを追加できない

VPC ネットワークをサービス境界に追加しようとすると、次のエラーが発生することがあります。

ERROR: (gcloud.access-context-manager.perimeters.update) PERMISSION_DENIED: Permission 'compute.networks.get' denied on resource '//compute.googleapis.com/projects/PROJECT_NAME/global/networks/VPC_NETWORK_NAME' (or it may not exist)

このエラーは、次のいずれかの理由によって発生します。

  • VPC ネットワークが存在しない。
  • VPC ネットワークは存在するが、サブネットが存在しない。
  • 呼び出し元に必要な権限がない。

この問題を解決するには、次の操作を行います。

  1. プロジェクトのネットワークを表示して、エラーメッセージで指定されている VPC ネットワークが存在するかどうかを確認します。

    • VPC ネットワークを確認する前に、次の手順を実行して、API 呼び出しに関連付けられているプロジェクトで Compute Engine API が有効になっていることを確認します。

      1. Google Cloud コンソールで、[API とサービス] ページに移動します。
        [API とサービス] に移動

      2. [API とサービス] ページで、Compute Engine API が一覧表示されているかどうかを確認します。

      3. Compute Engine API が見つからない場合は、API を有効にします。
        API を有効にする

  2. サブネットを表示して、VPC ネットワークに少なくとも 1 つのサブネットが存在するかどうかを確認します。サブネットが存在しない場合は、VPC ネットワークにサブネットを追加します。

  3. 呼び出し元に、VPC ネットワークのホスト プロジェクトに対する compute.networks.get 権限があるかどうかを確認します。この権限を使用して、プロジェクト内の VPC ネットワークを表示できます。

    • VPC ネットワークのホスト プロジェクトを所有する組織の管理者に、ホスト プロジェクトに対する compute.networks.get 権限を持つ IAM ロールを呼び出し元に付与するよう依頼します。Compute ネットワーク閲覧者のロールなどが当てはまります。

      ロールの付与の詳細については、アクセスの管理をご覧ください。

サービス境界での VPC ネットワークの使用に関する制限事項を必ずお読みください。

境界間のリクエスト

通常、アクセスレベルは、サービス境界内部の保護されたリソースに対し、サービス境界の外部からのリクエストを許可するために使用されます。

ただし、別の境界内の保護されたリソースに対する境界内のプロジェクトからのリクエストは、アクセスレベルによって通常は許可されるリクエストの場合でも拒否されます。

たとえば、境界 1 のプロジェクト A がプロジェクト B にあるリソースをリクエストするとします。プロジェクト B にあるそのリソースは、境界 2 で保護されています。プロジェクト A は境界内にあるため、通常は境界 2 のアクセスレベルによって許可されるような保護されたリソースに対するリクエストであっても、そのリクエストは拒否されます。

境界間のリクエストを円滑に進めるには、次のいずれかの方法を使用します。

  • 下り(外向き)ポリシーと上り(内向き)ポリシーを使用する。 境界内の保護されたリソースに対する別の境界からのリクエストを許可するには、相手の境界で下り(外向き)ポリシーを使用し、自分の境界で上り(内向き)ポリシーを設定する必要があります。

  • 境界ブリッジを使用する。 ブリッジを使用すると、異なる境界の 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 Service Controls によってブロックされたリクエストのデバッグ

VPC Service Controls によってブロックされたリクエストを調査する場合、VPC Service Controls の監査ログが基本的なツールとなります。

アクセスが予期せずにブロックされた場合は、サービス境界で保護されているプロジェクトの監査ログを確認します。このログには、リクエストされたリソースやリクエストが拒否された理由に関する重要なデータが記録されています。監査ログについては、トラブルシューティングを使用して問題を診断するをご覧ください。

以降のセクションでは、VPC Service Controls の使用時に発生する可能性のある 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

この問題は、1 つのリクエストが複数のリソースにアクセスしていて、リソースが同じサービス境界内にない場合に発生します。この問題は、クライアントが配置されている場所や、クライアントがリソースにアクセスできるかどうかにかかわらず発生します。

次の図は、クライアントが境界外のプロジェクトとサービス境界内のプロジェクトからリソースにアクセスする様子を示しています。

クライアントが境界外のプロジェクトからリソースにアクセスするために発生する下り(外向き)違反。

次の図では、クライアントが 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 Service Controls に統合されていない 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 Service Controls でサポートされていない 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 Service Controls の使用中に発生する可能性がある問題について説明します。

オンプレミスからの Cloud Storage へのアクセス

この例では、VPC Service Controls は従業員のワークステーション(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 へのアクセス

この例では、プロジェクト 458854174376data-collector)に属する VM が、プロジェクト 798816221974corp-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@developer.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 です。callerIp の他に callerNetwork が含まれています。IP アドレスはプライベートであり、曖昧さを解消するためにネットワークが使用されています。ここで問題に関連するリソースが VpcServiceControlAuditMetadata.resourceNamesrequestMetadata.callerNetwork (ネットワークを所有するプロジェクト)の 2 つの場所に記録されています。

問題は、corp-resources-protected プロジェクトがサービス境界内にある一方、VM が所属するネットワークを含む data-collector プロジェクトが同じ境界内に存在していないことです。この場合、アクセスの拒否は想定どおりの結果です。

プロジェクト間の BigQuery クエリ

この例では、perimeter-network プロジェクトに属する VM が、perimeter-network と同じサービス境界内に存在するプロジェクト corp-resources-protected と、存在しないプロジェクト corp-resources-public の 2 つの異なるプロジェクトの 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-network117961063178corp-resources-public)、690885588241corp-resources-protected)の 3 つのプロジェクトが確認できます。corp-resources-public は、perimeter-networkcorp-resources-protected と同じサービス境界内に存在しないことを思い出してください。

violationReasonRESOURCES_NOT_IN_SAME_SERVICE_PERIMETER の場合、リクエストの一部のリソースが、リクエストに適用される境界の外側にあることを意味しています。この場合、corp-resources-public が境界外のリソースです。

境界内の Cloud Storage ファイルの移動

この例では、プロジェクト perimeter-network 内の VM がコマンドを使用して、プロジェクト corp-resources-protected にある 1 つの Cloud Storage バケットからプロジェクト corp-resources-public にある別のバケットにファイルを移動しようとしています。

VM は次のコマンドを使用します。

gcloud storage 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 Service Controls の現在の監査ログ機能の制限です。

理由は明確ではありませんが、この監査ログレコードは、リクエストの一部のリソースがリクエストに適用される境界の外側にあることを示しています。この場合、corp-resources-public が境界外のリソースです。

境界外の Cloud Storage ファイルの移動

この例では、プロジェクト public-network 内の VM がコマンドを使用して、プロジェクト corp-resources-protected にある 1 つの Cloud Storage バケットからプロジェクト corp-resources-public にある別のバケットにファイルを移動しようとしています。

corp-resources-protected プロジェクトはサービス境界によって保護されています。public-network プロジェクトと corp-resources-public プロジェクトは境界外に存在します。

VM は次のコマンドを使用します。

gcloud storage 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)から発生し、バケットの 1 つが境界(corp-resources-public-1)の外側に存在していることを思い出してください。

境界の外側から corp-resources-public-1 バケットへの書き込みはできるため、前の例で失敗したチェックはパスします。ただし、データを実際にコピーする際のチェックは失敗します。

この例でわかるように、ユーザーの 1 回の操作で複数の内部オペレーションが発生し、それぞれのオペレーションに VPC Service Controls のチェックが実行されます。

境界内の VM からの BigQuery データセットのコピー

この例では、プロジェクト 927005422713perimeter-network)の VM がプロジェクト corp-resources-private から corp-resources-public117961063178)に BigQuery データセットをコピーしようとしています。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 の分散アーキテクチャの制限により、1 つの API アクションで、このリクエストで使用されているすべてのリソースを確認することはできません。

監査ログレコードを見ると、データをコピーするために、BigQuery がプロジェクト perimeter-network(リクエストの送信元)のネットワークを使用してターゲット プロジェクト(corp-resources-public)にアクセスする必要があるため、オペレーションが失敗しています。corp-resources-public は、perimeter-network を保護する境界の外側にあることを思い出してください。このリクエストは、corp-resources-public にデータを抜き出そうとする試みとして拒否されます。

この例でもわかるように、データのコピーのような 1 つのオペレーションが、Cloud Storage、BigQuery、Bigtable など、さまざまなストレージ システムのデータにアクセスする複数の処理の引き金となる可能性があります。オペレーションの実行方法によっては、生成される監査ログレコードが元のユーザー コマンドとは異なる場合があります。また、特定のサービス内で複数のチェックが行われ、チェックに失敗すると、生成された監査ログレコードが元のユーザー コマンドと異なるように見えることもあります。

プロジェクトからの Dataproc ジョブの読み込み

この例は、Dataproc などのデータ処理サービスの使用中に間接的に発生した VPC Service Controls のエラーをデバッグする方法を示しています。

この例では、VPC Service Controls で保護されたプロジェクトで Dataproc クラスタが稼働しています。Hello-world.py は、境界内の Cloud Storage バケットのデータにアクセスし、境界外の別のバケットに書き込む pyspark ジョブです。VPC Service Controls は、境界外のバケットにデータを書き込むオペレーションをブロックします。

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 によって、403 エラーとメッセージ Request violates VPC Service Controls が返されます。このエラーは、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@developer.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"
}

監査ログの制限により、実際のオペレーションが write であっても、Cloud Storage の methodNameRead と表示されます。監査ログレコードを見ると、プロジェクト corp-resources-private 内のネットワークがバケット corp-resources-public-1 のリソースのデータにアクセスを試みたため(この場合は書き込み)、オペレーションが失敗しています。Cloud Storage の監査ログに制限があるため、プロジェクト バケット corp-resources-public-1 の所属先がわかりません。

corp-resources-public-1 を含むプロジェクトを識別するには、次のコマンドを使用します。

gcloud storage ls gs://corp-resources-public-1 --buckets --log-http 2>&1 | grep projectNumber

このコマンドは "projectNumber": "117961063178", の出力を返します。

117961063178 はプロジェクト corp-resources-public で、これは境界外にあります。つまり、このエラーは予想されたものです。

サポートされていないサービスが原因でエラーが発生した

一部の Google Cloud サービスは、実装時に他の Google Cloud サービスに依存しています。境界で保護されているプロジェクト内で、App Engine などのサポートされていないサービスが使用されている場合、サービスのリソースにアクセスできないことがあります。

既知の問題の事例については、サービスに関する既知の制限事項をご覧ください。

制限付き VIP でサポートされていないサービス

VPC Service Controls の制限付き VIP でサポートされていない API にアクセスを試みると、403 エラーが発生します。 たとえば、VPC Service Controls は 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 Service Controls でサポートされず、制限付き VIP で使用できないサービスの場合、このタイプのエラーは予想されるエラーです。このエラーが VPC Service Controls でサポートされているサービスで発生した場合は、そのサービスをサービスに関する既知の制限事項で調べて、サービスが既知の制限事項に該当するかどうか確認することをおすすめします。該当していない場合は、問題を報告する必要があります

境界外のプロジェクトへのログのエクスポート

この例では、ログのエクスポートが VPC Service Controls でブロックされています。エクスポート先のプロジェクト corp-resources-public は VPC Service Controls の境界外にある一方、シンクはプロジェクト 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-public がプロジェクト perimeter-network を保護する境界の外側に存在するため、エクスポートが失敗します。

この例で示すように、ある Google Cloud サービスが Google Cloud 内部のマネージド サービス アカウント(p927005422713-439672@gcp-sa-logging.iam.gserviceaccount.com など)を使用して別のサービスを呼び出す場合、リクエストの「ネットワーク プロジェクト」(この場合、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 Service Controls に固有のエラーではありません。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 です。この例では、コマンドの実行に必要な IAM 権限が storage-accessing@example.iam.gserviceaccount.com にあるとします。

IAM 権限は原因ではないため、次に VPC Service Controls のエラーかどうかを確認します。

宛先のサービス(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"
}

このログを見ると、コマンドを完了するために、2 つのプロジェクト 1004338142803corp-resources-private-1)と corp-resources-public が使用されていることがわかります。これらのプロジェクトは境界を共有していないため、抽出ジョブが失敗しています。

この例が示しているように、複雑なマルチサービス オペレーションの場合、元のサービスと宛先のサービスの両方の監査ログに、デバッグに役立つデータが記録されている可能性があります。

Cloud NAT ゲートウェイを介した境界アクセス

この例では、組織 A のプロジェクト A が境界内に構成されていないとします。プロジェクト B は別の組織の境界で保護されています。プロジェクト A のプライベート リソースは、Cloud NAT ゲートウェイを使用してインターネット、Google API、サービスにアクセスします。プロジェクト A の外部ゲートウェイ IP アドレスに基づいてアクセスを許可するように、プロジェクト B でアクセスレベルが構成されています。

プロジェクト A(Google Kubernetes Engine ノード)に属する VM がプロジェクト B の保護されたリソースにアクセスしようとしていますが、接続は失敗し、次の監査ログレコードがプロジェクト B に生成されます。

{
  "protoPayload": {
    "@type": "type.googleapis.com/google.cloud.audit.AuditLog",
    "status": {
      "code": 7,
      "message": "Request is prohibited by organization's policy. vpcServiceControlsUniqueIdentifier: kmpY9Fgfuhgi2NE90lURjFWuiS1nGRqxCw4L12HdW8h46Un__-_LZw",
      "details": [
        {
          "@type": "type.googleapis.com/google.rpc.PreconditionFailure",
          "violations": [
            {
              "type": "VPC_SERVICE_CONTROLS",
              "description": "kmpY9Fgfuhgi2NE90lURjFWuiS1nGRqxCw4L12HdW8h46Un__-_LZw"
            }
          ]
        }
      ]
    },
    "authenticationInfo": {
      "principalEmail": "my-user@example.iam.gserviceaccount.com",
      "serviceAccountKeyName": "//iam.googleapis.com/projects/my-project/serviceAccounts/my-user@example.iam.gserviceaccount.com/keys/<code><var>ACCOUNT_KEY</var></code>"
    },
    "requestMetadata": {
      "callerIp": "gce-internal-ip",
      "requestAttributes": {},
      "destinationAttributes": {}
    },
    "serviceName": "cloudfunctions.googleapis.com",
    "methodName": "google.cloud.functions.v1.CloudFunctionsService.ListFunctions",
    "resourceName": "<code><var>PROJECT_ID_1</var></code>",
    "metadata": {
      "violationReason": "NETWORK_NOT_IN_SAME_SERVICE_PERIMETER",
      "resourceNames": [
        "projects/<code><var>PROJECT_ID_2</var></code>/locations/-"
      ],
      "securityPolicyInfo": {
        "servicePerimeterName": "accessPolicies/<code><var>ACCESS_POLICY</var></code>/servicePerimeters/us_sandbox",
        "organizationId": "<code><var>ORGANIZATION_ID</var></code>"
      },
      "deviceState": "Unknown",
      "vpcServiceControlsUniqueId": "kmpY9Fgfuhgi2NE90lURjFWuiS1nGRqxCw4L12HdW8h46Un__-_LZw",
      "ingressViolations": [
        {
          "targetResource": "projects/<code><var>PROJECT_ID_1</var></code>",
          "servicePerimeter": "accessPolicies/<code><var>ACCESS_POLICY</var></code>/servicePerimeters/<code><var>PERIMETER_NAME</var></code>",
          "source": "<code><var>PROJECT_ID_2</var></code>"
        }
      ],
      "@type": "type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata"
    }
  },
  "insertId": "tzf7fd103i",
  "resource": {
    "type": "audited_resource",
    "labels": {
      "service": "cloudfunctions.googleapis.com",
      "method": "google.cloud.functions.v1.CloudFunctionsService.ListFunctions",
      "project_id": "<code><var>PROJECT_ID_2</var></code>"
    }
  },
  "timestamp": "2024-04-02T19:56:10.770681816Z",
  "severity": "ERROR",
  "logName": "projects/<code><var>PROJECT_ID_2</var></code>/logs/cloudaudit.googleapis.com%2Fpolicy",
  "receiveTimestamp": "2024-04-02T19:56:11.463811603Z"
}

callerIp リソースは外部 IP アドレスを記録しません。Cloud NAT ゲートウェイの外部 IP アドレスではなく、callerIp リソースには gce-internal-ip が表示されます。

リクエストが別のプロジェクトまたは組織から発信され、ソースの Compute Engine VM に外部 IP アドレスがない場合は、callerIp フィールドが gce-internal-ip に編集されます。

Cloud NAT限定公開の Google アクセスと統合されており、リソースのサブネットで限定公開の Google アクセスが自動的に有効になり、Google API とサービスへのトラフィックが内部に保持されます。Cloud NAT ゲートウェイの外部 IP アドレスを使用してインターネットに転送されることはありません。

この場合、トラフィックは Google 内部ネットワーク内でルーティングされるため、AuditLog オブジェクトの RequestMetadata.caller_ip フィールドは gce-internal-ip に編集されます。この問題を解決するには、IP ベースの許可リストのアクセスレベルで Cloud NAT ゲートウェイの外部 IP アドレスを使用する代わりに、プロジェクトまたはサービス アカウントからのアクセスを許可するように上り(内向き)ルールを構成します。

次のステップ