Secure the Connection to Your VPC Network from Serverless Environments in Google Cloud
There might have been various situations where you have needed to connect to services created in your Virtual Private Cloud (VPC) network from a serverless environment such as Cloud Run, Cloud Functions or App Engine. All those services in your VPC network use private IPs which can not be directly accessed from a serverless environment outside your VPC, and providing a public IP to these services is not a good option from a security/cost standpoint.
To make a secure connection from serverless environments to private services (e.g. Compute Engine, Cloud Sql), we can use the Google Cloud-provided Serverless VPC connector. Serverless VPC Access allows your serverless environment to send requests to your VPC network using Google Cloud’s internal DNS and internal IP addressing systems. The responses to these requests also use your internal network.
Benefits
- Requests sent to your VPC network are never exposed to the internet.
- Communication through Serverless VPC Access can have less latency compared to the internet.
Reference Architecture
Configure Serverless VPC connector
Serverless VPC Access consists of a connector resource that is actually a cluster of VM instances ( f1-micro, e2-micro,e2-standard-4). Larger machine types provide more throughput.
You can configure the minimum and maximum number of VM instances for this cluster with a maximum number of instances of 10.
Note: Serverless VPC Access automatically scales out the number of instances in your connector as traffic increases. However, the number of connectors does not scale in. To prevent connectors from scaling out more than desired, set the maximum number of instances to as low number as possible/reasonable. If your connector has scaled out and you need to scale it back down, recreate the connector with the desired number of instances. This link explains the VPC connector throughput as per the machine size and number of instances.
When configuring the Serverless VPC connector, you need to associate the connector to a VPC network and assign an IP range. Below is the example code (in Terraform) for setting up a VPC connector in a GCP project.
Enable the API in the Google project:
resource "google_project_service" "vpcaccess" {
project = var.project_id
service = "vpcaccess.googleapis.com"
disable_on_destroy = false
}
Create the VPC network:
resource "google_compute_network" "custom-vpc" {
project = var.project_id
name = "test-vpc"
auto_create_subnetworks = false
}
Create a specific subnet for VPC access:
resource "google_compute_subnetwork" "vpc-access-subnet" {
project = var.project_id
name = "vpc-access-subnet"
ip_cidr_range = "10.100.1.32/28"
region = "us-central1"
network = google_compute_network.custom-vpc.id
}
Create VPC connector:
resource "google_vpc_access_connector" "connector" {
project = var.project_id
region = "us-central1"
name = "vpc-conn"
subnet {
name = google_compute_subnetwork.vpc-access-subnet.name
}
machine_type = "e2-standard-4"
}
IP Address
You must configure /28 IP CIDR Range, unused inside the same VPC (example 192.168.1.0/28). The IP range must not overlap with any existing Subnet range inside the VPC.
You cannot use a different mask other than /28, and the reason behind it rests in the maximum configuration. You can have up to 10 connector instances; if you require more throughput, you will need to increase the machine type of the instances.
Note:
- When you directly use the CIDR range while creating a VPC connector, a new subnet is created, but that won’t be visible in the subnet list from the console and can not be listed using command either, gcloud compute networks subnets list. Thus, it is recommended to first create the subnet and use that subnet while creating the VPC connector for better management of resources.
- An implicit firewall rule with priority 1000 is created on your VPC network to allow ingress from the connector's subnet or custom IP range to all destinations in the network. The implicit firewall rule is not visible in the Google Cloud console and exists only as long as the associated connector exists.
Configure your serverless environment to use a connector
After creating the serverless VPC connector, you need to configure your serverless environment to use this VPC connector to connect with your VPC network. To specify a connector during deployment, use the --vpc-connector flag. Below is an example of a Cloud Function service in Python. A similar approach can be used for App Engine and Cloud Run. Below is an example of setting up a cloud function. This function is going to provide the IP address for a given hostname. In this case, we should be able to get the IP address of a private compute VM from this cloud function because of serverless VPC access.
Sample source code for cloud function to get the IP of a hostname created in a private VPC using internal DNS.
from flask import Flask, request, jsonify
import socket
app = Flask(__name__)
def get_ip_address(hostname):
try:
ip_address = socket.gethostbyname(hostname)
return ip_address
except socket.error as e:
return None
@app.route('/')
def get_ip(self):
hostname = request.args.get('hostname', '')
if not hostname:
return jsonify({'error': 'Hostname parameter is missing'}), 400
ip_address = get_ip_address(hostname)
if ip_address:
return jsonify({'hostname': hostname, 'ip_address': ip_address})
else:
return jsonify({'error': 'Failed to retrieve IP address for the given hostname'}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
Create the zip archive for the code to be deployed in the cloud function:
resource "google_storage_bucket" "bucket" {
project = var.project_id
name = "test-bucket-vpc"
location = "US"
}
data "archive_file" "main" {
type = "zip"
output_path = pathexpand("${var.source_directory}.zip")
source_dir = pathexpand(var.source_directory)
}
resource "google_storage_bucket_object" "archive" {
name = "${data.archive_file.main.output_md5}-${basename(data.archive_file.main.output_path)}"
bucket = google_storage_bucket.bucket.name
source = data.archive_file.main.output_path
content_disposition = "attachment"
content_type = "application/zip"
}
resource "google_project_service" "function" {
project = var.project_id
service = "cloudfunctions.googleapis.com"
disable_on_destroy = false
}
resource "google_cloudfunctions_function" "function" {
project = var.project_id
region = "us-central1"
name = "test-function"
description = "My test function"
runtime = "python310"
available_memory_mb = 128
source_archive_bucket = google_storage_bucket.bucket.name
source_archive_object = google_storage_bucket_object.archive.name
trigger_http = true
entry_point = "get_ip"
vpc_connector = google_vpc_access_connector.connector.id
depends_on = [
google_project_service.function
]
}
resource "google_cloudfunctions_function_iam_member" "invoker" {
project = google_cloudfunctions_function.function.project
region = google_cloudfunctions_function.function.region
cloud_function = google_cloudfunctions_function.function.name
role = "roles/cloudfunctions.invoker"
member = "allUsers"
}
Configure your serverless environment to use a connector
Now, we can create a test VM, which will be created on a private VPC network. In the Terraform configuration below, we are creating the subnet “demovm-subnet” and creating the VM instance for demovm within the subnet.
resource "google_compute_subnetwork" "demovm-subnet" {
project = var.project_id
name = "demovm-subnet"
ip_cidr_range = "10.99.1.0/24"
region = "us-central1"
network = google_compute_network.custom-vpc.id
}
resource "google_compute_instance" "demovm" {
project = var.project_id
name = "demovm"
machine_type = "e2-micro"
zone = "us-central1-a"
boot_disk {
initialize_params {
image = "ubuntu-os-cloud/ubuntu-2004-lts"
}
}
network_interface {
subnetwork = google_compute_subnetwork.demovm-subnet.self_link
}
}
Test
Now, it's time to test our configuration. If we access the function URL with the hostname as an argument, we should get the IP address details. We are using the FQDN for the hostname, which accesses the VPC-scoped private DNS zone to get the IP of the host.
https:///test-function?hostname=demovm.c..internal
Conclusions
Serverless VPC access is a great way to connect Google-provided services to private network services. There are still some points which should be considered while designing the architecture.
- Serverless VPC access sets up GCP Virtual machines in the backend and can not scale in/down. Hence, the machine size and the number of instances should be considered according to the load to manage the cost.
- It's always better to use some sort of automation, such as IAC, to create these services so that they can be destroyed and created quickly when needed for cost management.
- If you are using serverless VPC access to connect to the services in a shared VPC network, there are two ways to do it. The VPC connector can be set up in each service project where the cloud runs or cloud functions are running, or it can be set up in the host project. The decision should be made depending on the requirements such as isolation, security and cost.
Reference Links: https://cloud.google.com/vpc/docs/serverless-vpc-access
Share this
You May Also Like
These Related Stories
No Comments Yet
Let us know what you think