DEV Community

Cover image for Configuring an isolated network in Google Cloud
Chabane R. for Stack Labs

Posted on

Configuring an isolated network in Google Cloud

In the first part we introduced the security patterns that could be implemented to secure the connectivity between Google Kubernetes Engine and Cloud SQL. In this part we will implement the network isolation by deploying the following GCP resources:

  • VPC with 2 subnets
    • 1 web subnet for GKE.
    • 1 data subnet.
  • Cloud NAT attached to the web subnet.
  • Firewall rules to restrict access to subnets to only authorized networks.

It is recommended to group similar applications into fewer, more manageable and larger subnets.

VPC isolated network

If you have multiples GKE clusters per environment, Google Cloud recommends to use Shared VPC to reduce management and topology complexity.

VPC

Let's start with the Virtual Private Cloud.

Create a terraform file infra/plan/vpc.tf:

  • A simple VPC resource
  • The web subnet. It will host our Google Kubernetes Engine
  • The data subnet. It could host your Cloud Dataflow jobs, Cloud Composer environments, etc.
resource "google_compute_network" "custom" {
  name                    = "custom"
  auto_create_subnetworks = "false" 
  routing_mode            = "GLOBAL"
}

resource "google_compute_subnetwork" "web" {
  name          = "web"
  ip_cidr_range = "10.10.10.0/24"
  network       = google_compute_network.custom.id
  region        = var.region

  secondary_ip_range  = [
    {
        range_name    = "services"
        ip_cidr_range = "10.10.11.0/24"
    },
    {
        range_name    = "pods"
        ip_cidr_range = "10.1.0.0/20"
    }
  ]

  private_ip_google_access = true
}

resource "google_compute_subnetwork" "data" {
  name          = "data"
  ip_cidr_range = "10.20.10.0/24"
  network       = google_compute_network.custom.id
  region        = var.region

  private_ip_google_access = true
}
Enter fullscreen mode Exit fullscreen mode

Cloud NAT

In order to allow our web subnet used by GKE to access the internet, we need to create a Cloud NAT. We associate Cloud NAT with the subnet using Cloud Router.

Create a terraform file infra/plan/nat.tf

resource "google_compute_address" "web" {
  name    = "web"
  region  = var.region
}

resource "google_compute_router" "web" {
  name    = "web"
  network = google_compute_network.custom.id
}

resource "google_compute_router_nat" "web" {
  name                               = "web"
  router                             = google_compute_router.web.name
  nat_ip_allocate_option             = "MANUAL_ONLY"
  nat_ips                            = [ google_compute_address.web.self_link ]
  source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS" 
  subnetwork {
    name                    = google_compute_subnetwork.web.id
    source_ip_ranges_to_nat = ["ALL_IP_RANGES"]
  }
  depends_on                         = [ google_compute_address.web ]
}
Enter fullscreen mode Exit fullscreen mode

Firewall

Firewall allows us to restrict the inbound and outbound network traffic to and from a VM instance, a network tag or a service account. In our case, we can implement the following rules:

  • A rule to restrict access to the Cloud SQL MySQL instance port to only GKE nodes.
  • A rule to restrict network access to only authorized networks.

Create a terraform file infra/plan/firewall.tf

resource "google_compute_firewall" "mysql" {
  name    = "allow-only-gke-cluster"
  network = google_compute_network.custom.name

  allow {
    protocol = "tcp"
    ports    = ["3306"]
  }

  priority = 1000

  source_ranges = ["10.10.10.0/24"]
}

resource "google_compute_firewall" "web" {
  name    = "allow-only-authorized-networks"
  network = google_compute_network.custom.name

  allow {
    protocol = "tcp"
  }

  priority = 1000

  source_ranges = var.authorized_source_ranges
}
Enter fullscreen mode Exit fullscreen mode

Let's configure our terraform.

Create a terraform file infra/plan/variable.tf

variable "region" {
  type = string
  default = "europe-west1"
}

variable "authorized_source_ranges" {
  type        = list(string)
  description = "Addresses or CIDR blocks which are allowed to connect to GKE API Server."
}
Enter fullscreen mode Exit fullscreen mode

Add a infra/plan/version.tf file

terraform {
  required_providers {
    google = {
      source = "hashicorp/google"
      version = "3.71.0"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Add a infra/plan/provider.tf file

provider "google" {
  region  = "europe-west1"
}
Enter fullscreen mode Exit fullscreen mode

And a infra/plan/backend.tf

terraform {
  backend "gcs" {
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, export the following variables and create a bucket to save your terraform states.

export PROJECT_ID=<PROJECT_ID>
export REGION=<REGION>
export TERRAFORM_BUCKET_NAME=<BUCKET_NAME>

gcloud config set project ${PROJECT_ID}

gsutil mb -c standard -l ${REGION} gs://${TERRAFORM_BUCKET_NAME}
gsutil versioning set on gs://${TERRAFORM_BUCKET_NAME}
Enter fullscreen mode Exit fullscreen mode

Create a infra/plan/terraform.tfvars and deploy the infrastructure:

authorized_source_ranges    = ["<AUTHORIZED_NETWORK>"]
Enter fullscreen mode Exit fullscreen mode
cd infra/plan

sed -i "s,<AUTHORIZED_NETWORK>,$AUTHORIZED_NETWORK,g" terraform.tfvars

terraform init \
  -backend-config="bucket=${TERRAFORM_BUCKET_NAME}" \
  -backend-config="prefix=state"

terraform apply
Enter fullscreen mode Exit fullscreen mode

Let's check if all the resources have been created and are working correctly

VPC & Subnets

VPC & Subnets

Cloud NAT

Cloud NAT

Firewalls

Firewall rules

Conclusion

Our network is now ready to host our GCP resources. In the next part, we will focus on setting up GKE Autopilot.

Top comments (5)

Collapse
 
nikhilakki profile image
Nikhil Akki

Great tutorial series, loving it so far... I have a question - What env value to set for AUTHORIZED_NETWORK?

Collapse
 
chabane profile image
Chabane R.

Thanks for your feedback :+1

AUTHORIZED_NETWORK could be your local ip (curl -s checkip.amazonaws.com/)
or the public IP of your private network

Collapse
 
nikhilakki profile image
Nikhil Akki

Thanks for the quick response, this helped and I was able to run the scripts, however, with some modifications.

  1. sed -i "s,,$AUTHORIZED_NETWORK,g" terraform.tfvars gave an error - sed: 1: "terraform.tfvars": undefined label 'erraform.tfvars'. I modified it to sed "s//$AUTHORIZED_NETWORK/g" terraform.tfvars I haven't really used sed tool before (followed steps from this link). Eventually I gave up and replaced the value for AUTHORIZED_NETWORK to the IP I got from the checkip.amazonaws.com url.
  2. All other definitions in *.tf files error-ed out due to missing definition/property - project. I created a variable project in variable.tf with default value as "" and referenced it in each definition as project = var.project.
  3. Created an apply.sh file for running these set of commands -
cd infra/plan

export AUTHORIZED_NETWORK=$(curl -s checkip.amazonaws.com/)
export TF_VAR_project=$PROJECT_ID

echo 'AUTHORIZED_NETWORK =' $AUTHORIZED_NETWORK
echo 'TF_VAR_project =' $PROJECT_ID

sed "s/<AUTHORIZED_NETWORK>/$AUTHORIZED_NETWORK/g" terraform.tfvars
# sed -i "s,<AUTHORIZED_NETWORK>,$AUTHORIZED_NETWORK,g" terraform.tfvars

terraform init \
    -backend-config="bucket=${TERRAFORM_BUCKET_NAME}" \
    -backend-config="prefix=state"

terraform apply
Enter fullscreen mode Exit fullscreen mode

It all worked at the end, but my approach seems a hacky one, let me know your thoughts.

Looking forward to reading the next posts in the series. 😃

Thread Thread
 
nikhilakki profile image
Nikhil Akki

Great series... thanks again for sharing it :-) I was able to setup and customise the scripts as per my requirements.

Collapse
 
thanhtranskywalker profile image
Thanh Tran

I have a question about subnet "data".

the "data" subnet is never used in the subsequent articles, which I find confusing.
I am unsure whether this is an oversight by the author or intentional?