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_idfield to the JWT claim - Populate the
Subjectclaim forAuthType: deploy_token(or ensureSubjectis not empty as best as we can) - Update the
actor: {}field with theuser_idin the event notification payload
B):
- Add an encrypted
Userclaim to the JWT - This new
Userclaim can contain all the fields that Rails need - The registry simply passes back the encrypted
Userobject 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.
- GitLab Rails authenticates the logged in user as usual via
/jwt/auth?service=container_registry&... - Rails obtains the following information and packages into a
kindobject-
tokentype:pat|job_token|deploy_token -
user_nameonly fordeploy_token.user_name -
user_idonly forpatandjob_token
-
- Rails generates a JWT (let's name it
user_jwt) from thekindobject created in step 2. - Rails embeds the
user_jwtinto theauthJWT as a claim and sends it back to the registry - The registry saves the
user_jwtin the request context and sends back the unchanged payload to the/container_registry_event/eventsendpoint - Rails verifies the JWT hasn't been modified and extracts the details appropriately.
- Proceed with Add the user tracking for deploy tokens (#390874 - closed) and Add user tracking for manifest delete events (#394966 - closed)