How to Access a Private GCS Bucket Using a Load Balancer

Serve a private GCS bucket through an HTTPS load balancer that signs its requests with a service account's HMAC key.

Published
Read time 2 min
Stack Cloud Storage · Cloud Load Balancing · Service accounts

Lab · Step through it

Open in the Lab →
Lab / A private bucket behind a load balancer
Google Cloud GET /image.jpg signed object reader HMAC curl the internet HTTPS LB static IP + cert Internet NEG port 443 Bucket private Service account HMAC key
  1. Step 01 · Console · Cloud Storage Private

    Lock the bucket

    Create the bucket with public access prevention and uniform access control, then upload a file. Nobody outside the project can read it.

    Public access prevention: Enforced
    Access control: Uniform
    Object: image.jpg
  2. Step 02 · Console · IAM and Cloud Storage Key ready

    Give a service account a key

    Create a service account, give it an HMAC key under Cloud Storage → Settings → Interoperability, and grant it Storage Legacy Object Reader on the bucket.

    Service account: PRIVATE_GCS_SA
    HMAC key: HMAC_ACCESS_KEY + HMAC_SECRET
    Bucket role: Storage Legacy Object Reader
  3. Step 03 · Console · Network endpoint groups Backend ready

    Point a NEG at the bucket

    An internet network endpoint group gives the load balancer a backend outside the VPC: the bucket’s own hostname on port 443.

    Name: private-gcs-neg
    Type: Internet NEG (Global)
    Endpoint: [BUCKET_NAME].storage.googleapis.com
    Default port: 443
  4. Step 04 · Console · Load balancing 403 Forbidden

    Put a load balancer in front

    An HTTPS load balancer with a static IP and certificate, the NEG as backend, Cloud CDN on, and a Host header set to the bucket’s hostname. Requests now reach the bucket, unsigned.

    Frontend: HTTPS, static IP, certificate
    Backend: Internet NEG (private-gcs-neg), HTTPS
    Cloud CDN: enabled
    Header: host: [BUCKET_NAME].storage.googleapis.com
  5. Step 05 · Cloud Shell 403 Forbidden

    Export the backend service

    Private origin authentication is not in the console. Export the backend service to YAML and add the HMAC key under securitySettings.

    Terminal window
    gcloud compute backend-services export --global [Backend Service Name] \
    --destination=private-gcs.yaml \
    --project [PROJECT_ID]
  6. Step 06 · private-gcs.yaml 403 Forbidden

    Add the HMAC key

    awsV4Authentication tells the load balancer to sign each request to the bucket with the service account’s key. Nothing changes until it is imported.

    securitySettings:
    awsV4Authentication:
    accessKeyId: [HMAC_ACCESS_KEY]
    accessKey: [HMAC_SECRET]
    originRegion: [REGION]
  7. Step 07 · Cloud Shell Signing

    Import it

    Import the edited file. From now on every request the load balancer sends to the bucket is signed.

    Terminal window
    gcloud compute backend-services import --global [Backend Service Name] \
    --source=private-gcs.yaml \
    --project [PROJECT_ID]
  8. Step 08 · Any terminal 200 OK

    Prove it

    Ask the load balancer for the file. The bucket is still private, but the signed request gets 200 OK.

    Terminal window
    curl -X GET -I https://[LOAD_BALANCER_IP]/image.jpg
    HTTP/1.1 200 OK
    x-goog-storage-class: STANDARD
    via: 1.1 google

Follow these steps to create a private Google Cloud Storage (GCS) bucket and configure access through a load balancer.

Create a Private GCS Bucket

  1. Create a GCS Bucket: Make sure it’s private by selecting Enforce public access prevention on this bucket and choose Uniform for Access control.
  2. Upload Your Content: Add your files to the bucket, for example image.jpg.

Create a Service Account

  1. Navigate to IAM & Admin -> Service Accounts.
  2. Create a Service Account: Add a name for the service account (referred to as PRIVATE_GCS_SA) and leave other settings as default.

Generate HMAC for the Service Account

  1. Go to Cloud Storage -> Settings.
  2. Switch to the Interoperability Tab.
  3. Create an Access Key: Under “Access keys for service accounts,” click + Create a key for a Service Account.
  4. Select the Service Account: Choose the previously created service account (PRIVATE_GCS_SA) and click Create Key.
  5. Save the Access Key and Secret: Note the “Access key” (HMAC_ACCESS_KEY) and “Secret” (HMAC_SECRET) for later use.

Add Service Account Permissions to the Bucket

  1. Go to Cloud Storage -> [BUCKET_NAME].
  2. Switch to the PERMISSIONS Tab.
  3. Grant Access: Click + GRANT ACCESS and type the service account email (PRIVATE_GCS_SA). Assign the “Storage Legacy Object Reader” role and click Save.

Create Network Endpoint Groups

  1. Navigate to Compute Engine -> Network Endpoint Groups.
  2. Create a Network Endpoint Group: Name it like “private-gcs-neg”
  3. Configure the Endpoint Group:
    • Type: Select “Internet NEG (Global, Regional).”
    • Default Port: 443.
    • Add Through: Choose “Fully qualified domain name and port.”
    • Domain Name: Input “[BUCKET_NAME].storage.googleapis.com.”
  4. Create the Endpoint Group.

Create an HTTP(S) Load Balancer

  1. Go to Network Services -> Load Balancing.
  2. Create a Load Balancer: Click + Create Load Balancer.
  3. Configure the Load Balancer:
    • Type: Select Application Load Balancer (HTTP/HTTPS).
    • Frontend Configuration:
      • Protocol: HTTPS (includes HTTP/2 and HTTP/3).
      • IP Address: Static IP address (or create a new one).
      • Certificate: Select or create a new one.
      • HTTP to HTTPS Redirect: Enable it.
    • Backend Configuration:
      • Create a Backend Service: Name it.
      • Backend Type: Select Internet network endpoint group.
      • Protocol: HTTPS.
      • Backend Selection: Choose the previously created Network Endpoint Group.
      • Cloud CDN: Ensure Cloud CDN is enabled.
      • Security: Select None for Cloud Armor backend security policy.
      • Custom Request Headers: Add a header with “host” as the name and “[BUCKET_NAME].storage.googleapis.com” as the value.
  4. Create the Load Balancer: Set the name and finalize the creation.

Configure Private Origin Authentication

  1. Open Cloud Shell or gcloud CLI.
  2. Export Backend Configuration: Use the command:
    Terminal window
    gcloud compute backend-services export --global [Backend Service Name] \
    --destination=private-gcs.yaml \
    --project [PROJECT_ID]
  3. Edit the YAML File: Add the following to the securitySettings section:
    securitySettings:
    awsV4Authentication:
    accessKeyId: [HMAC_ACCESS_KEY]
    accessKey: [HMAC_SECRET]
    originRegion: [REGION]
    Replace [HMAC_ACCESS_KEY], [HMAC_SECRET], and [REGION] with the appropriate values.
  4. Import the Configuration: Use the command:
    Terminal window
    gcloud compute backend-services import --global [Backend Service Name] \
    --source=private-gcs.yaml \
    --project [PROJECT_ID]

Testing

Run the following cURL command

Terminal window
curl -X GET -I https://[LOAD_BALANCER_IP]/image.jpg

IF success, it will return 200 similar like this

Terminal window
HTTP/1.1 200 OK
x-goog-generation: 1721373786382023
x-goog-metageneration: 1
x-goog-stored-content-encoding: identity
x-goog-stored-content-length: 1456
x-amz-meta-x-goog-reserved-source-generation: 1721283240953192
x-goog-hash: crc32c=Bbj32A==
x-goog-hash: md5=Gz8lupMp0rjzxJSMOWfbhQ==
x-amz-checksum-crc32c: Bbj32A==
x-goog-storage-class: STANDARD
accept-ranges: bytes
Content-Length: 1456
x-guploader-uploadid: ACJd0NqW9GOPmKlURsNWyGpDyxgA34g-BB9psFcQFai8XdB52HuwBbDEiO4B3ZJPOB91_ROPluE
server: UploadServer
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
via: 1.1 google
Date: Sat, 20 Jul 2024 06:37:12 GMT
Cache-Control: public,max-age=3600
Age: 17
Last-Modified: Fri, 19 Jul 2024 07:23:06 GMT
ETag: "1b3f25ba9329d2b8f3c4948c3967db85"
Content-Type: image/png
Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000

Reference


Next in Edge & Delivery