Securing Application with Identity Aware Proxy
This guide will show how to enable Identity Aware Proxy (IAP) to securely expose your application to the internet.
Overview
This guide will use our Terraform module in order to enable IAP for the application from one of our tutorials.
Enable IAP and create OAuth client
- Before securing the application with IAP, ensure that the OAuth consent screen is configured. Go to the IAP page and click “Configure consent screen” if prompted.
- Create an OAuth 2.0 client ID by visiting the Credentials page and selecting “Create OAuth client ID”. Use the
Web application
type and proceed with the creation. Save the Client ID and secret for later use. - Go back to the Credentials page, click on the OAuth 2.0 client ID you created, and add the redirect URI as follows:
https://iap.googleapis.com/v1/oauth/clientIds/<CLIENT_ID>:handleRedirect
(replace<CLIENT_ID>
with the actual client ID). This is crucial because the redirect URI is used by the OAuth 2.0 authorization server to return the authorization code to your application. If it is not configured correctly, the authorization process will fail, and users will not be able to log in to the application.
Apply the IAP Terraform module
- Create a directory for IAP terraform config. If you came here from another guide, then you should already have this directory created, otherwise, create it now:
mkdir iap
cd iap
-
Create terraform files with the following contents:
File
iap.tf
:module "iap_auth" { source = "github.com/ai-on-gke/common-infra//common/modules/iap-with-cluster?ref=main" project_id = var.project_id cluster_name = var.cluster_name cluster_location = var.cluster_location namespace = var.k8s_namespace k8s_backend_service_name = var.k8s_backend_service_name k8s_backend_service_port = var.k8s_backend_service_port support_email = var.support_email app_name = var.app_name client_id = var.client_id client_secret = var.client_secret domain = var.domain members_allowlist = var.members_allowlist create_brand = var.create_brand k8s_ingress_name = var.k8s_ingress_name k8s_managed_cert_name = var.k8s_managed_cert_name k8s_iap_secret_name = var.k8s_iap_secret_name k8s_backend_config_name = var.k8s_backend_config_name }
File
variables.tf
:variable "project_id" { type = string description = "GCP project ID" } variable "cluster_name" { type = string description = "Name of a target GKE cluster where the target application is deployed" } variable "cluster_location" { type = string description = "Location of a target GKE cluster where the target application is deployed" } variable "app_name" { type = string description = "App Name" } # IAP settings variable "create_brand" { type = bool description = "Create Brand OAuth Screen" default = false } variable "k8s_namespace" { type = string description = "Kubernetes namespace where resources are deployed" } variable "k8s_ingress_name" { type = string description = "Name for k8s Ingress" default = "" } variable "k8s_managed_cert_name" { type = string description = "Name for k8s managed certificate" default = "" } variable "k8s_iap_secret_name" { type = string description = "Name for k8s iap secret" default = "" } variable "k8s_backend_config_name" { type = string description = "Name of the Kubernetes Backend Config" default = "" } variable "k8s_backend_service_name" { type = string description = "Name of the Backend Service" } variable "k8s_backend_service_port" { type = number description = "Name of the Backend Service Port" } variable "domain" { type = string description = "Provide domain for ingress resource and ssl certificate. " default = "{IP_ADDRESS}.sslip.io" } variable "support_email" { type = string description = "Email for users to contact with questions about their consent" default = "" } variable "client_id" { type = string description = "Client ID used for enabling IAP" default = "" } variable "client_secret" { type = string description = "Client secret used for enabling IAP" default = "" } variable "members_allowlist" { type = list(string) default = ["domain:your-domain.com"] }
File
outputs.tf
:output "app_public_ip" { value = module.iap_auth.ip_address } output "app_url" { value = "https://${module.iap_auth.domain}" } output "k8s_managed_cert_name" { value = module.iap_auth.k8s_managed_cert_name }
-
Create a tfvars file with the following values.
starIf you came from another guide, then you may already have this file.
The file must have the following variables:
project_id = "<PROJECT_ID>" cluster_name = "<CLUSTER_NAME>" cluster_location = "<CLUSTER_LOCATION>" app_name = "<APP_NAME>" k8s_namespace = "<KUBERNETES_NAMESPACE>" k8s_backend_service_name = "<SERVICE_NAME>" k8s_backend_service_port = "<SERVICE_PORT>" support_email = "<SUPPORT_EMAIL>" client_id = "<CLIENT_ID>" client_secret = "<CLIENT_SECRET>"
Where:
project_id
- The project ID.cluster_name
- Name of a target cluster where app resources are deployed.cluster_location
- Location of a target cluster.app_name
- Name of the application. This will be used as a prefix for created resources names.k8s_namespace
- The namespace where the app is deployed.k8s_backend_service_name
- Name of a service to expose.k8s_backend_service_port
- Port of a service to expose.support_email
- A support email to be shown in the authorization screen.client_id
- ID of a created OAuth client.client_secret
- Secret if a created OAuth client
For information about other variables please refer to the
variables.tf
file. -
Init the Terraform config:
terraform init
-
Apply the IAP Terraform config:
terraform apply -var-file values.tfvars
-
Wait for a managed certificate to be provisioned. Since the provisioning time for the certificate is longer than for other resources, it should be safe to assume that other resources are also ready:
kubectl wait --for='jsonpath={.status.domainStatus[0].status}=Active' managedcertificate/$(terraform output -raw k8s_managed_cert_name) --timeout=20m
-
Get the url of the app:
terraform output app_url
Cleanup
terraform destroy -var-file values.tfvars
Troubleshooting
Can not create valid Healthcheck
By default the Health check that is created for a GCP LoadBalancer backend is derived from the Pod readiness probe, so make sure that its values are compatible with GCP healthcheck.
For example the service object has to use a port only from range 80 to 443.
Read more here.