Breaking down Google Cloud IAP
By: Mayan Salama
Tags: Identity Aware Proxy, Kubernetes, Microservices, Google Cloud Platform, Authentication

But not literally

Recently I’ve had the problem of securing a custom ETL tool for a client project built using a combination of AppEngine and Kubernetes applications on Google Cloud Platform (GCP). I need to control access to these apps from external networks, and ideally integrate with an existing IAM policy. Enter IAP!

Identity Aware Proxy (IAP) is GCP’s offering to lock down applications that would otherwise be publicly exposed on the cloud. The sell is pretty sweet, just turn it on and within minutes you get a free wall of Google’s good stuff surrounding your shamefully exposed app. The security guy stops sweating and everyone is happy, right? Well… kinda: It does what the box says, however there are some design decisions that will leave you a little bit stumped.

In this post I’ll cover how it works behind the scenes, and how you can integrate with a micro-services architecture built around service accounts. I’ll provide a summary of the good and the bad at the end if you’re after a TLDR. Otherwise, let’s see how it works, and more importantly, how you can use it.

Build the Wall!

Turning on IAP is pretty simple. In fact, let’s do it right now. First things first, I’ve deployed a basic flask app that will print out the user’s identity. For testing purposes I’ve done this in App Engine Standard, as it has the users API baked in to validate identity.

IAP is available in Security > Identity-Aware Proxy. Once you get here you’re greeted with a screen like this:

It tells us that we need to configure our OAuth consent screen before I can protect my app. This screen is presented to unauthenticated users when they hit a secured endpoint, and provides information like your company name, homepage, and privacy policy. Unfortunately there is no way to automate this configuration yet which means that an IAP-centric app cannot use a fully-automated deployment.

Anyway, once we’ve configured that then we can turn IAP on for all App Engine apps in the project. This will instantly lock down all AppEngine apps completely. Including the service URL below will allow authorised users to access that URL.

After this when I hit my App Engine endpoint I’m prompted to log in and:

Oops — my account doesn’t have access to the App Engine resource yet. On the IAP page under access you’ll find the add button, which will let you give IAP permissions to users at the project level. Behind the scenes this is just applying the “IAP-Secured Web App User” IAM role to the account you provide. This means that we can apply these permissions to already existing groups or roles. Once that change has propagated we get the result:

It worked!

But how does it work?

IAP is the central pillar of BeyondCorp, enabling remote authentication to online-services without a VPN. It can be enabled for App Engine or a Cloud Load Balancer (supporting GCE or GKE instances), and configured via IAM to allow fine grained access-control. Google says it can do authorisation as well, however IAP permissions are applied at the project level. If you turn on IAP for two applications, and grant a user IAP access, they will be able to access both. If you want to get more low level, for example at the application or even endpoint level, you’ll have to build some scaffolding around what IAP gives you.

At a high level, IAP has two main layers:

Resource Authorisation — an Oauth2 flow to generate a signed access token. IAP will use this token to validate identity. App Validation — verifying a user’s identity using signed headers generated by IAP. This provides an additional layer of security if someone manages to bypass IAP (or if you forget to turn it on ;) )

Depending on what you’re protecting and the language you’ve written it in you’ll have various levels of support to implement these two layers. The simplest use case is authorising as a user, as demonstrated in the example above. This process can be done programmatically (see here), however if you’re thinking about automating sign-in then you should probably be using service accounts. Unfortunately, automating this process is more complicated; if your app isn’t written in Java, C#, Python, or PHP* then things are going to get cURLy.

* At the time of writing these are the only available languages in the docs.

Bizarrely, this list doesn’t include Node.js. Although Cloud Functions now supports Python, adding IAP tokens to a Node 6 app is a nightmare (see this handy stack overflow thread). With Node 8 you should in theory be able to leverage the google-auth-library, although the example requires a Service Account key file, instead of leveraging the application credentials.

Resource Authorisation

In order to pass IAP border control and make a request against a protected app, we need to generate an OpenID token signed by Google and add it to our request as a header. To do that, we need to generate a JWT signed by the service account that our app is using. Fortunately, the examples in the docs implement this logic for you, so you don’t need to worry about it — unless you’re writing Node 6. Unfortunately, I had to implement this in Node 6.

We used the following approach, based on the above stack overflow link. Note that I had mixed results with this approach — if you get the ambiguous “401: Error Code 13” then the only advice I have for you is to start from scratch on a new project. I wish I was joking. Anyway, here’s what that snippet is actually doing:

Get service account access token from instance metadata store Create a JWT header and claim set for our OpenID request Sign that JWT using our service account access token and the Sign Blob API Use signed JWT to get OpenID token Attach that OpenID token to our request as a header

Frankly, if I were to do it again I’d just do it in Python (or one of the other supported languages) and use Google’s sample code.

Once you’ve generated a token, you can make an authenticated request by adding it to the “Bearer” header. A request made to an IAP protected endpoint will be redirected to the IAP gateway, where the token is decoded and validated. If valid, IAP will then create or replace the x-goog headers which can be used by the app to validate identity.

App Validation

In order to prevent nefarious parties (or l337 h4xors as they’re known in the industry) from accessing your app, IAP uses two layers of security: The first layer requires a token as generated above, which is used to validate the user’s identity. The second stage involves encrypting this identity into a second token using keys managed by the IAP service. This token can then be decrypted using public keys, which is what makes IAP so secure.

This is because spoofing this token would require knowing the private keys used to encode the IAP assertion. As long as your app performs this validation, it is locked down even if IAP isn’t turned on.

A Quick Note on GKE

Before I wrap up, a quick note on securing a GKE app with IAP. You’ll need a DNS name and an Ingress load balancer, as covered in the tutorial here. However, there’s a chicken and egg problem with this deployment that’s a pain to deal with:

In order to decode a JWT token inside a GKE app you need to know the backend service ID of that app.

This means that if you want to decode the IAP header inside your app and get the requester’s account you need the load-balancer to already exist. Since this isn’t possible you are left with three options:

Redeploy the app with the correct configuration once the load balancer is created Use dynamic config, for example by using a config map linked to a file Look up the backend service ID using the Instance Metadata server at runtime, also known as the “O’Reilly” option.

Wrapping Up

IAP provides a fast and secure way to lock down your apps, however there are a few caveats:

Not being able to automatically create an Oauth2 consent screen will prevent any deployments that use IAP from being fully automated.

Access is provisioned per account for all IAP-protected services at the project level. Access control at the application level will require additional scaffolding on top of what IAP provides.

The support for token generation in Javascript needs a lot of work.

Using this with Kubernetes will complicate deployment requirements

That being said, it still passes with flying colours. Authentication is hard, and having it as a fully managed service more than makes up for the overhead in deployment. Some of these problems are also minor fixes, and will likely become available as the product matures. If you’re searching for a quick-and-easy to lock down your services looks no further.

A Note on Cloud Endpoints

If you do need something with more fine-grained access control and better language support have a look at Cloud Endpoints. It doesn’t use the same 2-layer model as IAP, and you will have to manage swagger specs at the application level, however it does offer a more extensible service.

https://www.servian.com/gcp/