In Google Cloud, a stolen service account key or an over-broad IAM binding is often enough to copy a whole BigQuery dataset or Cloud Storage bucket to a project the attacker controls, and IAM alone cannot stop it. VPC Service Controls adds a second, context-based boundary around Google-managed services, so that even a valid credential is refused when the request comes from the wrong network, targets a project outside the perimeter, or tries to move data across it. It is the main data exfiltration control on the platform, and also one of the easiest to get wrong: perimeters left permanently in dry run, ingress rules that allow ANY_IDENTITY from any source, and VPC networks that can still reach the public Google API endpoints. This guide is for cloud security engineers and platform teams who operate perimeters. It goes deeper than the Google Cloud Security Guide, which covers the platform as a whole.
Key takeaways
- Perimeters complement IAM, they do not replace it. IAM decides who may call an API; a service perimeter decides from where, and to which projects, that call may move data. A request must pass both.
- Ingress and egress rules are the precise tool. Google recommends them over perimeter bridges, and each rule should name specific identities, sources, services, methods and projects rather than wildcards.
- Dry run is a stage, not a destination. Use it to collect violations for a defined test window, fix the configuration, then enforce. A dry-run-only perimeter protects nothing.
- The network path matters. Route
*.googleapis.comtorestricted.googleapis.com, set VPC accessible services toRESTRICTED-SERVICES, and remove other routes to the internet, or workloads can sidestep the perimeter. - Service accounts are the main subject of every rule. Most legitimate cross-perimeter traffic is machine traffic, so the quality of your perimeter depends on knowing which service accounts and workloads cross it and why.
How identity and access work in VPC Service Controls
VPC Service Controls is configured through Access Context Manager. An access policy (organisation-level, or a scoped policy limited to particular folders or projects) holds two kinds of object: access levels and service perimeters. Every call to a protected Google API is evaluated against IAM first and against the perimeter configuration as well; a denial from either blocks the request.
- Service perimeter: a boundary around a set of projects (and, through rules, VPC networks) and a list of restricted services such as
storage.googleapis.comorbigquery.googleapis.com. Resources inside can communicate freely; calls that cross the boundary are denied unless a rule allows them. Perimeters cover Google-managed services only; they do not filter traffic to third-party APIs on the internet. - Access level: a classification of requests arriving from the internet, based on attributes such as source IP range, device attributes and geolocation. A perimeter can admit requests that match an access level.
- Ingress rule: allows a client outside the perimeter to reach resources inside it. It has an
ingressFromblock (who:identitiesor anidentityType, plussourcesthat can be access levels, resources or Private Service Connect endpoints) and aningressToblock (what:operationswith service names and method or permission selectors, or a list of IAMroles, plus targetresources). - Egress rule: allows a client or resource inside to reach Google Cloud resources outside. It has
egressFrom(identities, and optionallysources, enforced only whensourceRestrictionisSOURCE_RESTRICTION_ENABLED) andegressTo(operations or roles, outsideresources, andexternalResourcesfor BigQuery Omni locations in Amazon S3 or Azure Blob Storage). - Perimeter bridge: lets projects in two perimeters communicate. Bridges are bidirectional and give equal access within their scope, which is why Google documents ingress and egress rules as the preferred, more granular option.
How rules are evaluated
- Within one rule, the
sourcescondition and the identity condition are combined with AND; entries insidesourcesare combined with OR. - A request is allowed if it matches any one ingress or egress rule.
- When a request touches resources in two perimeters, both perimeters must allow it: typically an egress rule on one side and an ingress rule on the other.
- When one request needs several permissions, all of them must be listed under the same operation in a single rule.
- Resources are identified by project, folder or organisation, never by labels.
| identityType value | Matches | Use it when |
|---|---|---|
ANY_IDENTITY | Every identity, including unauthenticated requests | Almost never for ingress to data services; consider only for deliberately public content and document the exception |
ANY_USER_ACCOUNT | All human user accounts (not restricted to your organisation) | Combined with a tight access level, for example a corporate IP range plus managed device |
ANY_SERVICE_ACCOUNT | All service accounts (not restricted to your organisation) | Rarely; prefer named service accounts in identities |
Named identities | Specific users, groups or service accounts | The default choice for every production rule |
Neither ANY_USER_ACCOUNT nor ANY_SERVICE_ACCOUNT limits matches to your organisation, so a rule using them with a loose source admits identities from any Google account that also holds the IAM permission. Google also documents operations where these two values cannot be used, including Cloud Storage access with signed URLs and exporting logs from a Cloud Logging sink to Cloud Storage; those need named identities.
Admin roles and privileged access
Whoever can edit a perimeter can open an exfiltration path with a single ingress or egress rule, so Access Context Manager roles are privileged access and belong in your PAM scope.
| Role | What it allows | Who should hold it |
|---|---|---|
roles/accesscontextmanager.policyAdmin (Access Context Manager Admin) | View and configure access policies, perimeters and access levels | A small platform security group, ideally through just-in-time elevation |
roles/accesscontextmanager.policyEditor (Access Context Manager Editor) | Read-write access, including creating organisation-level or scoped policies | The CI/CD service account that applies perimeter configuration from code |
roles/accesscontextmanager.policyReader (Access Context Manager Reader) | Read-only access to policies | Security operations, auditors, application teams diagnosing denials |
- Grant at the right level: Access Context Manager permissions take effect at the organisation or on an individual access policy. Grants on folders or projects have no effect on scoped policies, and organisation-level grants apply to every policy, including scoped ones.
- Use scoped policies for delegation: a business unit can manage the perimeter for its own folder through a scoped policy, with IAM bindings on that policy, without being able to touch the organisation-level perimeters.
- Separate perimeter authors from data owners: people who use a dataset should not approve the rule that lets it leave.
- Remember IAM still governs metadata: Google notes that perimeters do not fully control metadata access, so custom IAM roles remain necessary for least privilege. See the Google Cloud IAM and Service Accounts Guide.
Integrations, API tokens and service accounts
Most violations in production come from machines: pipelines, partner exports, SaaS connectors and Google service agents. Treat each crossing as a documented NHI flow.
- Name the service account: write ingress rules with the exact service account email in
identities, the exact service and methods inoperations, and the exact target project inresources. - Bind the source too: pair the identity with a source, such as an access level for the partner's egress IP range or the specific VPC network resource the workload runs in. A stolen key used from elsewhere then fails the perimeter check even though IAM accepts it. See the Google Cloud Service Account Keys Security Guide.
- Prefer keyless workloads: external CI systems and other clouds should use workload identity federation rather than exported keys; the federated principal can still be named in rules. See the Workload Identity Federation Guide.
- Expect Google-internal callers: during enablement, Google advises that a
privatecaller IP in a violation indicates traffic from within Google's network, for example log sink writers, which must be allowed explicitly. - Cross-perimeter key use: using a Cloud KMS key in one perimeter to protect a disk created outside it needs an egress rule in the perimeter that contains the key project. See the Cloud KMS and Cloud HSM Guide.
- Unsupported services: not every Google Cloud service is supported, and services that are not supported can fail when Cloud Storage or Pub/Sub underneath them is restricted. Run unsupported services in a project outside the perimeter and allow only the specific flows they need.
Common crossing patterns and the rule each needs
| Flow | Rule needed | Tightening |
|---|---|---|
| External client downloads objects from a bucket inside | Ingress | Named identity, access level for its IP range, storage.googleapis.com read methods only |
| VM in an outside VPC writes to a bucket inside | Ingress with the VPC network as source resource | Named service account, write methods only, single target project |
| Inside client copies objects to a bucket outside | Egress | Named identity, single outside project, ideally sourceRestriction enabled |
| BigQuery job in an outside project queries an inside dataset | Egress (and ingress if the caller is outside) | Specific job and read methods, specific projects on both sides |
| Disk outside uses a KMS key inside | Egress in the key's perimeter | KMS encrypt and decrypt methods only, specific disk project |
Logging and detection
Perimeter decisions are recorded in Cloud Audit Logs, and both denials and configuration changes are logged without you having to enable anything. See the Google Cloud Audit Logs and Log Sinks Guide for sink design.
- Policy Denied logs: written to
cloudaudit.googleapis.com/policywhenever a perimeter blocks a user or service account. They cannot be disabled, but they can be excluded from the_Defaultsink, so check that nobody has done so. - Key fields:
violationReason,securityPolicyInfo(perimeter name and owning organisation),ingressViolationsandegressViolations(which side denied and why),dryRun(true for dry-run evaluations) andvpcServiceControlsUniqueId, which matches thevpcServiceControlsUniqueIdentifiera caller sees in the error message. - Configuration changes: Access Context Manager writes Admin Activity entries under
accesscontextmanager.googleapis.com, for exampleUpdateServicePerimeter; changes to rules show instatus.ingressPoliciesandstatus.egressPolicies.
| violationReason | Typical meaning | Detection value |
|---|---|---|
RESOURCES_NOT_IN_SAME_SERVICE_PERIMETER | The request moves data between a protected project and one outside, or in another perimeter | Highest: the signature of copy-to-attacker-project attempts (ATT&CK T1537) |
NO_MATCHING_ACCESS_LEVEL | Internet request without a matching access level | High when the identity is a service account: possible stolen key used from outside (T1078.004) |
NETWORK_NOT_IN_SAME_SERVICE_PERIMETER | Call from a VPC network that is not in the perimeter | Medium: misplaced workload or lateral movement attempt |
SERVICE_NOT_ALLOWED_FROM_VPC | Service not in the VPC accessible services list | Usually configuration; review before adding the service |
Metrics that show the perimeter is working
- Percentage of projects holding sensitive data that sit in an enforced perimeter (target 100 percent).
- Number of perimeters, or projects, that have been in dry run only for more than 30 days.
- Number of ingress or egress rules using
ANY_IDENTITY,ANY_SERVICE_ACCOUNTor"*"resources, trending to zero. - Enforced denials per week for service accounts, each triaged within one working day.
Hardening baseline
This baseline follows Google's documented enablement steps.
- Protect all supported services: in a perimeter that holds sensitive data, add every supported service under restricted services, not just the ones in use today. A service enabled later is otherwise an open door.
- Set VPC accessible services: include
RESTRICTED-SERVICESso endpoints inside the perimeter can reach only the services the perimeter protects, and the list stays in sync as protected services change. - Use the restricted VIP: create a private Cloud DNS zone for
googleapis.comwith a CNAME from*.googleapis.comtorestricted.googleapis.com, and A records for199.36.153.4to199.36.153.7(IPv62600:2d00:0002:1000::/56). This range is not announced to the internet. - Close other paths: VMs should have private IP addresses only, with Private Google Access on the subnet. Add an egress deny firewall rule with higher-priority allows for the VIP ranges on TCP 443, and remove routes to the default internet gateway that are not needed. See the Google Cloud VPC and Cloud NGFW Security Guide.
- On-premises access: reach the VIP over Cloud VPN or Cloud Interconnect, advertise the restricted range with Cloud Router custom advertisement, and point on-premises DNS at a Cloud DNS inbound forwarder.
- Manage configuration as code: keep access policies, levels and perimeters in Terraform or similar, applied by a dedicated service account with Access Context Manager Editor, with peer review on every rule change.
- Back it with organisation policy: constraints such as domain restricted sharing and disabling service account key creation reduce how often the perimeter is the last line. See the Google Cloud Organization Policy Guide.
Phased rollout
- Coordinate: name an owner who runs meetings between the platform team and each application team, and agree how violations are raised and resolved.
- Document access patterns: for each workload list data access, resource access and endpoint access, with actors, triggers, whether the flow should be allowed and any assumptions.
- Build the dry-run perimeter: add the projects, protect all supported services, set accessible services to
RESTRICTED-SERVICES, add known legitimate flows as ingress rules or access levels, and confirm DNS and routing as above. - Collect evidence: create an aggregated sink of
cloudaudit.googleapis.com%2Fpolicyto BigQuery, then have the application team run every use case in an agreed window, with full coverage of code that calls Google APIs. - Analyse: query the
cloudaudit_googleapis_com_policytable by time window and project, decide for each identity, IP range, project and network whether it should cross, and convert the approved ones into narrow rules. - Enforce: apply the dry-run configuration. Bulk enforcement with the Access Context Manager
commitmethod enforces every modified perimeter in the policy at once, so coordinate before using it; otherwise update enforced configurations in order (remove a project from the old perimeter before adding it to the new one). - Roll back safely: keep the previous enforced configuration in version control. If enforcement breaks a critical flow, add a narrow temporary ingress or egress rule with an expiry ticket rather than removing the project from the perimeter.
- Repeat per workload: onboard business-critical data first, then less critical workloads, and keep a standing dry-run configuration for testing future changes.
Note that access levels have no dry-run version (test with a new access level), and a project can sit in only one enforced and one dry-run perimeter.
Choosing a perimeter design
| Design | Strength | Trade-off | Fits |
|---|---|---|---|
| One large perimeter around all production projects | Simple, few cross-perimeter rules, strong outer boundary | No separation between workloads inside; a compromise in one project can reach data in all | Smaller organisations, early adoption |
| Perimeter per data domain or environment | Limits blast radius; matches data ownership | More ingress and egress rules to maintain; more change requests | Larger organisations with distinct regulated datasets |
| Scoped policies per business unit | Delegated administration without organisation-wide rights | Needs a central standard so units do not drift | Federated enterprises |
| Bridges between perimeters | Quick to set up | Bidirectional and coarse; Google recommends rules instead | Legacy configurations being migrated |
If sensitive and non-sensitive data share a project, fix the project structure first: rules cannot target labels.
Common misconfigurations
- Permanent dry run: the perimeter was built, violations were noisy, and nobody enforced it. Audit logs fill with
dryRun: trueentries while data can still leave. - Wildcard rules: ingress rules with
ANY_IDENTITYandaccessLevel: "*", or egress rules to"*"resources, added to stop a denial and never narrowed. These make the perimeter decorative. - Egress sources ignored: egress rules list
sourcesbut do not setsourceRestrictiontoSOURCE_RESTRICTION_ENABLED, so the source condition is silently ignored. - Open network path: DNS still resolves Google APIs to public addresses, or a route to the internet remains, so workloads can reach services that the perimeter does not protect, or reach the private VIP instead of the restricted one.
- Partial service list: only Cloud Storage and BigQuery are restricted, so a newly enabled service in the same project is outside the control.
- Policy Denied logs excluded: someone adds an exclusion to the
_Defaultsink to cut cost, and the main detection signal disappears. Route these logs to a dedicated sink before excluding anything. For the wider response, see the Data Exfiltration Playbook.
Framework mapping
| Framework | Reference | How VPC Service Controls supports it |
|---|---|---|
| NIST SP 800-53 Rev. 5 | AC-4 Information Flow Enforcement; SC-7 Boundary Protection; SC-7(5) Deny by Default, Allow by Exception | Perimeters deny cross-boundary data flow unless an ingress or egress rule allows it |
| NIST SP 800-53 Rev. 5 | AU-6 Audit Record Review, Analysis, and Reporting | Policy Denied log analysis and alerting |
| NIST CSF 2.0 | PR.IR-01, PR.DS-01, PR.AA-05, DE.CM-01 | Environment protection, data-at-rest protection, access enforced by policy, monitoring for adverse events |
| CIS Controls v8.1 | 3.3 Configure Data Access Control Lists; 12.2 Establish and Maintain a Secure Network Architecture | Context-based access to data services and segmented API access paths |
| ISO/IEC 27001:2022 Annex A | A.8.12 Data leakage prevention; A.8.20 Networks security; A.8.22 Segregation of networks | Exfiltration prevention and separation of environments |
| MITRE ATT&CK | T1537 Transfer Data to Cloud Account; T1530 Data from Cloud Storage; T1078.004 Cloud Accounts | Blocks copy to outside projects and use of valid cloud credentials from outside approved contexts |
Practitioner checklist
- Inventory projects holding sensitive data and confirm each sits in an enforced perimeter, not only a dry-run one.
- Restrict all supported services in those perimeters and set VPC accessible services to
RESTRICTED-SERVICES. - Route
*.googleapis.comtorestricted.googleapis.comin every VPC and on-premises resolver, and remove unneeded internet routes. - Rewrite every rule using
ANY_IDENTITY,ANY_SERVICE_ACCOUNTor wildcard resources into named identities, sources, methods and projects. - Enable
sourceRestrictionon egress rules that rely on sources. - Limit Access Context Manager Admin and Editor to a small group and one deployment service account, and manage perimeters as reviewed code.
- Send Policy Denied logs to a dedicated sink and alert on
RESOURCES_NOT_IN_SAME_SERVICE_PERIMETERand service accountNO_MATCHING_ACCESS_LEVELdenials. - Alert on Access Context Manager Admin Activity changes outside the deployment pipeline.
Standards and references
- Google Cloud: Overview of VPC Service Controls
- Google Cloud: Ingress and egress rules
- Google Cloud: Dry run mode for service perimeters
- Google Cloud: Enable VPC Service Controls
- Google Cloud: Set up private connectivity to Google APIs and services
- Google Cloud: VPC Service Controls audit logging
- NIST: SP 800-53 Rev. 5
- MITRE ATT&CK: Transfer Data to Cloud Account
Related NHI Mgmt Group resources: Google Cloud Security Guide · Security Command Center Guide · Google Cloud Enterprise Foundations Blueprint Guide · Data Exfiltration Guide