gcp-iap-tunnel-instance-access

A principal with iap.tunnelInstances.accessViaIAP can open a TCP tunnel through Google IAP to any port on the target GCE instance (SSH/22, RDP/3389, or any custom TCP port) without the instance having a public IP. This provides direct network-level reach to a private VM from any location with HTTPS access to Google's infrastructure.

derived gcp emits CanNetworkReach

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'principal': None} HasPermission {'instance': None}

where

node_type(?instance) == VirtualMachine effective_permission(?principal, ?instance) contains 'iap.tunnelInstances.accessViaIAP' ?instance.provider == 'gcp'

emit

source typeIdentity
target typeVirtualMachine
source?principal
target?instance
permissionsiap.tunnelInstances.accessViaIAP compute.instances.get
conditionsiam_permission network_reachability
state logicif effective_permission(?principal, ?instance) contains 'iap.tunnelInstances.accessViaIAP' AND vpc_firewall_allows_ingress(?instance, '35.235.240.0/20', any_port): ACTIVE elif iam_permission present but firewall rule not confirmed: CONDITIONAL(network_reachability) elif iam_deny_policy or org_policy blocks iap.tunnelInstances.accessViaIAP: BLOCKED else: CONDITIONAL(iam_permission, network_reachability)

Narrative

{principal.name} holds iap.tunnelInstances.accessViaIAP on {instance.name} and can open an IAP TCP tunnel to any port on this private instance (gcloud compute start-iap-tunnel) without the instance needing a public IP. If the VPC firewall allows ingress from 35.235.240.0/20 on the target port, this provides direct network reach equivalent to being on the instance's VPC.

Raw rule rules/derived/gcp/iap.yaml

id: gcp-iap-tunnel-instance-access
emits: CanNetworkReach
description: A principal with iap.tunnelInstances.accessViaIAP can open a TCP tunnel through Google IAP
  to any port on the target GCE instance (SSH/22, RDP/3389, or any custom TCP port) without the instance
  having a public IP. This provides direct network-level reach to a private VM from any location with
  HTTPS access to Google's infrastructure.
match:
- - principal: null
  - HasPermission
  - instance: null
where:
- node_type(?instance) == VirtualMachine
- effective_permission(?principal, ?instance) contains 'iap.tunnelInstances.accessViaIAP'
- ?instance.provider == 'gcp'
emit:
  source_type: Identity
  target_type: VirtualMachine
  source: ?principal
  target: ?instance
  permissions:
  - iap.tunnelInstances.accessViaIAP
  - compute.instances.get
  conditions:
  - iam_permission
  - network_reachability
  state_logic: "if effective_permission(?principal, ?instance) contains 'iap.tunnelInstances.accessViaIAP'\
    \ AND vpc_firewall_allows_ingress(?instance, '35.235.240.0/20', any_port):\n  ACTIVE\nelif iam_permission\
    \ present but firewall rule not confirmed:\n  CONDITIONAL(network_reachability)\nelif iam_deny_policy\
    \ or org_policy blocks iap.tunnelInstances.accessViaIAP:\n  BLOCKED\nelse:\n  CONDITIONAL(iam_permission,\
    \ network_reachability)"
  confidence: 0.9
  derived_from:
  - HasPermission(?principal, ?instance) [iap.tunnelInstances.accessViaIAP]
  false_positive_note: "Two independently confirmed facts are required for ACTIVE state: (1) The IAM binding\
    \ granting iap.tunnelInstances.accessViaIAP to ?principal\n    on ?instance (or at zone/project/folder/org\
    \ level, which inherits down).\n(2) A VPC firewall ingress rule permitting traffic from 35.235.240.0/20\n\
    \    (Google IAP TCP forwarding IP range) on the target port(s). Without this\n    firewall rule,\
    \ the IAP tunnel is authenticated but the TCP connection is\n    dropped at the GCE firewall \u2014\
    \ downgrade to CONDITIONAL(network_reachability).\nAdditionally: iap.tunnelInstances.accessViaIAP\
    \ alone yields CanNetworkReach to the TCP port, NOT CanExecuteCommand. SSH/RDP login additionally\
    \ requires OS Login permission (compute.instances.osLogin / compute.instances.osAdminLogin) or a metadata\
    \ SSH key injected on the instance. Those paths are closed by gce.yaml (gcp-gce-os-login, gcp-gce-set-metadata-ssh-keys).\
    \ Do not conflate TCP tunnel reachability with interactive code execution. compute.instances.get and\
    \ compute.instances.list are needed by the gcloud wrapper but are recon permissions, not the authorization\
    \ gate."
  narrative: '{principal.name} holds iap.tunnelInstances.accessViaIAP on {instance.name} and can open
    an IAP TCP tunnel to any port on this private instance (gcloud compute start-iap-tunnel) without the
    instance needing a public IP. If the VPC firewall allows ingress from 35.235.240.0/20 on the target
    port, this provides direct network reach equivalent to being on the instance''s VPC.'
move · open · esc close