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.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
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): 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.'