Ensure workspaces are created under same root namespace as agent
Security MRs: - master: TODO - backports: - 16.7: TODO - 16.6: TODO - 16.5: TODO Original implementation MR (closed without merge): ~~https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests/3754+~~ Security issue: https://gitlab.com/gitlab-org/security/gitlab/-/issues/1018+ <!-- The first line of the MR must be one of the following: 1. `MR: Pending` 2. `MR: <MR link with trailing +>`, and the first description line of the MR should be `Issue: <Issue link with trailing +>` 3. `MR: No MR` For more context, see: https://about.gitlab.com/handbook/engineering/development/dev/create/ide/index.html#1-to-1-relationship-of-issues-to-mrs --> <!-- The following sections should be filled out as part of the refinement process before the issue is prioritized. For more context, see: https://about.gitlab.com/handbook/engineering/development/dev/create/ide/#2-pre-iteration-planning-meeting --> ## Description ### PROBLEM User should be prevented from creating workspace in one group/namespace hierarchy which is associated with an agent from a different group ### STEPS TO REPRODUCE 1. Create or find a user (`user`) 1. Create a root group/namespace (`root_group_1`) 1. Create a project in `root_group_1` (`agent_project`) 1. Give `user` developer access to `agent_project` 1. Create a cluster agent for that project (`agent`) 1. Create a different root group/namespace (`root_group_2`) 1. Create a project in `root_group_2` (`workspace_project`) 1. Give `user` developer access to `workspace_project` 1. Make a graphql request to: 1. create a workspace 1. ...with project set to `workspace_project` 1. ...with agent set to `agent` ### EXPECTED 1. User is prevented from creating workspace with an error related to authorization, which does not expose any sensitive information. For example the error message should be "The resource that you are attempting to access does not exist or you don't have permission to perform this action". ### ACTUAL 1. User is allowed to create the workspace ## BACKGROUND CONTEXT 1. An agent determines where a workspace runs, on some kubernetes cluster somewhere, potentially with access to sensitive data volumes that are exposed within that cluster 2. A workspace is associated with an agent. 3. Our rule is that you should only be able to run a workspace on an agent that is under the same root group/namespace. This implies that they are at least within the same organization. 4. The above rule is especially relevant on gitlab.com / SaaS, where multiple organizations run on the same instance, but are segregated by having different root namespaces. ## EXAMPLE SCENARIO So, the bug here is that (only via the GraphQL API, not the UI) you can violate the rule in number 3, and run a workspace on ANY agent. An example exploit scenario would be: 1. A contractor who has accounts for two independent clients, both of whom have accounts and separate root namespaces/groups on gitlab.com/SaaS, e.g.: `client_a` and `client_b` root groups. 2. Given the adequate roles on both groups, she uses the GraphQL API to create a workspace for a project under under `client_a` group hierarchy, but using an agent in the `client_b` group hierarchy. 3. This lets the workspace run on the `client_b` agent's kubernetes cluster, use the cluster resources to run the workspaces, and potentially access sensitive data on the cluster depending on the volume shares and permissions set up the cluster. ## Acceptance Criteria When the agent and project for a workspace create mutation are in different namespaces - [x] A workspace cannot be created in this scenario ## GraphQL Example Query showing dev projects and agent project and groups: ```graphql query projects432188 { projects(ids: ["gid://gitlab/Project/47", "gid://gitlab/Project/48", "gid://gitlab/Project/49"]) { nodes { id name clusterAgent(name: "remotedev") { id } group { id fullPath } } } } ``` Response from groups query: ```json { "data": { "projects": { "nodes": [ { "id": "gid://gitlab/Project/49", "name": "dev-proj-for-ns-2", "clusterAgent": null, "group": { "id": "gid://gitlab/Group/123", "fullPath": "ns-2-root/ns-2-dev" } }, { "id": "gid://gitlab/Project/48", "name": "dev-proj-for-ns-1", "clusterAgent": null, "group": { "id": "gid://gitlab/Group/118", "fullPath": "ns-1-root/ns-1-dev" } }, { "id": "gid://gitlab/Project/47", "name": "agent-proj-for-ns-1", "clusterAgent": { "id": "gid://gitlab/Clusters::Agent/6" }, "group": { "id": "gid://gitlab/Group/117", "fullPath": "ns-1-root/ns-1-agent" } } ] } } } ``` Mutation violating root namespace check: ```graphql mutation workspaceCreate432188Fail { workspaceCreate( input: {clusterAgentId: "gid://gitlab/Clusters::Agent/6", projectId: "gid://gitlab/Project/49", desiredState: "Running", editor: "webide", maxHoursBeforeTermination: 24, devfileRef: "main", devfilePath: ".devfile.yaml"} ) { workspace { id name namespace url desiredState actualState editor maxHoursBeforeTermination devfile deploymentResourceVersion projectId } errors } } ``` Error response: ```json { "data": { "workspaceCreate": null }, "errors": [ { "message": "Workspace's project and agent's project must both be under the same common root group/namespace.", "locations": [ { "line": 107, "column": 3 } ], "path": [ "workspaceCreate" ] } ] } ``` Mutation not violating check: ```graphql mutation workspaceCreate432188Succeed { workspaceCreate( input: {clusterAgentId: "gid://gitlab/Clusters::Agent/6", projectId: "gid://gitlab/Project/48", desiredState: "Running", editor: "webide", maxHoursBeforeTermination: 24, devfileRef: "main", devfilePath: ".devfile.yaml"} ) { workspace { id name namespace url desiredState actualState editor maxHoursBeforeTermination devfile deploymentResourceVersion projectId } errors } } ``` Success Response ```json { "data": { "workspaceCreate": { "workspace": { "id": "gid://gitlab/RemoteDevelopment::Workspace/76", "name": "workspace-6-1-utkkio", "namespace": "gl-rd-ns-6-1-utkkio", "url": "https://60001-workspace-6-1-utkkio.workspaces.localdev.me?folder=%2Fprojects%2Fdev-proj-for-ns-1", "desiredState": "Running", "actualState": "CreationRequested", "editor": "webide", "maxHoursBeforeTermination": 24, "devfile": "schemaVersion: 2.2.0\ncomponents:\n- name: tooling-container\n attributes:\n gl/inject-editor: true\n container:\n image: quay.io/mloriedo/universal-developer-image:ubi8-dw-demo\n", "deploymentResourceVersion": null, "projectId": "gid://gitlab/Project/48" }, "errors": [] } } } ``` <!-- Replace with other type, e.g. bug or maintenance, if appropriate --> <!-- Replace with other subtype if appropriate --> <!-- By default, all issues start in the unprioritized status. See https://about.gitlab.com/handbook/engineering/development/dev/create/ide/#-remote-development-planning-process --> <!-- For simplicity and to avoid triage bot warnings about missing workflow labels, we will default to issues starting at the refinement phase -->
issue