By Matthew Sachs, Technical Lead Manager
At Google Cloud Next ’17, we announced the beta of Cloud Identity-Aware Proxy (Cloud IAP). Cloud IAP lets you control access to your web applications running on Google Cloud Platform (GCP). You can learn more about it and why it’s a simpler and more secure method than traditional perimeter-based access controls such as LANs and VPNs, in our previous post about Cloud IAP. In this post, we go into the internals of how Cloud IAP works and some of the engineering decisions we made in building it.
How does Cloud IAP work?
When a request comes into App Engine or Cloud Load Balancing HTTP(S), code inside the serving infrastructure for those products checks whether Cloud IAP is enabled for the App Engine app or Google Compute Engine backend service. If it is, the serving infrastructure calls out to the Cloud IAP auth server with some information about the protected resource, such as the GCP project number, the request URL and any Cloud IAP credentials present in the request headers or cookies.
If the request has valid credentials, the auth server can use those credentials to get the identity (email address and user ID) of the user. Using that identity information, the auth server calls Cloud Identity & Access Management (Cloud IAM) to check whether the user is authorized for the resource.
Authenticating with OpenID Connect
The credential that Cloud IAP relies on is an OpenID Connect (OIDC) token. That token can come from either a cookie (
GCP_IAAP_AUTH_TOKEN1) or an
Authorization: bearer header. To initiate the flow needed to get this token, Cloud IAP needs an OAuth2 client ID and secret. When you turn on Cloud IAP from Cloud Console, we silently create an OAuth2 client in your project and configure Cloud IAP to use it. If you use GCP APIs or the Cloud SDK to enable Cloud IAP, you’ll need to configure an OAuth2 client manually.
Anyone who interacts with a Cloud IAP-secured application from a web browser receives a cookie with their credentials. When the Cloud IAP auth server sees a request with missing or invalid credentials, it redirects the user into Google’s OpenID Connect flow. By using the OIDC flow, users get control over which applications can see their identity. The auth server handles the OAuth redirect and completes the OpenID Connect flow.
To protect against Cross-Site Request Forgery attacks, the auth server also generates a random nonce when redirecting the user into the OAuth flow. Auth server stores that nonce in a
GCP_IAAP_XSRF_NONCE cookie, as well as signed with a key private to the auth server in the OAuth flow state parameter (along with the original URL requested by the user, also signed.) When processing an OAuth redirect, the auth server verifies the signature on the state parameter and checks that its nonce value matches the one from the cookie.
To support access from scripts and programs, the auth server also looks for an OIDC token in an Authorization header. The process to obtain an OIDC token given an OAuth2 access token or a service account private key is a bit complex; the IAP documentation provides sample code for authenticating from a service account or mobile app. If you want to know what’s going on behind the scenes there, or want to roll your own, the steps automated by that sample code are:
- Create a JWT with the following claims:
exp: Some time in the future.
iat: The current time.
iss: Your service account’s email address.
target_audience: Either the base URL (protocol, domain and optional port; no path) or OAuth2 client ID for your Cloud IAP-protected application. (This controls the aud claim in the resulting OpenID Connect token. Cloud IAP validates this claim to prevent a token intended for use in one application from being used with another application.)
Authorization with Cloud IAM
The Cloud IAP access list displayed in Cloud Console is really just part of your project’s Cloud IAM policy. You can use all standard Cloud IAM capabilities to manipulate it, including the IAM API and granting the Cloud IAP role at the folder and organization levels of the Cloud IAM hierarchy.
The role that grants access to Cloud IAP is
roles/iap.httpsResourceAccessor. Unlike many other Cloud IAM roles, none of the broad roles like Owner or Editor grant the permissions associated with this role. This was done to better enable scenarios where security administrators are responsible for configuring the access policy, but they’re not intended to use the application. (Yes, they can always grant themselves access, but this way it’s something they have to go out of their way to do. If application owners got access automatically, they might unintentionally access the application.)
Many applications protected by Cloud IAP will want to know the user’s identity, either to perform additional access control or as part of a user preferences system. Cloud IAP provides a few ways to do this. Two of them are straightforward:
- For applications using the Google App Engine standard environment, Cloud IAP supports the App Engine Users API. Existing code using this API typically works with no modifications, and Cloud IAP even uses the same user IDs as Users API.
- Cloud IAP sends the user’s email address and ID in two HTTP headers.
The third way requires a few additional steps to ensure maximum security for your application. For applications that can’t use the Users API and so have to go with option 2, relying on unauthenticated HTTP headers is a security risk2. If you accidentally disable Cloud IAP, anyone could potentially connect to your application and set those headers to arbitrary values! If your application runs on Compute Engine or Google Container Engine, anyone who can connect directly to a VM running your application could then bypass Cloud IAP and set those headers to whatever they want. As discussed earlier, Cloud IAP access control is enforced inside the HTTP(S) load balancer, so if someone can bypass the load balancer, they can bypass Cloud IAP! This could happen if you’ve misconfigured your firewall rules, or because the attacker was able to SSH into the instance or another instance on the network.
So, Cloud IAP provides a third HTTP header, which contains a JSON Web Token (JWT) signed with a Cloud IAP private key. This JWT closely resembles the OpenID Connect token, but it’s signed by Cloud IAP instead of by the Google account service. We considered just passing through the OpenID Connect token that Cloud IAP used to authenticate the user, but by minting our own token, we’re free to add additional methods for users to authenticate to Cloud IAP in the future.
We hope this provides you a solid understanding of how Cloud IAP works behind the scenes, as well as some of the simplicity it offers. Spend a few minutes reading the IAP quickstarts to learn how to use it, and stay tuned for a steady stream of security and identity content.
1 Yes, there’s an extra A.
2 The Users API, on the other hand, is safe. Cloud IAP uses a protected internal channel to set the identity information consumed by this API.