Authenticate workloads and devices to Google Cloud using mTLS
Before a workload can access Google Cloud APIs, it needs to authenticate and obtain an access token. For a workload that runs on Google Cloud, we can attach a service account to the compute resource that runs the workload, and that lets the workload authenticate without any additional secrets or keys. But for IoT devices or on-premises workloads, it’s more difficult.
One way to authenticate IoT devices and on-premises workloads is to let them use service account keys. However, service account keys can leak and need to be rotated periodically, so they can easily turn into a maintenance and security burden.
Often, we can eliminate this burden by using workload identity federation: Workload identity federation allows devices or workloads to exchange existing, externally-issued credentials against short-lived access tokens. This means that we don’t have to manage service account keys, which can help to improve security.
However, workload identity federation only works for certain types of credentials: If we have a SAML assertion or OIDC token, we can use workload identity federation to exchange it against a Google access token. But if we only have a different type of credential such as a X.509 certificate and key, we’re out of luck… or are we?
Chaining token exchanges
There’s no problem that can’t be solved by introducing an extra level of indirection, and that also applies to authentication: Instead of exchanging an external credential against a Google access token in one step, we can do it in two steps:
- First, we use a token broker to exchange our external credential (which isn’t compatible with workload identity federation) against an ID token that meets the requirements of workload identity federation.
- Second, we use workload identity federation to exchange the ID token against a Google access token.
By combining workload identity federation with a token broker, we can enable workloads and devices to authenticate to Google Cloud using all sorts of credentials, including X.509 client certificates.
For example, if we’re already using an X.509 public key infrastructure (PKI) to issue client certificates to devices or workloads, we can use a token broker service to let clients authenticate using their existing client certificates and mutual TLS.
But how do we implement such a token broker?
Token broker
A token broker has to mimic an OIDC identity provider by implementing the following interface:
/token
: An OAuth client credentials flow-compatible endpoint that lets workloads authenticate and obtain an OIDC token./.well-known/openid-metadata
: A OIDC-compliant metadata endpoint that lets the token broker present itself as an identity provider towards workload identity federation.
When implementing a token broker, we can take advantage of the fact that we can
use service accounts to sign JWTs
and that IAM automatically publishes a JSON Web Ket Set (JWKS) for each service account’s
Google-managed key pair. So we can implement the /token
endpoint as follows:
- Create a JWT that reflects the claims provided in the input credential (for ex, X.509 CN,
SpiffeID, etc) and asserts itself as issuer (
iss
). - Use the
signJwt
method of its attached service account to sign the JWT.
The following diagram illustrates the process in more detail:
- The workload makes a request to the token broker’s
/token
API and authenticates using some external credential, for example using an mTLS client certificate. - The token broker verifies the credential and identifies the workload.
- The token broker creates an ID token that asserts the workload’s identity.
- The token broker uses the
signJwt
method of its attached service account to sign the ID token and returns it to the workload. By usingsignJwt
to sign tokens, the application avoids having to maintain any encryption keys. - Assuming we registered the token broker as a workload identity pool provider, the workload can now use workload identity federation to exchange the ID token against an STS token.
- The Google STS fetches the OIDC metadata document and JWKS from the token broker and uses that to verify the authenticity of the ID token.
Using the STS token, the workload can now call APIs or, if necessary, impersonate a service account.
To show how the idea works in practice, I published a new project on GitHub that contains an open-source example implementation of a token broker.
Token Service
The token service is an open-source example implementation of a token broker that extends workload identity federation by supporting additional authentication flows. It’s a Java application that uses Quarkus, CDI, and JAX-RS and is designed to run on Cloud Run.
Currently, the implementation supports a single flow, xlb-mtls-client-credentials
: This flow lets clients
authenticate using mutual TLS (mTLS).
Clients use mTLS to authenticate to the token broker. Using the identity information from the client certificate, the token broker can then issue different types of credentials to the client, including short-lived Google credentials.
But the token service isn’t limited to mTLS: The service is designed to be extensible and we can implement
additional authentication flows by extending the ClientCredentialsFlow
base class.
For more information about this new open-source project, see Authenticate workloads and devices.