Google Cloud HTTP(S) Global Load Balancer Redirect HTTP to HTTPS Demo

• Updated October 28, 2018

A common configuration for any web serving infrastructure is to redirect all HTTP requests to HTTPS. Because a Google Cloud HTTP(S) Global Load Balancer is a globally available resource comprised of many software defined networking components, configuring this, despite the end result being the same, works a bit differently than what you might be used to.

The following post will go through the necessary steps to configure a Google Cloud HTTP(S) Global Load Balancer and the backend servers to redirect all HTTP requests to HTTPS.

Prerequisites

This post assumes the following:

  • You are familiar with Google Cloud Platform (GCP)
  • You have an existing GCP Project to create GCP resources in or you will be creating a new GCP Project to create GCP resources in

Create a GCP Project

If you have not yet created a GCP Project, follow these steps:

  1. Open a web browser, and create or log in to a Google Account
  2. Navigate to the GCP Console
  3. If this is your first GCP Project, you will be prompted to create a GCP Project. Each Google account gets $300 in GCP credit to use within 12 months. You are required to enter a credit card to create a GCP Project, but it will not be charged until the $300 credit is consumed or 12 months expire, whatever comes first.

Enable GCP Services

If you just created a new GCP Project, you will need to enable the APIs for the GCP services you will be using in this post.

In the GCP Console, go to Compute Engine and wait for the Compute Engine API to start.

Use Cloud Shell

The entirety of this post will be done using Cloud Shell: GCP’s built-in terminal environment.

Cloud Shell allows you to operate GCP using command-line tools from any computer with a modern web browser. Every Google account gets their own Cloud Shell instance which is simply an f1-micro Google Compute Engine (GCE) virtual machine with 5 GB of Persistent Disk storage attached to your home directory. Cloud Shell instances are terminated after about 20 minutes of inactivity, but anything you store in your home directory will persistent across Cloud Shell instances.

Open Cloud Shell by going to the GCP Console and clicking the terminal prompt icon (which looks like this: [>_]) in the upper-right part of the web browser window.

Create a Private Google Cloud Storage Bucket

Start by creating a Google Cloud Storage (GCS) bucket. Later in the post, the bucket will be used to store and pull configuration files.

BUCKET="$(gcloud config list project --format 'value(core.project)')-private"

gsutil mb gs://$BUCKET

Create an nginx Configuration File

The first configuration file to create is for nginx. This is a basic nginx configuration with a server block to listen on port 443.

You might be wondering why there is not an nginx server block for port 80. The Global Load Balancer (GLB) is actually a proxy. Because it’s a proxy, clients will connect to the GLB and it will create new connections to the backend virtual machines running nginx on port 443. So, the only part of this architecture that needs to be listening on port 80 is the GLB, and that is accomplished by creating a Global Forward Rule listening on port 80 that attaches to the frontend of the GLB. That Global Forwarding Rule, and a second Global Forwarding Rule on port 443, will be created later in the post.

Additionally, the Global Load Balancer will pass request header X-Forwarded-Proto to the backend. It is this header that must be used within nginx to determine if clients need to be redirected from HTTP to HTTPS. If they need to be redirected, a response header will be returned to the client indicating a 301 redirect needs to occur.

cat << 'EOF' > default
server {
  listen 443 ssl;

  server_name _;

  root /var/www/html;

  if ($http_x_forwarded_proto = 'http') {
      return 301 https://$host$request_uri;
  }

  ssl_certificate /etc/ssl/server.crt;
  ssl_certificate_key /etc/ssl/server.key;
}
EOF

Create a Virtual Machine Startup Script

Next, create the startup script that will run on boot time when the virtual machines are created.

The following startup script will download the SSL keys and nginx configuration files from the private GCS bucket you created earlier. Additionally, it will install nginx and create an index.html file that shows the hostname of the virtual machine when the web server is accessed - this is simply to show that load balancing is working between the two virtual machines.

cat << 'EOF' > www-test-startup.sh
#!/bin/bash

BUCKET="$(gcloud config list project --format 'value(core.project)')-private"

gsutil cp gs://$BUCKET/server.{key,crt} /etc/ssl/

gsutil cp gs://$BUCKET/default /etc/nginx/sites-enabled/default

HOSTNAME=$(curl http://metadata.google.internal/computeMetadata/v1/instance/name -H "Metadata-Flavor: Google")

apt-get update
apt-get install -y nginx

cat <<EOFNGINX > /var/www/html/index.html
<html><body><h1>Hello World</h1>
<p>Backend VM Hostname: $HOSTNAME</p>
</body></html>
EOFNGINX
EOF

Create a Self-Signed SSL Certificate

The final file to create is a self-signed SSL certificate. A self-signed SSL certificate is required to create a Global Forwarding Rule on port 443 and to enable end-to-end encryption between the client, the GLB, and the backend servers. Read more about end-to-end encryption when using the Global Load Balancer.

When creating the self-signed SSL certificate, all prompted fields can be ignored except for Common Name. Be sure to input a domain name in the Common Name field - example.com will work - otherwise the self-signed SSL certificate will be rejected when creating the Global Load Balancer later in the post.

openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr

openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

Upload Files to the Private GCS Bucket

Finally, upload all the files you created to the private GCS bucket:

gsutil cp server.key server.crt default www-test-startup.sh gs://$BUCKET/

Create the Virtual Machines

Now, create two GCE virtual machines. An image-type is not specified below, so, by default, the virtual machines will be created with the latest version of Debian.

gcloud compute instances create www-test-1 www-test-2 \
    --tags "https-glb" \
    --machine-type f1-micro \
    --zone us-central1-f \
    --metadata startup-script-url=gs://$BUCKET/www-test-startup.sh

Create a Firewall Rule

Create a firewall rule to ensure only the Global Load Balancer can communicate with the virtual machines, otherwise anyone and anything can communicate directly with the two virtual machines on their public IP addresses. Read more about shutting off HTTP(S) access from everywhere but the load balancing service.

gcloud compute firewall-rules create allow-https-glb-and-health \
    --source-ranges 130.211.0.0/22,35.191.0.0/16 \
    --target-tags https-glb \
    --allow tcp:443

Create an Unmanaged Instance Group

In order for the Global Load Balancer to load balance across the virtual machines, the virtual machines need to be wrapped in an instance group - for this scenario an Unmanaged Instance Group will be used.

Create an Unmanaged Instance Group for the two virtual machines previously created:

gcloud compute instance-groups unmanaged create www-test-unmanaged-ig \
    --zone us-central1-f

gcloud compute instance-groups unmanaged add-instances www-test-unmanaged-ig \
    --instances www-test-1,www-test-2

Create the Global Load Balancer

Finally, it is time to configure the Global Load Balancer. This is a multistep process that will configure the backend and frontend.

The Google Cloud HTTP(S) Global Load Balancer is made up of many different components: a public IP address, Global Forwarding Rules, a Target Proxy, a URL Map (which contains Host Rules, Path Matchers, and Path Rules), and Backend Services (which point to Managed or Unmanaged Instance Groups). Together, all of these components give you a single, Anycast public IP address to frontend and load balance your backend services.

Start by creating a global, public IP address and save the resulting public IP address to an environment variable:

gcloud compute addresses create www-test-glb-ip \
    --ip-version=IPV4 \
    --global

GLB_PUBLIC_IP=$(gcloud compute addresses list '--filter=name=www-test-glb-ip' --format 'value(address)')

Set the list of Named Ports listening in the Unmanaged Instance Group created earlier in the post:

gcloud compute instance-groups unmanaged set-named-ports www-test-unmanaged-ig \
    --named-ports https:443 \
    --zone us-central1-f

Create an HTTPS health check:

gcloud compute health-checks create https https-basic-check \
    --port 443

Create a Backend Service that communicates with the backend virtual machines over HTTPS:

gcloud compute backend-services create www-test-glb-backend \
    --protocol HTTPS \
    --health-checks https-basic-check \
    --global

Point the newly created Backend Service to the Unmanaged Instance Group created earlier in the post:

gcloud compute backend-services add-backend www-test-glb-backend \
    --balancing-mode UTILIZATION \
    --max-utilization 0.8 \
    --capacity-scaler 1 \
    --instance-group www-test-unmanaged-ig \
    --instance-group-zone us-central1-f \
    --global

Create a default URL Map that routes all traffic to the Backend Service:

gcloud compute url-maps create www-test-glb \
    --default-service www-test-glb-backend

Create an SSL certificate resource that maps to the self-signed SSL certificate created earlier in the post:

gcloud compute ssl-certificates create www-test-ssl-cert \
    --certificate server.crt \
    --private-key server.key

Create an HTTP Target Proxy:

gcloud compute target-http-proxies create http-lb-proxy \
    --url-map www-test-glb

Create an HTTPS Target Proxy:

gcloud compute target-https-proxies create https-lb-proxy \
    --url-map www-test-glb \
    --ssl-certificates www-test-ssl-cert

Create a Global Forwarding Rule on port 80:

gcloud compute forwarding-rules create www-test-glb-gfr-http \
    --address $GLB_PUBLIC_IP \
    --global \
    --target-http-proxy http-lb-proxy \
    --ports 80

Create a Global Forwarding Rule on port 443:

gcloud compute forwarding-rules create www-test-glb-gfr-https \
    --address $GLB_PUBLIC_IP \
    --global \
    --target-https-proxy https-lb-proxy \
    --ports 443

At this point the Global Load Balancer is fully programmed, but it might take several more minutes for it to become fully operational. During this time period you might experience 404s when accessing the public IP address in the following section.

Final Test

Once the Global Load Balancer is fully operational, get the public IP address you created in the previous section:

echo $GLB_PUBLIC_IP

Then, navigate to it using a web browser. If you access the public IP address over HTTP, you should automatically be redirected to HTTPS.

If you refresh the web page over and over, the Backend VM Hostname field should eventually change to show that the Global Load Balancer is load balancing across the two virtual machines you created earlier in the post.

Delete Everything

Once you are satisfied with the demo, you should delete all the GCP resources you created so you are not charged further.

If you created a GCP Project specifically for this demo, you can simply delete the GCP Project and all GCP resources will be deleted.

Otherwise, if you want to keep the GCP Project you created, you can delete each GCP resource created in this post with the following commands:

gcloud compute forwarding-rules delete www-test-glb-gfr-https --global
gcloud compute forwarding-rules delete www-test-glb-gfr-http --global

gcloud compute target-https-proxies delete https-lb-proxy
gcloud compute target-http-proxies delete http-lb-proxy

gcloud compute ssl-certificates delete www-test-ssl-cert

gcloud compute url-maps delete www-test-glb

gcloud compute backend-services delete www-test-glb-backend --global

gcloud compute health-checks delete https-basic-check

gcloud compute addresses delete www-test-glb-ip --global

gcloud compute instance-groups unmanaged delete www-test-unmanaged-ig --zone us-central1-f

gcloud compute firewall-rules delete allow-https-glb-and-health

gcloud compute instances delete www-test-1 www-test-2

gsutil rm gs://$BUCKET/{server.key,server.crt,default,www-test-startup.sh}
gsutil rb gs://$BUCKET

References