17

I've recently been making use of the GKE Workload Identity feature. I'd be interested to know in more detail how the gke-metadata-server component works.

  1. GCP client code (gcloud or other language SDKs) falls through to the GCE metadata method
  2. Request made to http://metadata.google.internal/path
  3. (guess) Setting GKE_METADATA_SERVER on my node pool configures this to resolve to the gke-metadata-server pod on that node.
  4. (guess) the gke-metadata-server pod with --privileged and host networking has a means of determining the source (pod IP?) then looking up the pod and its service account to check for the iam.gke.io/gcp-service-account annotation.
  5. (guess) the proxy calls the metadata server with the pods 'pseudo' identity set (e.g. [PROJECT_ID].svc.id.goog[[K8S_NAMESPACE]/[KSA_NAME]]) to get a token for the service account annotated on its Kubernetes service account.
  6. If this account has token creator / workload ID user rights to the service account presumably the response from GCP is a success and contains a token, which is then packaged and set back to the calling pod for authenticated calls to other Google APIs.

I guess the main puzzle for me right now is the verification of the calling pods identity. Originally I thought this would use the TokenReview API but now I'm not sure how the Google client tools would know to use the service account token mounted into the pod...

Edit follow-up questions:

Q1: In between step 2 and 3, is the request to metadata.google.internal routed to the GKE metadata proxy by the setting GKE_METADATA_SERVER on the node pool?

Q2: Why does the metadata server pod need host networking?

Q3: In the video here: https://youtu.be/s4NYEJDFc0M?t=2243 it's taken as a given that the pod makes a GCP call. How does the GKE metadata server identify the pod making the call to start the process?

David Xia
  • 5,075
  • 7
  • 35
  • 52
Charlie Egan
  • 4,878
  • 6
  • 33
  • 48

1 Answers1

27

Before going into details, please familiarize yourself with these components:

OIDC provider: Runs on Google’s infrastructure, provides cluster specific metadata and signs authorized JWTs.

GKE metadata server: It runs as a DaemonSet meaning one instance on every node, exposes pod specific metadata server (it will provide backwards compatibility with old client libraries), emulates existing node metadata server.

Google IAM: issues access token, validates bindings, validates OIDC signatures.

Google cloud: accepts access tokens, does pretty much anything.

JWT: JSON Web token

mTLS: Mutual Transport Layer Security

The steps below explain how GKE metadata server components work:

Step 1: An authorized user binds the cluster to the namespace.

Step 2: Workload tries to access Google Cloud service using client libraries.

Step 3: GKE metadata server is going to request an OIDC signed JWT from the control plane. That connection is authenticated using mutual TLS (mTLS) connection with node credential.

Step 4: Then the GKE metadata server is going use that OIDC signed JWT to request an access token for the [identity namespace]/[Kubernetes service account] from IAM. IAM is going to validate that the appropriate bindings exist on identity namespace and in the OIDC provider.

Step 5: And then IAM validates that it was signed by the cluster’s correct OIDC provider. It will then return an access token for the [identity namespace]/[kubernetes service account].

Step 6: Then the metadata server sends the access token it just got back to IAM. IAM will then exchange that for a short lived GCP service account token after validating the appropriate bindings.

Step 7: Then GKE metadata server returns the GCP service account token to the workload.

Step 8: The workload can then use that token to make calls to any Google Cloud Service.

I also found a video regarding Workload Identity which you will find useful.

EDIT Follow-up questions' answers:

Below are answers to your follow-up questions:

Q1: In between step 2 and 3, is the request to metadata.google.internal routed to the gke metadata proxy by the setting GKE_METADATA_SERVER on the node pool?

You are right, GKE_METADATA_SERVER is set on the node pool. This exposes a metadata API to the workloads that is compatible with the V1 Compute Metadata APIs. Once workload tries to access Google Cloud service, the GKE metadata server performs a lookup (the metadata server checks to see if a pod exists in the list whose IP matches the incoming IP of the request) before it goes on to request the OIDC token from the control plane.

Keep in mind that GKE_METADATA_SERVER enumeration feature can only be enabled if Workload Identity is enabled at the cluster level.

Q2: Why does the metadata server pod need host networking?

The gke-metadata-server intercepts all GCE metadata server requests from pods, however pods using the host network are not intercepted.

Q3: How does the GKE metadata server identify the pod making the call to start the process?

The pods are identified using iptables rules.

  • Thanks very much for this detailed explanation and helpful video - details on the topic are hard to come by! I have a few remaining questions. SO comments don't allow new lines to I have included them at the end of my question. – Charlie Egan Nov 05 '19 at 09:20
  • It is my pleasure. I am glad it helped you. I have updated my answer by adding your questions' answers. – Md Daud Walizarif Nov 05 '19 at 18:01
  • Nice explanation, could you clarify step 3 please? Does request an OIDC signed JWT mean retrieve the JWT from the Secret associated with the Kubernetes ServiceAccount trying to authenticate? If not is this using a particular Kubernetes resource that allows you to do this? And what are the claims on the JWT? – dippynark Nov 06 '19 at 14:00
  • 1
    Yes, in step 3, the GKE metadata server requests JSON Web Token (JWT) from the Kubernetes API server which is a front end component for the Kubernetes control plane. Afterwards, the Kubernetes API server answers back with the JWT for Kubernetes service account. (Answer for question about claims on JWT will be in the next comment as I ran out of characters.) – Md Daud Walizarif Nov 06 '19 at 17:12
  • Since the GKE metadata server is requesting an OpenID Connect (OIDC) signed JWT, its claims’ type is reserved. Keep in mind that OIDC is a standards-based authentication protocol built on top of OAuth 2.0.This [link](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) provides you the claims that are used within the ID Token for all OAuth 2.0 flows used by OpenID Connect (OIDC). – Md Daud Walizarif Nov 06 '19 at 17:12
  • Great, saw that the gke metadata server is mounting in the kubeconfig (/var/lib/kubelet/kubeconfig) for the local kubelet so I guess that is how it is able to retrieve said secrets/JWTs – dippynark Nov 07 '19 at 11:48
  • Finally, I see the metadata service is using the container-runtime-interface (mounting in `/var/run/`) - do you know what that is being used for? I assume it is being used to intercept Pod traffic when it is trying to authenticate with some Google API using a client library, but what specifically is it doing? – dippynark Nov 07 '19 at 12:00
  • Container Runtime Interface (CRI) is a plugin interface which enables kubelet to use a wide variety of container runtimes, without the need to recompile, that follows the [OCI Image](https://github.com/opencontainers/image-spec) Format. – Md Daud Walizarif Nov 07 '19 at 13:19
  • Ah yeah I know what the CRI is, I'm wondering how gke-metadata-server is using it? As you say it was designed for the kubelet, so I was surprised to see gke-metadata-server using it – dippynark Nov 07 '19 at 15:22
  • You can consider the gke-metadata-server as an “artificial” metadata server that instead of the service account associated to the instance/node, is serving credentials for another service account specified in the iam.gke.io/gcp-service-account annotation for the service account of the pod. Its implementation with CRI is not usual. If you can specify what links them both, then I will be able to help you further. – Md Daud Walizarif Nov 11 '19 at 16:45
  • This is very helpful! Just curious, are there any official documents that introduce this flow? – JimmyCYJ Jan 16 '20 at 18:17
  • So it would be impossible to run the GKE metadata server locally or in minikube? Because of the mTLS connection between the node and the GKE managed control plane? – red888 Jun 15 '22 at 16:27