Design for Supporting Workspaces over SSH
Background
Today we only support access to workspaces using HTTP. However, many developers want to use their IDEs installed on their local machines to access a cloud development / remote environment. Additionally, some IDEs such as the JetBrains IDEs only support that method of access. We therefore need to build SSH support into workspaces.
Design & Options
We already support accessing repositories over SSH using GitLab Shell. Users can register their SSH public keys in GitLab and use their private key stored on their local machine to pull/push from a repository in GitLab.
There is an existing component called the gitlab-workspace-proxy
that we run in the customer's k8s cluster that is being used for authentication and authorization of HTTP requests to workspaces. We want to enhance the proxy to start an additional SSH listener. The workspace proxy will be exposed to the external world by using a Kubernetes service of type LoadBalancer
. Users will login to the proxy by using the Workspace Name as the username and their SSH key.
ssh -i ~/path/to/key workspace-123@ssh.workspaces.mycompany.com
Once the requests hits the proxy, we then need to use the workspace ID to figure out which user is associated to the workspace and then validate the SSH connection using the public key for that user.
flowchart TD
User -->|ssh| LB(Load Balancer)
LB --> Proxy(GitLab Workspaces Proxy)
Proxy -->|Get Authorized Keys| GitLab
GitLab -->|Public keys| Proxy
Proxy --> |Validate keys| Proxy
Proxy --> |Proxy SSH Connection| Workspace
Options
Option 1: The Workspace Proxy communicates with GitLab using a service account / PAT
In this option, we ask the customer to deploy the workspace proxy and provide a Service Account / PAT when they deploy the service. The user tries to login to the workspace proxy using their SSH keys, the workspace proxy then invokes an API to validate the keys and check whether the owner of the workspace owns the keys.
sequenceDiagram
actor User
User ->> Load Balancer: Access Workspace over SSH
Load Balancer ->> GitLab Workspaces Proxy: Access Workspace over SSH
GitLab Workspaces Proxy ->> GitLab Workspaces Proxy: Get workspace name from username
GitLab Workspaces Proxy ->> GitLab: GraphQL - Get Workspace from Workspace Name
GitLab -->> GitLab Workspaces Proxy: User ID, Workspace ID
GitLab Workspaces Proxy ->> GitLab: Get Auth Keys for User /api/v4/keys/<id>
GitLab -->> GitLab Workspaces Proxy: Public keys for user
GitLab Workspaces Proxy ->> GitLab Workspaces Proxy: Validate with public keys
GitLab Workspaces Proxy ->> Workspace: Proxy request
Option 2: The Workspace Proxy communicates with the Agent which in turn calls the API to validate whether the user has access to the workspace
Similar to the previous option, when the user tries to login to the workspace using SSH, the SSH proxy will call a service on the agent that in turn would invoke an API to fetch the public keys.
sequenceDiagram
actor User
User ->> Load Balancer: Access Workspace over SSH
Load Balancer ->> GitLab Workspaces Proxy: Access Workspace over SSH
GitLab Workspaces Proxy ->> GitLab Workspaces Proxy: Get workspace name from username
GitLab Workspaces Proxy ->> agentk: Get keys for Workspace gRPC - getKeys(workspaceID)
agentk ->> GitLab: Get Auth Keys for Workspace /internal/kubernetes/remote_development/workspaces/<workspace name>/keys
GitLab ->> GitLab: Get Workspace by Name
GitLab ->> GitLab: Get keys for User
GitLab -->> GitLab Workspaces Proxy: Public keys
GitLab Workspaces Proxy ->> GitLab Workspaces Proxy: Validate with public keys
GitLab Workspaces Proxy ->> Workspace: Proxy request
Option 3: Login to the workspace with password authentication providing a JWT or PAT.
In this option we use password authentication instead of key based authentication. The user logins to the gitlab-workspaces-proxy
using password authentication. The password that they would need to provide would be a PAT or an Access Token, generated by using the glab
CLI. The user would run a command such as glab workspaces ssh login
, we would then generate a standard gitlab access token for the user.
Note: The OAuth2.0 method would not work with Self Managed (SM instances), the reason is that we don't know the CLIENT_ID for SM instances (the global instance wide app does not exist on SM instances).
sequenceDiagram
actor User
User ->> Load Balancer: Access Workspace over SSH
Load Balancer ->> GitLab Workspaces Proxy: Access Workspace over SSH
GitLab Workspaces Proxy -->> User: Enter Password
User ->> GLAB CLI: Runs glab workspaces ssh show-token or uses PAT
GLAB CLI -->> User: Presents access token
User ->> GitLab Workspaces Proxy: Send AccessToken / PAT
GitLab Workspaces Proxy ->> GitLab Workspaces Proxy: Get workspace name from username
GitLab Workspaces Proxy ->> GitLab: GraphQL - Get Workspace from Workspace Name (Using PAT/AccessToken)
GitLab -->> GitLab Workspaces Proxy: User ID, Workspace ID
GitLab Workspaces Proxy ->> Workspace: Proxy request
The user would use ssh workspace_name@ssh.workspaces.mycompany.com
to login. Both JetBrains gateway and VS Code both support password authentication with SSH.