GCP — How to implement VPC Service Control using Terraform.

Sumit K
Google Cloud - Community
11 min readFeb 1, 2023
Terraform+GCP

Welcome to my blog. In this tutorial, we will deploy and test VPC service controls using terraform ways to protect storage APIs with a very simple example. Basically, we will create a service/defense perimeter around the cloud storage so that no one can access it from the internet. Access from the internet to a cloud Storage bucket within a service perimeter is denied by default however we can also enable access level controls based on various attributes such as source IP, Location, Device, etc. which means rather than based on identity, VPC service control provides access to services based on client type, device, Location, IP, etc. So these are something that would not be possible with IAM.

if you are not familiar with this service, I would highly recommend you to please watch my previous blog where I have already demonstrated a detailed explanation of VPC service control with a good demo. Click here to watch my previous blog.VPC service control is a complex topic as it involves several resources, permissions, Organization level roles, etc. In today’s demo, we will deploy the VPC service control with the terraform approach. VPC security control is a very important topic of cloud security and it is similar to AWS security control policy. Many enterprise companies hesitate to move to the cloud because of security concerns. VPC service control let you allow to connect GCP services securely and configure your deployment that isolates and protects resources from external access and from other GCP resources. Ultimately preventing data from exfiltrating a company’s defined security perimeter.

Data exfiltration is the unauthorized copying, transfer or retrieval of data from source

Definition of VPC Service Controls.

VPC Service Controls provide an extra layer of security defense for Google Cloud services that are independent of Identity and Access Management (IAM). While IAM enables granular identity-based access control, VPC Service Controls enable broader context-based perimeter security. Google recommends using both VPC Service Controls and IAM for defense in depth.

Capabilities

VPC Service Controls lets you define security policies that prevent access to Google-managed services outside of a trusted perimeter, block access to data from untrusted locations, and mitigate data exfiltration risks. You can use VPC Service Controls for the following use cases:

  • Control VM to Service and Service to Service Paths.
  • Ingress: Prevent Access from unauthorized networks.
  • Egress: Prevent copying of data to unauthorized networks.
  • project level granularity.
GCP cloud storage API is covered under service perimeter in both projects and blocked access from the internet from certain Geo-location or IP ranges.

Scope of This Demo:

VPC Service Perimeters function like a firewall for GCP APIs.In our demo, we are going to cover some essential components when creating a new Service perimeter. The perimeter includes the following list but we will cover everything except Ingress and Egress Policy.

  1. Protect Projects — list of projects you want to protect. In my case My First Project and ServiceA)
  2. Restricted Service — list of services protected by the perimeter. In my case storage APIs.
  3. VPC Accessible Service — Leave it to default: All Service.
  4. Access Levels — Allow/Deny Access to protected resources based on certain Attributes like IP, Location, Device Type, etc.
  5. Ingress Policy — Allow Access to Certain resources from another project to access the protected resource.
  6. Egress Policy — Same As Above but Egress.

To follow this tutorial you will need:

  • GCP Organization. You must have Organization enabled in your domain. Click here to see how to create an organization with additional information.
  • A GCP Account, folder, and Two or more Projects (In my case I have three projects: ProjectA, ProjectB, and ProjectC ).
  • A Service Account to authenticate programmatically from terraform code. Make sure the SA has the role “Compute Organisation Security Policy Admin” and “Access Context Manager Admin” along with the Owner.
  • Enable the cloud storage API on both projects

Take a look at my current GCP project structure.

Let’s Get Started:

Step1. First Create a Directory for the terraform configuration.

mkdir storage-api-control

Step2. Change into the Directory

cd storage-api-control

Step3. Create a new file for the configuration block.

touch main.tf

Step4. Paste the configuration below into main.tf and save it. I have put all the code into a main configuration file. it’s up to you how you organize your terraform configuration files but for now, i made it one for the sake of simplicity



# Provider
provider "google" {
credentials = file("host-project-375410-c25c379d9e10.json")
project = "host-project-375410"
region = "us-east1"
}

# Fetch Organization name
data "google_organization" "org" {
domain = "lyfedge.com"

}

# Create an access policy scoped to folder. thats is "BU1"
resource "google_access_context_manager_access_policy" "org-policy" {
parent = data.google_organization.org.name
title = "Parent policy for ACL restrictions"
scopes = ["folders/741128092978"] # replace the numbber with your folder ID
}

# Create Service Perimeter resource to the specific projects. Protected projects.
resource "google_access_context_manager_service_perimeter_resource" "service-perimeter-resource" {
perimeter_name = google_access_context_manager_service_perimeter.service-perimeter.name
resource = "projects/654385628748" # (Required) A GCP resource that is inside of the service perimeter. Currently only projects are allowed. Format: projects/{project_number}
}

# Create a service perimeter to protect google cloud storage API within the access policy
resource "google_access_context_manager_service_perimeter" "service-perimeter" {
parent = "accessPolicies/${google_access_context_manager_access_policy.org-policy.name}"
name = "accessPolicies/${google_access_context_manager_access_policy.org-policy.name}/servicePerimeters/restrict_storage"
title = "restrict_storage"
perimeter_type = "PERIMETER_TYPE_REGULAR"
status {
restricted_services = ["storage.googleapis.com"]

}

}

# Create Access level to block the access from certain Geo-location.

resource "google_access_context_manager_access_levels" "access-levels" {
parent = "accessPolicies/${google_access_context_manager_access_policy.org-policy.name}"
access_levels {
name = "accessPolicies/${google_access_context_manager_access_policy.org-policy.name}/accessLevels/chromeos_no_lock"
title = "regions-block"
basic {
conditions {

regions = [
"CH",
"IT",
"US",
"IN",

]
}
}
}

}

Before we apply and create these resources, Let’s understand this code a Little bit..

The first block is a provider block for the terraform to download the plugins and authenticate with the provider.

Next is the data block “google_organization” “org” which we used to fetch our organization details. domain name lyfedge.com

Next is the resource block ”google_access_context_manager_access_policy" wherein we are going to create an access policy on the organization but the scope is limited to a folder. you can not create another policy on the Organizational level since it has already a default policy that can neither be deleted nor overridden. That’s why the scope is a folder as mentioned in the code.

Now, Next, we create 3 more resource blocks for protected projects that create a service perimeter around the storage APIs for the projects that we want to protect. the last resource block is an access level block. An Access Level is a label that can be applied to requests for GCP services, along with a list of requirements necessary for the label to be applied.

1. resource “google_access_context_manager_access_policy” “org-policy”

2. resource “google_access_context_manager_service_perimeter_resource” “service-perimeter-resource”

3. resource “google_access_context_manager_service_perimeter” “service-perimeter”

Step5. Initialize the backend, Format, and validate your configuration.

$ terraform init
Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/google from the dependency lock file
- Using previously-installed hashicorp/google v4.51.0

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

$ terraform validate
Success! The configuration is valid.

Step6. Create your resources

data.google_organization.org: Reading...
data.google_organization.org: Read complete after 2s [id=organizations/289967738514]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create

Terraform will perform the following actions:

# google_access_context_manager_access_levels.access-levels will be created
+ resource "google_access_context_manager_access_levels" "access-levels" {
+ id = (known after apply)
+ parent = (known after apply)

+ access_levels {
+ name = (known after apply)
+ title = "chromeos_no_lock"

+ basic {
+ combining_function = "AND"

+ conditions {
+ ip_subnetworks = []
+ members = []
+ regions = [
+ "CH",
+ "IT",
+ "US",
+ "IN",
]
+ required_access_levels = []
}
}
}
}

# google_access_context_manager_access_policy.org-policy will be created
+ resource "google_access_context_manager_access_policy" "org-policy" {
+ create_time = (known after apply)
+ id = (known after apply)
+ name = (known after apply)
+ parent = "organizations/289967738514"
+ scopes = [
+ "folders/741128092978",
]
+ title = "Parent policy for ACL restrictions"
+ update_time = (known after apply)
}

# google_access_context_manager_service_perimeter.service-perimeter will be created
+ resource "google_access_context_manager_service_perimeter" "service-perimeter" {
+ create_time = (known after apply)
+ id = (known after apply)
+ name = (known after apply)
+ parent = (known after apply)
+ perimeter_type = "PERIMETER_TYPE_REGULAR"
+ title = "restrict_storage"
+ update_time = (known after apply)

+ status {
+ restricted_services = [
+ "storage.googleapis.com",
]
}
}

# google_access_context_manager_service_perimeter_resource.service-perimeter-resource will be created
+ resource "google_access_context_manager_service_perimeter_resource" "service-perimeter-resource" {
+ id = (known after apply)
+ perimeter_name = (known after apply)
+ resource = "projects/654385628748"
}

Plan: 4 to add, 0 to change, 0 to destroy.

Step7. type yes at the confirmation prompt to proceed.

Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.

Enter a value: yes

google_access_context_manager_access_policy.org-policy: Creating...
google_access_context_manager_access_policy.org-policy: Creation complete after 6s [id=520157655267]
google_access_context_manager_service_perimeter.service-perimeter: Creating...
google_access_context_manager_access_levels.access-levels: Creating...
google_access_context_manager_service_perimeter.service-perimeter: Creation complete after 3s [id=accessPolicies/520157655267/servicePerimeters/restrict_storage]
google_access_context_manager_service_perimeter_resource.service-perimeter-resource: Creating...
google_access_context_manager_access_levels.access-levels: Creation complete after 3s [id=accessPolicies/520157655267/accessLevels]
google_access_context_manager_service_perimeter_resource.service-perimeter-resource: Creation complete after 4s [id=accessPolicies/520157655267/servicePerimeters/restrict_storage/projects/654385628748]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

Step8. To see the list of resources created. Run terraform state list

$ terraform state list
data.google_organization.org
google_access_context_manager_access_levels.access-levels
google_access_context_manager_access_policy.org-policy
google_access_context_manager_service_perimeter.service-perimeter
google_access_context_manager_service_perimeter_resource.service-perimeter-resource

Let’s verify.. Google Console Output screenshot

  • Access policy:
Access policy has been created in the organization.
  • Service Perimeter: Please note that you will not be able to see the Service perimeter under organization. the reason you do not see it here is because of the scope of the policy is limited to the folder only as by default organizational policy can not define or overridden. you have to switch to the folder view to see your access policy. you can see the policy title, type, protected project(project count), and the services that is restricted under this perimeter.
Service Perimeter scoped under the folder
  • Access Context Manager ( Access Level )
Access Context Manager: Blocked countries

Finally, it’s time to access the storage bucket in the protected projects. Let me log in to my project: My First Project.

Access to cloud storage is blocked from India

You can see that access from India including the other 3 countries (whatever we defined in the code) has been blocked even though you have full admin IAM access. that's the beauty of VPC service control. another use can be let’s say if you have a compute VM in the protected projects and they want to access the storage which is within the service permit, it will get the access however if any VM from outside of the perimeter lets say from other projects will not be able to access to storage bucket. As you can see, we have controlled the path from your network to a resource that is actually not inside the perimeter. Let me quickly remove India “IN” from the access level conditions and run the “terraform apply” to update the resource.

Removed IN from the access level block

Now, refresh the console and you will see that the storage access is granted. So Access level is something that allows or disallows your request based on certain attributes like Location, IP Ranges, Device Type or software browser, etc. Similarly, you can add/remove the geo-location that you want to restrict. I hope you understood the concept of each component of service control, if you do, hit a thumbnail :)

storage Access is granted after removing the IN from the code.

Let’s quickly perform a demo to test the storage access from the virtual machine within the service perimeter project(My First Project) and outside of the perimeter such that another project resources.

For this, I have already created a storage bucket inside the protected projects (My First Project) which is under the service perimeter.

Bucket: gs://cloud-storage-bucket-demo-lyfedge

Let’s deploy 2 VMs. The first VM will be deployed in the protected project (My First Project) where the service perimeter is applied for cloud Storage APIs and the second one into another project “Host-Project”. Once you deployed the VM, it’s time to check the access from each VMs to see if the service perimeter allows or disallows access to the storage bucket.

Google Console Output Screenshot

first vm in protected project : My First Project
Access to bucket is allowed from first instance-1 which is the same project where service perimeter is applied

you can see that Access is allowed within the perimeter boundary.

Now let’s move on to the second VM which deployed in “Host-Project” outside of the perimeter. Let’s SSH and try to access the storage bucket API.

Second VM in another project: Host-Project (outside of perimeter)
Access to the bucket is denied with the exception Error is VPC Service Controls

As you can see in the preceding screenshot that the second VM is not allowed to access the storage bucket in the protected projects. This proves that our VPC service control is working as expected.

Please note that you have to add at least storage viewer permission to the default service account on the storage bucket otherwise you will get a permission error when accessing over the internet even though the service perimeter is not applied. So please keep this in mind.

That’s it for now. this was just a simple demo to show you how to implement VPC service control with the terraform and how it can easily scale out with infrastructure as code. you can do a lot with security control around your cloud-managed services. check out this terraform official link to know more about it and try to perform an ingress and egress policy which is not covered in this demo. Alternatively, you can refer to my previous blogs wherein ingress and egress policies are deeply covered along with the demo.

Congratulations!! You have completed the implementation of VPC service control with terraform module. I hope this blog is useful. Please feel free to like, share, and comment.

Thank you for Reading….. See You in Next blog :)

--

--

Sumit K
Google Cloud - Community

Humanity is the quality that we lack so much in real life, An Abide leaner, Cloud Architect⛅️, Love DevOps, AWS Community Builder 2023, Proud Hindu 🕉️