Update JWT Claims with user information for the Container Registry

Context

As part of Container Registry GMAU: Track usage (&8213), we keep track of container registry usage by using webhook notifications emitted by the registry and consumed by GitLab Rails. In Container Registry: Process webhook notificatio... (#385497 - closed) we started tracking tag creation and deletion, followed by Container Registry: Process webhook notificatio... (#382568 - closed).

The event notification payload contains an actor: {} object with a name and a user_type key in it as defined in the registry documentation. The registry populates this information, based on the JWT claims offered by the /jwt/auth endpoint on a successful user authentication.

Problem

The claims not always contain the Subject (username) (see !107918 (comment 1269744199)), this is true for deploy_token, so Rails cannot track the username properly.

Proposal

The solution is to include the username and, perhaps the user_id in the actor:{} object of the notification payload so that Rails can identify the username properly and make the processed data for tracking more accurate. There are a couple of solutions that involve adding Claims to the JWT when ?service=container_registry, these are:

A):

  • Add a user_id field to the JWT claim
  • Populate the Subject claim for AuthType: deploy_token (or ensure Subject is not empty as best as we can)
  • Update the actor: {} field with the user_id in the event notification payload

B):

  • Add an encrypted User claim to the JWT
  • This new User claim can contain all the fields that Rails need
  • The registry simply passes back the encrypted User object to Rails as part of the event notification payload, e.g. `actor: { EncryptedUser: {} }
  • We could be interesting here is have an encrypted payload that the container registry simply gives back in the notification. That's what we do with the JWT token in the Conan Repository (code linked in my previous comment). This way, rails can choose whatever fields it want, encrypt that, send it to the container registry, the container registry read and return back that field as-is, rails can decrypt the part and retrieve the fields. That's what we do with the JWT token for the Conan Repository (see my previous comment). The only doubt I have is can we encrypt a single field in the JWT token, not the whole token. I'm not sure.

  • What's nice with this is that:

  • That field could be considered as optional = the GitLab container registry will return it only if it is present. It doesn't need to require it.

  • If we need updates on the data contained in that field, it's a rails only change. No change for the container repository.

I am in favour of solution B, however we probably need to get approval from Application Security and get ~"group::authentication and authorization" involved in the process too.

Whichever solution we go with, will give way to Add the user tracking for deploy tokens (#390874 - closed) and Add user tracking for manifest delete events (#394966 - closed)


Solution

The agreed solution is to implement a modified version of option (B) above.

  1. GitLab Rails authenticates the logged in user as usual via /jwt/auth?service=container_registry&...
  2. Rails obtains the following information and packages into a kind object
    • token type: pat | job_token | deploy_token
    • user_name only for deploy_token.user_name
    • user_id only for pat and job_token
  3. Rails generates a JWT (let's name it user_jwt) from the kind object created in step 2.
  4. Rails embeds the user_jwt into the auth JWT as a claim and sends it back to the registry
  5. The registry saves the user_jwt in the request context and sends back the unchanged payload to the /container_registry_event/events endpoint
  6. Rails verifies the JWT hasn't been modified and extracts the details appropriately.
  7. Proceed with Add the user tracking for deploy tokens (#390874 - closed) and Add user tracking for manifest delete events (#394966 - closed)
Edited by Jaime Martinez