Chapter 6. Securing CodeReady Workspaces This section describes all aspects of user authentication, types of authentication, and permissions models on the CodeReady Workspaces server and its workspaces. Authenticating users Authorizing users 6.1. Authenticating users This document covers all aspects of user authentication in Red Hat CodeReady Workspaces, both on the CodeReady Workspaces server and in workspaces. This includes securing all REST API endpoints, WebSocket or JSON RPC connections, and some web resources. All authentication types use the JWT open standard as a container for transferring user identity information. In addition, CodeReady Workspaces server authentication is based on the OpenID Connect protocol implementation, which is provided by default by Keycloak . Authentication in workspaces implies the issuance of self-signed per-workspace JWT tokens and their verification on a dedicated service based on JWTProxy . 6.1.1. Authenticating to the CodeReady Workspaces server 6.1.1.1. Authenticating to the CodeReady Workspaces server using OpenID OpenID authentication on the CodeReady Workspaces server implies the presence of an external OpenID Connect provider and has the following main steps: Authenticate the user through a JWT token that is retrieved from an HTTP request or, in case of a missing or invalid token, redirect the user to the RH-SSO login page. Send authentication tokens in an Authorization header. In limited cases, when it is impossible to use the Authorization header, the token can be sent in the token query parameter. Example: OAuth authentication initialization. Compose an internal subject object that represents the current user inside the CodeReady Workspaces server code. Note The only supported and tested OpenID provider is RH-SSO. Procedure To authenticate to the CodeReady Workspaces server using OpenID authentication: Request the OpenID settings service where clients can find all the necessary URLs and properties of the OpenId provider, such as jwks.endpoint , token.endpoint , logout.endpoint , realm.name , or client_id returned in the JSON format. The service URL is https://codeready-<openshift_deployment_name>.<domain_name>/api/keycloak/settings , and it is only available in the CodeReady Workspaces multiuser mode. The presence of the service in the URL confirms that the authentication is enabled in the current deployment. Example output: { "che.keycloak.token.endpoint": "http://172.19.20.9:5050/auth/realms/che/protocol/openid-connect/token", "che.keycloak.profile.endpoint": "http://172.19.20.9:5050/auth/realms/che/account", "che.keycloak.client_id": "che-public", "che.keycloak.auth_server_url": "http://172.19.20.9:5050/auth", "che.keycloak.password.endpoint": "http://172.19.20.9:5050/auth/realms/che/account/password", "che.keycloak.logout.endpoint": "http://172.19.20.9:5050/auth/realms/che/protocol/openid-connect/logout", "che.keycloak.realm": "che" } The service allows downloading the JavaScript client library to interact with the provider using the https://codeready-<openshift_deployment_name>.<domain_name>/api/keycloak/OIDCKeycloak.js URL. Redirect the user to the appropriate provider's login page with all the necessary parameters, including client_id and the return redirection path. This can be done with any client library (JS or Java). When the user is logged in to the provider, the client side-code is obtained, and the JWT token has validated the token, the creation of the subject begins. The verification of the token signature occurs in two main steps: Authentication: The token is extracted from the Authorization header or from the token query parameter and is parsed using the public key retrieved from the provider. In case of expired, invalid, or malformed tokens, a 403 error is sent to the user. The minimal use of the query parameter is recommended, due to its support limitations or complete removal in upcoming versions. If the validation is successful, the parsed form of the token is passed to the environment initialization step: Environment initialization: The filter extracts data from the JWT token claims, creates the user in the local database if it is not yet present, and constructs the subject object and sets it into the per-request EnvironmentContext object, which is statically accessible everywhere. If the request was made using only a machine token, the following single authentication filter is used: org.eclipse.che.multiuser.machine.authentication.server.MachineLoginFilter : The filter finds the user that the userId token belongs to, retrieves the user instance, and sets the principal to the session. The CodeReady Workspaces server-to-server requests are performed using a dedicated request factory that signs every request with the current subject token obtained from the EnvironmentContext object. Note Providing user-specific data Since RH-SSO may store user-specific information (first and last name, phone number, job title), there is a special implementation of the ProfileDao that can provide this data to consumers. The implementation is read-only, so users cannot perform create and update operations. 6.1.1.1.1. Obtaining the token from credentials through RH-SSO Clients that cannot run JavaScript or other clients (such as command-line clients or Selenium tests) must request the authorization token directly from RH-SSO. To obtain the token, send a request to the token endpoint with the username and password credentials. This request can be schematically described as the following cURL request: The CodeReady Workspaces dashboard uses a customized RH-SSO login page and an authentication mechanism based on grant_type=authorization_code . It is a two-step authentication process: Logging in and obtaining the authorization code. Obtaining the token using this authorization code. 6.1.1.1.2. Obtaining the token from the OpenShift token through RH-SSO When CodeReady Workspaces is installed on OpenShift using the Operator, and the OpenShift OAuth integration is enabled, as it is by default, the user's CodeReady Workspaces authentication token can be retrieved from the user's OpenShift token. To retrieve the authentication token from the OpenShift token, send a schematically described cURL request to the OpenShift token endpoint: The default values for <openshift_identity_provider_name> are: On OpenShift 3.11: openshift-v3 On OpenShift 4.x: openshift-v4 <user_openshift_token> is the token retrieved by the end-user with the command: Warning Before using this token exchange feature, it is required for an end user to be interactively logged in at least once to the CodeReady Workspaces Dashboard using the OpenShift login page. This step is needed to link the OpenShift and RH-SSO user accounts properly and set the required user profile information. 6.1.1.2. Authenticating to the CodeReady Workspaces server using other authentication implementations This procedure describes how to use an OpenID Connect (OIDC) authentication implementation other than RH-SSO. Procedure Update the authentication configuration parameters that are stored in the multiuser.properties file (such as client ID, authentication URL, realm name). Write a single filter or a chain of filters to validate tokens, create the user in the CodeReady Workspaces dashboard, and compose the subject object. If the new authorization provider supports the OpenID protocol, use the OIDC JS client library available at the settings endpoint because it is decoupled from specific implementations. If the selected provider stores additional data about the user (first and last name, job title), it is recommended to write a provider-specific ProfileDao implementation that provides this information. 6.1.1.3. Authenticating to the CodeReady Workspaces server using OAuth For easy user interaction with third-party services, the CodeReady Workspaces server supports OAuth authentication. OAuth tokens are also used for GitHub-related plug-ins. OAuth authentication has two main flows based on the RH-SSO brokering mechanism. The following are the two main OAuth API implementations: internal org.eclipse.che.security.oauth.EmbeddedOAuthAPI external org.eclipse.che.multiuser.keycloak.server.oauth2.DelegatedOAuthAPI To switch between the two implementations, use the che.oauth.service_mode= <embedded|delegated> configuration property. The main REST endpoint in the OAuth API is org.eclipse.che.security.oauth.OAuthAuthenticationService , which contains: An authentication method that the OAuth authentication flow can start with. A callback method to process callbacks from the provider. A token to retrieve the current user's OAuth token. These methods apply to the currently activated, embedded or delegated, OAuthAPI. The OAuthAPI then provides the following underlying operations: Finding the appropriate authenticator. Initializing the login process. Forwarding the user. 6.1.1.4. Using Swagger or REST clients to execute queries The user's RH-SSO token is used to execute queries to the secured API on the user's behalf through REST clients. A valid token must be attached as the Request header or the ?token=USDtoken query parameter. Access the CodeReady Workspaces Swagger interface at https://codeready-<openshift_deployment_name>.<domain_name>/swagger . The user must be signed in through RH-SSO, so that the access token is included in the Request header. 6.1.2. Authenticating in a CodeReady Workspaces workspace Workspace containers may contain services that must be protected with authentication. Such protected services are called secure . To secure these services, use a machine authentication mechanism. Machine tokens avoid the need to pass RH-SSO tokens to workspace containers (which can be insecure). Also, RH-SSO tokens may have a relatively shorter lifetime and require periodic renewals or refreshes, which is difficult to manage and keep in sync with the same user session tokens on clients. Figure 6.1. Authentication inside a workspace 6.1.2.1. Creating secure servers To create secure servers in CodeReady Workspaces workspaces, set the secure attribute of the endpoint to true in the dockerimage type component in the devfile. Devfile snippet for a secure server components: - type: dockerimage endpoints: - attributes: secure: 'true' 6.1.2.2. Workspace JWT token Workspace tokens are JSON web tokens ( JWT ) that contain the following information in their claims: uid : The ID of the user who owns this token uname : The name of the user who owns this token wsid : The ID of a workspace which can be queried with this token Every user is provided with a unique personal token for each workspace. The structure of a token and the signature are different than they are in RH-SSO. The following is an example token view: # Header { "alg": "RS512", "kind": "machine_token" } # Payload { "wsid": "workspacekrh99xjenek3h571", "uid": "b07e3a58-ed50-4a6e-be17-fcf49ff8b242", "uname": "john", "jti": "06c73349-2242-45f8-a94c-722e081bb6fd" } # Signature { "value": "RSASHA256(base64UrlEncode(header) + . + base64UrlEncode(payload))" } The SHA-256 cipher with the RSA algorithm is used for signing machine tokens. It is not configurable. Also, there is no public service that distributes the public part of the key pair with which the token is signed. 6.1.2.3. Machine token validation The validation of machine tokens is performed using a dedicated per-workspace service with JWTProxy running on it in a separate Pod. When the workspace starts, this service receives the public part of the SHA key from the CodeReady Workspaces server. A separate verification endpoint is created for each secure server. When traffic comes to that endpoint, JWTProxy tries to extract the token from the cookies or headers and validates it using the public-key part. To query the CodeReady Workspaces server, a workspace server can use the machine token provided in the CHE_MACHINE_TOKEN environment variable. This token is the user's who starts the workspace. The scope of such requests is restricted to the current workspace only. The list of allowed operations is also strictly limited. 6.2. Authorizing users User authorization in CodeReady Workspaces is based on the permissions model. Permissions are used to control the allowed actions of users and establish a security model. Every request is verified for the presence of the required permission in the current user subject after it passes authentication. You can control resources managed by CodeReady Workspaces and allow certain actions by assigning permissions to users. Permissions can be applied to the following entities: Workspace Organization System All permissions can be managed using the provided REST API. The APIs are documented using Swagger at https://codeready-<openshift_deployment_name>.<domain_name>/swagger/#!/permissions . 6.2.1. CodeReady Workspaces workspace permissions The user who creates a workspace is the workspace owner. By default, the workspace owner has the following permissions: read , use , run , configure , setPermissions , and delete . Workspace owners can invite users into the workspace and control workspace permissions for other users. The following permissions are associated with workspaces: Table 6.1. CodeReady Workspaces workspace permissions Permission Description read Allows reading the workspace configuration. use Allows using a workspace and interacting with it. run Allows starting and stopping a workspace. configure Allows defining and changing the workspace configuration. setPermissions Allows updating the workspace permissions for other users. delete Allows deleting the workspace. 6.2.2. CodeReady Workspaces organization permissions An CodeReady Workspaces organization is a named set of users. The following permissions are applicable to organizations: Table 6.2. CodeReady Workspaces organization permissions Permission Description update Allows editing of the organization settings and information. delete Allows deleting an organization. manageSuborganizations Allows creating and managing sub-organizations. manageResources Allows redistribution of an organization's resources and defining the resource limits. manageWorkspaces Allows creating and managing all the organization's workspaces. setPermissions Allows adding and removing users and updating their permissions. 6.2.3. CodeReady Workspaces system permissions CodeReady Workspaces system permissions control aspects of the whole CodeReady Workspaces installation. The following permissions are applicable to the system: Table 6.3. CodeReady Workspaces system permission Permission Description manageSystem Allows control of the system, workspaces, and organizations. setPermissions Allows updating the permissions for users on the system. manageUsers Allows creating and managing users. monitorSystem Allows accessing endpoints used for monitoring the state of the server. All system permissions are granted to the administrative user who is configured in the CHE_SYSTEM_ADMIN__NAME property (the default is admin ). The system permissions are granted when the CodeReady Workspaces server starts. If the user is not present in the CodeReady Workspaces user database, it happens after the first user's login. 6.2.4. manageSystem permission Users with the manageSystem permission have access to the following services: Path HTTP Method Description /resource/free/ GET Get free resource limits. /resource/free/{accountId} GET Get free resource limits for the given account. /resource/free/{accountId} POST Edit free resource limit for the given account. /resource/free/{accountId} DELETE Remove free resource limit for the given account. /installer/ POST Add installer to the registry. /installer/{key} PUT Update installer in the registry. /installer/{key} DELETE Remove installer from the registry. /logger/ GET Get logging configurations in the CodeReady Workspaces server. /logger/{name} GET Get configurations of logger by its name in the CodeReady Workspaces server. /logger/{name} PUT Create logger in the CodeReady Workspaces server. /logger/{name} POST Edit logger in the CodeReady Workspaces server. /resource/{accountId}/details GET Get detailed information about resources for the given account. /system/stop POST Shutdown all system services, prepare CodeReady Workspaces to stop. 6.2.5. monitorSystem permission Users with the monitorSystem permission have access to the following services. Path HTTP Method Description /activity GET Get workspaces in a certain state for a certain amount of time. 6.2.6. Listing CodeReady Workspaces permissions To list CodeReady Workspaces permissions that apply to a specific resource , perform the GET /permissions request. To list the permissions that apply to a user , perform the GET /permissions/{domain} request. To list the permissions that apply to all users , perform the GET /permissions/{domain}/all request. The user must have manageSystem permissions to see this information. The suitable domain values are: system organization workspace Note The domain is optional. If no domain is specified, the API returns all possible permissions for all the domains. 6.2.7. Assigning CodeReady Workspaces permissions To assign permissions to a resource, perform the POST /permissions request. The suitable domain values are: system organization workspace The following is a message body that requests permissions for a user with a userId to a workspace with a workspaceID : Requesting CodeReady Workspaces user permissions { "actions": [ "read", "use", "run", "configure", "setPermissions" ], "userId": "userID", 1 "domainId": "workspace", "instanceId": "workspaceID" 2 } 1 The userId parameter is the ID of the user that has been granted certain permissions. 2 The instanceId parameter is the ID of the resource that retrieves the permission for all users. 6.2.8. Sharing CodeReady Workspaces permissions A user with setPermissions privileges can share a workspace and grant read , use , run , configure , or setPermissions privileges for other users. Procedure To share workspace permissions: Select a workspace in the user dashboard. Navigate to the Share tab and enter the email IDs of the users. Use commas or spaces as separators for multiple emails.

Commands:
che.keycloak.token.endpoint: http://172.19.20.9:5050/auth/realms/che/protocol/openid-connect/token
che.keycloak.profile.endpoint: http://172.19.20.9:5050/auth/realms/che/account
che.keycloak.client_id: che-public
che.keycloak.auth_server_url: http://172.19.20.9:5050/auth
che.keycloak.password.endpoint: http://172.19.20.9:5050/auth/realms/che/account/password
che.keycloak.logout.endpoint: http://172.19.20.9:5050/auth/realms/che/protocol/openid-connect/logout
che.keycloak.realm: che
curl --data "grant_type=password&client_id= <client_name> &username= <username> &password= <password> " http://<keyckloak_host>:5050/auth/realms/ <realm_name> /protocol/openid-connect/token
curl -X POST -d "client_id= <client_name> " --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" -d "subject_token= <user_openshift_token> " -d "subject_issuer= <openshift_identity_provider_name> " --data-urlencode "subject_token_type=urn:ietf:params:oauth:token-type:access_token" http://<keyckloak_host>:5050/auth/realms/ <realm_name> /protocol/openid-connect/token
oc whoami --show-token
components: - type: dockerimage endpoints: - attributes: secure: 'true'
Header { "alg": "RS512", "kind": "machine_token" } Payload { "wsid": "workspacekrh99xjenek3h571", "uid": "b07e3a58-ed50-4a6e-be17-fcf49ff8b242", "uname": "john", "jti": "06c73349-2242-45f8-a94c-722e081bb6fd" } Signature { "value": "RSASHA256(base64UrlEncode(header) + . + base64UrlEncode(payload))" }
{ "actions": [ "read", "use", "run", "configure", "setPermissions" ], "userId": "userID", 1 "domainId": "workspace", "instanceId": "workspaceID" 2 }