Make pipeline permissions more controllable and flexible
### Problem to solve
At the moment, pipelines use the security access of the user (personal access token) that is running the pipeline, but with hard-coded limitations applied in order to reduce the risk of security problems. For example, `write_repository` is limited, which prevents a pipeline from pushing changes back to the repository it is working on.
These are sensible basic defaults, but some pipelines need the ability to do things like write to the repository, write to the registry, access to the API, and so on. GitLab needs a way for project administrators to bump up the level of access back up to the full capabilities of the running user in situations where they are needed.
It's important to call out that it will not be possible to elevate a users permissions beyond the permission they have using this method.
### Communication with GitLab
Users use Personal Access Tokens to communicate with GitLab, but these are not secure enough.
The problem here is that in order to communicate with GitLab users need to use Personal Access Tokens. Another way could be using a `$CI_JOB_TOKEN` but these tokens can not access API because it is not possible to configure their scopes, [like it is possible for PATs](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#limiting-scopes-of-a-personal-access-token).
Because a `$CI_JOB_TOKEN`, that is injected by GitLab into the context of a CI/CD build, can not be used with API, users are using personal access tokens, and there are few important differences between PATs and job tokens.
The most important difference is that `$CI_JOB_TOKEN` is an ephemeral token, it can be revoked after a build is complete, it might not be used until a build is running. Another important difference is that a `$CI_JOB_TOKEN` is different for every build, and for every user.
Users that are injecting PATs into their builds, inject the same PAT all the time, and their colleagues can easily see that token, thus it is not secure enough. Every builds receives the same PAT, the token is not revoked when a build is done.
### Intended users
* [Devon (DevOps Engineer)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#devon-devops-engineer)
### Further details
This issue originated out of the comment thread at https://gitlab.com/gitlab-org/gitlab/issues/20416#note_216069060.
The current set of scopes that are possible are as follows:

### Proposal
Gitlab is already an OAUTH2.0 authorization/resource server and tokens are already oauth2 access token, so we can use its full power to solve this issue.
There are three main components to us delivering this feature:
* Change the backend to use JWT instead of access tokens for build permissions
* Expose the necessary JWKs endpoint(s) and facilities to allow external systems to validate the authenticity of the minted tokens
* Design and implement a UX for people to control what scopes (things like `read_repository`, `read_registry`, `read_api`, `write_repository`, etc.. basically the ones you can choose from in the access token dialog) are given for the resources.
A resource is defined as a project or maybe group, and then scopes are applied to resources.
A user will never be able to get more permissions than the user has through this mechanism, so even if `read_api` is turned on in the scope, if the user does not have that ability they don't get it for free. This could maybe be something that comes in a future iteration, but we are not touching it now.
We are also not supporting different scope evaluation depending on the target environment, branch, etc., but will limit all destructive operations (all writes on API, except registry image creation) to be locked to protected jobs. This is because pipelines can run a ton of untrusted code, and with fork pipelines running in parent we will run even more.
#### New Scopes
There are a few scopes that may need to be added for additional functionality of this feature:
* read_package_registry: this is a new scope, but currently users can grant read/write permission only via the `api` scope
* write_package_registry: also a new scope, which will help avoid granting full `api` scope for writing to the package registries.
### Implementation Plan
From @ayufan at https://gitlab.com/gitlab-org/gitlab/issues/20416#note_230466323
1. Change `ci_builds.token` to be JWT,
1. The `JWT` expiry time is `max(runner_timeout, project_timeout)+1h?`,
1. Make `JWT` to hold information about `build_id/pipeline_id/project_id/user_id` and assigned scopes and abilities granted, similar to how we do that for container registry,
1. Make the other `JWT` scopes to be constructed from project settings where you are allowed to specify additional abilities (a scopes) for another projects that you want to access.
1. UI up to choosing I guess, but ideally I would see it like: adding a new project, choosing the scope (likely simple CRUD interface).
The biggest change is change of token to JWT. This is to make the further processing of scope much simpler to implement on all parts of the system. It improves the security of runner communication by creating short-living token, and makes this flexible to handle in generic way addition of new scopes and projects. This is also efficient, as handling JWT is very cheap to do.
### Permissions and Security
Even for the MVC we need a way to disable this feature at the group and instance level. This is a requirement in compliance driven environments where elevating access (even to the user's full access level) may not be permitted. It should be on by default, but easy to disable under these scenarios.
### Documentation
- [NPM Registry Authentication](https://docs.gitlab.com/ee/user/packages/npm_registry/#authenticating-to-the-gitlab-npm-registry)
- [Maven Repository Authentication](https://docs.gitlab.com/ee/user/packages/maven_repository/#authenticating-with-a-personal-access-token)
- [Conan Repository Authentication](https://docs.gitlab.com/ee/user/packages/conan_repository/#authenticating-to-the-gitlab-conan-repository)
- [Container Registry Authentication](https://docs.gitlab.com/ee/user/packages/container_registry/#using-with-private-projects)
### Testing
<!-- What risks does this change pose? How might it affect the quality of the product? What additional test coverage or changes to tests will be needed? Will it require cross-browser testing? See the test engineering process for further help: https://about.gitlab.com/handbook/engineering/quality/test-engineering/ -->
### What does success look like, and how can we measure that?
<!-- Define both the success metrics and acceptance criteria. Note that success metrics indicate the desired business outcomes, while acceptance criteria indicate when the solution is working correctly. If there is no way to measure success, link to an issue that will implement a way to measure this. -->
### What is the type of buyer?
<!-- Which leads to: in which enterprise tier should this feature go? See https://about.gitlab.com/handbook/product/pricing/#four-tiers -->
### Future Improvements
1. In the near term, implementing permission controls using OAuth2 scopes could likely be done in a way sufficiently robust enough to handle operations against a single Project. I think this becomes much trickier if a given job wanted to, say, clone another repo (or repos), update it with changes, and push it back up. Policy-based Access Control seems like a better fit in the mid / long term as it presents an opportunity to unify the different token types. Maybe we could express the scopes as policies under the hood to make this easier in the future.
1. The fractured CI identity model is likely going to need to be addressed regardless of how permissions are expressed. When a `CI_JOB_TOKEN` hits the API, *who* does the API think it is? It's not the user. It *is* a robot with some subset of the users permissions. There doesn't yet exist an entity in GitLab to represent this. (plug for https://gitlab.com/gitlab-org/gitlab/issues/24123#note_215458587)
1. This feature and the necessary refactoring present a good opportunity to introduce a first-class expirable credential API.
### Links / references
issue