GCP: global external HTTPS LB for securely exposing insecure VM services

If you have unmanaged GCP VM instances running services on insecure ports (e.g. Apache HTTP on port 80), one way to secure the public external traffic is to create an external GCP HTTPS load balancer.

Conceptually, we want to expose a secure front to otherwise insecure services.

While the preferred method would be to secure the client communication natively (easy enough using our Apache example), this is not always an option when dealing with unmanaged VM instances that contain legacy solutions.

This security stance follows the same model one might follow on-premise, where either a commercial load balancer or NGINX/HAProxy is used as a secure front to an insecure backend pool of VM.

Solution Overview

Our ultimate goal is to take two VM instances that only speak on insecure ports (Apache on port 80), and expose their services instead on a GCP global External HTTPS loadbalancer using HTTPS on port 443.

This can be accomplished by creating a backing service with unmanaged instance groups for VMs in each zone as shown below.

 

We then create and wire together the health-checks, backend-services, url-maps, target-https-proxies, and forwarding rule objects as shown in the diagram.

The rest of the this article will detail the creation of these objects.

GCP network prerequisites

There is one GCP network prerequisites for an external HTPS load balancer, you must create a firewall rule to allow health checks.

As noted in the Google documentation, the health checks will come from two Google source IP ranges: 35.191.0.0/16 or 130.211.0.0/22.

network_name=mynetwork
gcloud compute firewall-rules create fw-allow-health-checks \
--network=${network_name} \
--action=ALLOW \
--direction=INGRESS \
--source-ranges=35.191.0.0/16,130.211.0.0/22 \
--target-tags=allow-health-checks \
--rules=tcp

The VMs in this article “apache1-10-0-90-0” and “apache2-10-0-90-0” are both tagged with “allow-health-checks” to participate in this firewall rule.

gcloud compute instances add-tags apache1-10-0-90-0 --tags=allow-health-checks --zone=us-east1-b
gcloud compute instances add-tags apache2-10-0-90-0 --tags=allow-health-checks --zone=us-east1-c

Here is an example of the network firewall being done with Terraform, and a terraform example of tagging VM instances using Terraform and its variables being passed.

Notice that for our global HTTPS LB, a proxy-only subnet is not necessary.  It would be necessary if this HTTPS LB was regional.

Unmanaged VM instances running insecure service

For this article, we will have two unmanaged VM instance running Apache on port 80. These VMs are named:

  • apache1-10-0-90-0 running on us-east1-a
  • apache2-10-0-90-0 running on us-east2-b

Create unmanaged instance groups for the VMs in each zone, with a named port on 80.

project_id=$(gcloud config get project)
region=us-east1
instance_group_prefix=lb-ig

# for creating global objects
location_flag="--global"

ig_name=${instance_group_prefix}-a
gcloud compute instance-groups unmanaged create $ig_name --zone=${region}-a
gcloud compute instance-groups unmanaged set-named-ports $ig_name --named-ports=http:80 --zone=${region}-a
gcloud compute instance-groups unmanaged add-instances $ig_name --instances=apache1-10-0-90-0 --zone=${region}-a

ig_name=${instance_group_prefix}-b
gcloud compute instance-groups unmanaged create $ig_name --zone=${region}-b
gcloud compute instance-groups unmanaged set-named-ports $ig_name --named-ports=http:80 --zone=${region}-b
gcloud compute instance-groups unmanaged add-instances $ig_name --instances=apache2-10-0-90-0 --zone=${region}-b

Health check

Create a health check for the backend-service.

healthcheck_name=extb-health
gcloud compute health-checks create http $healthcheck_name --host='' --request-path=/index.html --port=80 --enable-logging $location_flag

Backend Service

Create a backend service.

backend_name=extlb-backend
gcloud compute backend-services create $backend_name --health-checks=$healthcheck_name --port-name=http --protocol=HTTP --load-balancing-scheme=EXTERNAL --enable-logging --logging-sample-rate=1 $location_flag

Then add its two unmanaged instance group members.

gcloud compute backend-services add-backend $backend_name --instance-group=${instance_group_prefix}-a --instance-group-zone=${region}-a $location_flag

gcloud compute backend-services add-backend $backend_name --instance-group=${instance_group_prefix}-b --instance-group-zone=${region}-b $location_flag

SSL Certificate

Create the TLS certificate that will be used for secure communication. We will use a self-signed cert, but you are free to use a commercial one.

domain=httpslb.fabianlee.org
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout /tmp/$FQDN.key -out /tmp/$FQDN.crt \
-subj "/C=US/ST=CA/L=SFO/O=myorg/CN=$FQDN"

Load the certificate into GCP.

gcloud compute ssl-certificates create lbcert1 --certificate=/tmp/$domain.crt --private-key=/tmp/$domain.key --project=$project_id $location_flag

URL Map

The URL map directs traffic based on the request. The name of the URL map object is what the cloud console shows as the load balancer name.

lb_name=extlb-lb1
gcloud compute url-maps create $lb_name --default-service=$backend_name $location_flag

Target HTTPS Proxies

This is the actual proxy-based load balancer entity.

target_https_proxy_name=extlb-target-https-proxy
gcloud compute target-https-proxies create $target_https_proxy_name --url-map $lb_name --ssl-certificates=lbcert1 $location_flag

Forwarding Rule

Finally, you must create the forwarding rule that exposes an IP address and port for the load balancer. A forwarding rule can only expose a single port.

fwd_rule_name=extlb-frontend
gcloud compute forwarding-rules create $fwd_rule_name --load-balancing-scheme=EXTERNAL --ports=443 --target-https-proxy=$target_https_proxy_name $location_flag

The IP address of the global HTTS load balancer can be retrieved using:

gcloud compute forwarding-rules describe $fwd_rule_name --global | grep -i IPAddress

Cloud Console UI

Here is the view from the cloud console.

Script

If you want a full example, see my create-https-lb.sh on github.

 

 

REFERENCES

google ref, external HTTPS LB overview

google ref, external HTTPS LB architecture explanation and diagram

stackoverflow, creating https lb with diagram

google, google loadbalancing health checks

google, backend-services

google, creating LB health checks

google, http-health-checks

chainerweb.com, loadbalancer and instancegroups for GCP lb with terraform

google ref, gcloud unmanaged instance groups

google ref, gcloud backend-services

nbtechsolutions, gcp https lb with gcloud

realkinetic blog, GCP external loadbalancer with managed VM instances and gcloud

stackoverflow, good answer describing GCP LB constructs