Skip to content

Code to demonstrate how Google Cloud Storage static content can be protected with IAP.

Notifications You must be signed in to change notification settings

debakkerb/gcs-static-iap

Repository files navigation

Static Webhosting

The purpose of this demo is to host a static website in Google Cloud Storage, with protected resources.
Protection is done via Identity Aware Proxy and Cloud Run. It follows more or less this guide, but the difference is that the Cloud Run endpoint is protected with the Identity Aware Proxy and does not serve a Login page itself.

DISCLAIMER

This is a demo and example and shouldn't be treated as production-ready code. It's your responsibility to ensure that this complies with internal processing and requirements.

Installation instructions

Create a terraform.tfvars-file in this directory with the following content:

billing_account_id      = "123456-123456-123456"
folder_id               = "0123456789"
organization_id         = "0123456789"
cors_origin             = ["https://static-hosting-domain"]
ssl_domain_names        = ["static-hosting-domain"]
login_service_access    = ["user:john.doe@acme.com"]
brand_application_title = "Name of Static Host"
brand_support_email     = "john.doe@acme.com"
iap_client_display_name = "Name of Static Host"

Variables

Please refer to variables.tf for more information on what these variables are and which other variables can be overridden.

terraform.tfvars

Name Description
cors_origin Used for the storage bucket, domain which is allowed to send requests to the bucket.
ssl_domain_names These are the domain names used for the managed SSL certificates.
login_service_access List of identities who require access to the service. Can be a combination of anything, just make sure the type is added as a prefix. So, for example, [ "user:john.doe@acme.com", "group:service-accessors@acme.com"]
brand_application_title Title of the application that is shown to users when the IAP login results in an error
brand_support_email Email address of the developer or support. This address is publicly shown when access is denied to the underlying resource.
iap_client_display_name Display that is shown on the error page when a client is denied access to the underlying resource.

Create Infrastructure

The application requires a key to sign the Cookie, for the underlying CDN endpoint. I recommend rotating the key via the command line on a regular basis.

SIGNING_KEY=$(head -c 16 /dev/urandom | base64 | tr +/ -_)

# Init Terraform
terraform init -upgrade -reconfigure

# Apply the changes
terraform apply -auto-approve -var="cdn_signing_key=${SIGNING_KEY}"

Even though the CDN signing key is sensitive, it will end up in the Terraform state this way. It's not recommended to do this in a PRD environment, so a better approach is to generate the key outside of the Terraform code and push it to Secret Manager and the backend service directly:

PROJECT_ID=$(terraform output -json | jq -r .project_id.value)
echo "$(head -c 16 /dev/urandom | base64 | tr +/ -_)" > key.fm

gcloud secrets versions add $(terraform output -json | jq -r .cdn_secret_name.value) --data-file=./key.fm --project $PROJECT_ID

# Remove the existing signing key, as you can't add two at the same time

gcloud compute backend-buckets delete-signed-url-key $(terraform output -json | jq -r .backend_bucket_name.value) --key-name $(terraform output -json | jq -r .cdn_sign_key_name.value) --project ${PROJECT_ID}

gcloud compute backend-buckets add-signed-url-key $(terraform output -json | jq -r .backend_bucket_name.value) --key-file=./key.fm --key-name=$(terraform output -json | jq -r .cdn_sign_key_name.value)
rm -rf key.fm

When everything is created, it will take a while for the SSL certificate to be generated, signed and generally made available. You can check the status of the SSL certificate by running the following command:

gcloud compute ssl-certificates describe $(terraform output -json | jq -r .ssl_certificate_name.value) --format "value(managed.status)"

Status should be ACTIVE before you can send requests to the SSL endpoint.

DNS Configuration

Before the SSL certificate can become available, it's important to create an A-record on the DNS load balancer that matches the value for ssl_domain_names. Create an A-record and point it to the public IP address of the Load Balancer.

You can find the correct value by running the following command:

terraform output -json | jq -r .load_balancer_ip_address.value

Development

If you want to further develop the application, you need to generate new image tags, as otherwise Cloud Run will not point to the updated service.

terraform apply -var="image_tag=$(date +%s)"

Every time this command runs, it will generate a new image tag and will update the latest revision of Cloud Run.

Existing project

It's possible to deploy the resources in an existing project, in case the user executing the deployment doesn't have the necessary permissions to create new projects.

To accomplish this, add the following to your terraform.tfvars-file:

create_project    = false
project_name      = "<EXISTING_PROJECT_ID>"

The value for variable project_name should correspond to the existing project ID of the target project. The user creating the resources must have the Project IAM Admin (roles/resourcemanager.projectIamAdmin) role on the project.

IAP Brand

There is an issue with the google_iap_brand-resource. The problem is that the underlying API does not allow you to delete the brand from the Google Cloud project. When you run terraform destroy, or you to try to modify the resource, you may see the following error in the console:

╷
│ Error: Error creating Brand: googleapi: Error 409: Requested entity already exists
│ 
│   with google_iap_brand.project_brand,
│   on iap.tf line 17, in resource "google_iap_brand" "project_brand":
│   17: resource "google_iap_brand" "project_brand" {
│ 
╵

The solution is to import the resource into your Terraform state file, via this command:

terraform import google_iap_brand.project_brand projects/$(terraform output -json | jq -r .project_id.value)/brands/$(terraform output -json | jq -r .project_number.value)

About

Code to demonstrate how Google Cloud Storage static content can be protected with IAP.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published