VPC Service Controls is GCP’s way of creating a security perimeter around your data, preventing exfiltration and unauthorized access by restricting which services can talk to each other, and from where.
Let’s see it in action. Imagine you have a Cloud Storage bucket containing sensitive customer data. You want to ensure that this data can only be accessed by applications running within your Virtual Private Cloud (VPC) network, and never from the public internet or other GCP projects.
Here’s a simplified setup:
1. Create a Service Perimeter:
gcloud access-context-manager perimeters create my-data-perimeter \
--title "Sensitive Data Perimeter" \
--resources projects/123456789012 \
--restricted-services storage.googleapis.com
my-data-perimeter: The name of your perimeter.projects/123456789012: The GCP project ID you’re protecting. You can add multiple projects here.storage.googleapis.com: The specific GCP API you want to restrict. You can add more services likebigquery.googleapis.com,aiplatform.googleapis.com, etc.
2. Configure Access Levels (Who can access into the perimeter):
This is where you define trusted sources. You might create an access level for your internal VPC network.
# First, create a custom access level
gcloud access-context-manager levels create trusted-vpc-access \
--title "Trusted VPC Access" \
--policy-name "123456789012" # Your policy ID
# Then, add a condition for your VPC network
gcloud access-context-manager levels update trusted-vpc-access \
--add-condition="vpcNetwork":{"projects":["123456789012"],"networkUrls":["10.10.0.0/24"]} \
--policy-name="123456789012"
trusted-vpc-access: The name of your access level.--add-condition="vpcNetwork":{"projects":["123456789012"],"networkUrls":["10.10.0.0/24"]}: This is the key. It specifies that access is allowed only from resources within project123456789012that reside on the10.10.0.0/24subnet.
3. Associate the Access Level with the Perimeter:
Now, tell the perimeter to use this access level for inbound requests.
gcloud access-context-manager perimeters update my-data-perimeter \
--resources projects/123456789012 \
--restricted-services storage.googleapis.com \
--add-access-levels trusted-vpc-access \
--policy-name="123456789012"
What this achieves:
With this setup, if a Compute Engine VM in your VPC network (with an IP in 10.10.0.0/24) tries to access storage.googleapis.com (e.g., to read from your protected bucket), the request will be allowed because it originates from a trusted source defined in trusted-vpc-access.
However, if an application running on a publicly accessible VM outside your VPC, or even a service account key being used from a developer’s laptop, attempts to access the same storage bucket, the request will be blocked by VPC Service Controls before it even reaches the storage service. This prevents data exfiltration, even if credentials are compromised.
The core problem VPC Service Controls solves is the "shared security model" within GCP. While GCP secures the infrastructure, you are responsible for securing your data and services from each other. VPC Service Controls provides a network-level boundary within GCP to enforce this. It acts like a firewall, but instead of IP addresses and ports, it uses GCP service identities, project IDs, and network configurations.
The "restricted services" define the boundaries of your perimeter. Services outside this list can still communicate freely with services inside the perimeter, but services inside the perimeter cannot communicate with each other if they are also listed as restricted. This is a crucial nuance: the perimeter is defined by what’s restricted, not what’s allowed. If you want to allow communication between two services within your perimeter, neither of them should be in the restricted-services list for that perimeter, or you need to configure specific bridges.
When you add a service to restricted-services, you’re essentially saying, "This service’s API is now protected by this perimeter." Any attempt to call that API from outside the perimeter will be denied. Any attempt to call it from inside the perimeter must originate from an IP or identity that satisfies one of the associated access levels.
The surprising thing is that VPC Service Controls doesn’t just prevent outbound traffic from leaving your projects; it also prevents unauthorized inbound traffic from reaching your services, even if that traffic originates from within GCP but outside your defined perimeter. It’s not just about keeping data in, but also about controlling what can access that data.
The interaction between restricted-services and access-levels is key. A service is only protected if it’s in restricted-services. But even if it is, access is only granted if it meets the criteria defined in the access-levels associated with the perimeter. This allows for fine-grained control: you can have a broad perimeter protecting many services, but then use different access levels to permit different subsets of users or networks to access specific services within that perimeter.
You can also configure "ingress" and "egress" rules. Egress rules control what resources inside the perimeter can talk to outside the perimeter. Ingress rules control what resources outside the perimeter can talk to resources inside the perimeter. The access-levels are effectively defining your ingress policies.
The most common misconception is that simply putting a project into a perimeter automatically restricts all API access for that project. That’s not quite right. The perimeter protects the services you list as restricted. If a service isn’t listed, it’s not protected by that perimeter. Furthermore, access within the perimeter is governed by the access levels. If you have a project in a perimeter, but no access levels configured, then nothing can access the restricted services within that project from outside the perimeter, and nothing within the project can access those services either (unless they are explicitly allowed via an access level).
The next hurdle you’ll likely face is managing exceptions for specific use cases, like allowing a trusted third-party partner to access a specific BigQuery dataset within your perimeter.