Commit 72fe60a0 authored by Craig Norris's avatar Craig Norris

Merge branch '354845-aqualls-workhorse-channel' into 'master'

Revise Workhorse channels page for tone, style

See merge request gitlab-org/gitlab!84792
parents 1424d7a8 84439849
...@@ -4,29 +4,29 @@ group: Source Code ...@@ -4,29 +4,29 @@ group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
--- ---
# Websocket channel support # Websocket channel support for Workhorse
In some cases, GitLab can provide in-browser terminal access to an In some cases, GitLab can provide the following through a WebSocket:
environment (which is a running server or container, onto which a
project has been deployed), or even access to services running in CI
through a WebSocket. Workhorse manages the WebSocket upgrade and
long-lived connection to the websocket connection, which frees
up GitLab to process other requests.
This document outlines the architecture of these connections. - In-browser terminal access to an environment: a running server or container,
onto which a project has been deployed.
- Access to services running in CI.
Workhorse manages the WebSocket upgrade and long-lived connection to the websocket
connection, which frees up GitLab to process other requests. This document outlines
the architecture of these connections.
## Introduction to WebSockets ## Introduction to WebSockets
A websocket is an "upgraded" HTTP/1.1 request. Their purpose is to Websockets are an "upgraded" `HTTP/1.1` request. They permit bidirectional
permit bidirectional communication between a client and a server. communication between a client and a server. **Websockets are not HTTP**.
**Websockets are not HTTP**. Clients can send messages (known as Clients can send messages (known as frames) to the server at any time, and
frames) to the server at any time, and vice-versa. Client messages vice versa. Client messages are not necessarily requests, and server messages are
are not necessarily requests, and server messages are not necessarily not necessarily responses. WebSocket URLs have schemes like `ws://` (unencrypted) or
responses. WebSocket URLs have schemes like `ws://` (unencrypted) or
`wss://` (TLS-secured). `wss://` (TLS-secured).
When requesting an upgrade to WebSocket, the browser sends a HTTP/1.1 When requesting an upgrade to WebSocket, the browser sends a `HTTP/1.1`
request that looks like this: request like this:
```plaintext ```plaintext
GET /path.ws HTTP/1.1 GET /path.ws HTTP/1.1
...@@ -36,164 +36,166 @@ Sec-WebSocket-Protocol: terminal.gitlab.com ...@@ -36,164 +36,166 @@ Sec-WebSocket-Protocol: terminal.gitlab.com
# More headers, including security measures # More headers, including security measures
``` ```
At this point, the connection is still HTTP, so this is a request and At this point, the connection is still HTTP, so this is a request.
the server can send a normal HTTP response, including `404 Not Found`, The server can send a normal HTTP response, such as `404 Not Found` or
`500 Internal Server Error`, etc. `500 Internal Server Error`.
If the server decides to permit the upgrade, it sends a HTTP
`101 Switching Protocols` response. From this point, the connection is no longer
HTTP. It is now a WebSocket and frames, not HTTP requests, flow over it. The connection
persists until the client or server closes the connection.
In addition to the sub-protocol, individual websocket frames may
also specify a message type, such as:
If the server decides to permit the upgrade, it will send a HTTP - `BinaryMessage`
`101 Switching Protocols` response. From this point, the connection - `TextMessage`
is no longer HTTP. It is a WebSocket and frames, not HTTP requests, - `Ping`
will flow over it. The connection will persist until the client or - `Pong`
server closes the connection. - `Close`
In addition to the subprotocol, individual websocket frames may Only binary frames can contain arbitrary data. The frames are expected to be valid
also specify a message type - examples include `BinaryMessage`, UTF-8 strings, in addition to any sub-protocol expectations.
`TextMessage`, `Ping`, `Pong` or `Close`. Only binary frames can
contain arbitrary data - other frames are expected to be valid
UTF-8 strings, in addition to any subprotocol expectations.
## Browser to Workhorse ## Browser to Workhorse
Using the terminal as an example, GitLab serves a JavaScript terminal Using the terminal as an example:
emulator to the browser on a URL like
`https://gitlab.com/group/project/-/environments/1/terminal`. 1. GitLab serves a JavaScript terminal emulator to the browser on a URL like
This opens a websocket connection to, e.g., `https://gitlab.com/group/project/-/environments/1/terminal`.
`wss://gitlab.com/group/project/-/environments/1/terminal.ws`, 1. This URL opens a websocket connection to
This endpoint doesn't exist in GitLab - only in Workhorse. `wss://gitlab.com/group/project/-/environments/1/terminal.ws`.
This endpoint exists only in Workhorse, and doesn't exist in GitLab.
When receiving the connection, Workhorse first checks that the 1. When receiving the connection, Workhorse first performs a `preauthentication`
client is authorized to access the requested terminal. It does request to GitLab to confirm the client is authorized to access the requested terminal:
this by performing a "preauthentication" request to GitLab. - If the client has the appropriate permissions and the terminal exists, GitLab
responds with a successful response that includes details of the terminal
If the client has the appropriate permissions and the terminal the client should be connected to.
exists, GitLab responds with a successful response that includes - Otherwise, Workhorse returns an appropriate HTTP error response.
details of the terminal that the client should be connected to. 1. If GitLab returns valid terminal details to Workhorse, it:
Otherwise, it returns an appropriate HTTP error response. 1. Connects to the specified terminal.
1. Upgrades the browser to a WebSocket.
Errors are passed back to the client as HTTP responses, but if 1. Proxies between the two connections for as long as the browser's credentials are valid.
GitLab returns valid terminal details to Workhorse, it will 1. Send regular `PingMessage` control frames to the browser, to prevent intervening
connect to the specified terminal, upgrade the browser to a proxies from terminating the connection while the browser is present.
WebSocket, and proxy between the two connections for as long
as the browser's credentials are valid. Workhorse will also The browser must request an upgrade with a specific sub-protocol:
send regular `PingMessage` control frames to the browser, to
keep intervening proxies from terminating the connection - [`terminal.gitlab.com`](#terminalgitlabcom)
while the browser is present. - [`base64.terminal.gitlab.com`](#base64terminalgitlabcom)
The browser must request an upgrade with a specific subprotocol:
### `terminal.gitlab.com` ### `terminal.gitlab.com`
This subprotocol considers `TextMessage` frames to be invalid. This sub-protocol considers `TextMessage` frames to be invalid. Control frames,
Control frames, such as `PingMessage` or `CloseMessage`, have such as `PingMessage` or `CloseMessage`, have their usual meanings.
their usual meanings.
`BinaryMessage` frames sent from the browser to the server are - `BinaryMessage` frames sent from the browser to the server are
arbitrary text input. arbitrary text input.
- `BinaryMessage` frames sent from the server to the browser are
`BinaryMessage` frames sent from the server to the browser are arbitrary text output.
arbitrary text output.
These frames are expected to contain ANSI text control codes These frames are expected to contain ANSI text control codes
and may be in any encoding. and may be in any encoding.
### `base64.terminal.gitlab.com` ### `base64.terminal.gitlab.com`
This subprotocol considers `BinaryMessage` frames to be invalid. This sub-protocol considers `BinaryMessage` frames to be invalid.
Control frames, such as `PingMessage` or `CloseMessage`, have Control frames, such as `PingMessage` or `CloseMessage`, have
their usual meanings. their usual meanings.
`TextMessage` frames sent from the browser to the server are - `TextMessage` frames sent from the browser to the server are
base64-encoded arbitrary text input (so the server must base64-encoded arbitrary text input. The server must
base64-decode them before inputting them). base64-decode them before inputting them.
- `TextMessage` frames sent from the server to the browser are
`TextMessage` frames sent from the server to the browser are base64-encoded arbitrary text output. The browser must
base64-encoded arbitrary text output (so the browser must base64-decode them before outputting them.
base64-decode them before outputting them).
In their base64-encoded form, these frames are expected to In their base64-encoded form, these frames are expected to
contain ANSI terminal control codes, and may be in any encoding. contain ANSI terminal control codes, and may be in any encoding.
## Workhorse to GitLab ## Workhorse to GitLab
Using again the terminal as an example, before upgrading the browser, Using the terminal as an example, before upgrading the browser,
Workhorse sends a normal HTTP request to GitLab on a URL like Workhorse sends a normal HTTP request to GitLab on a URL like
`https://gitlab.com/group/project/environments/1/terminal.ws/authorize`. `https://gitlab.com/group/project/environments/1/terminal.ws/authorize`.
This returns a JSON response containing details of where the This returns a JSON response containing details of where the
terminal can be found, and how to connect it. In particular, terminal can be found, and how to connect it. In particular,
the following details are returned in case of success: the following details are returned in case of success:
- WebSocket URL to **connect** to, e.g.: `wss://example.com/terminals/1.ws?tty=1` - WebSocket URL to connect** to, such as `wss://example.com/terminals/1.ws?tty=1`.
- WebSocket subprotocols to support, e.g.: `["channel.k8s.io"]` - WebSocket sub-protocols to support, such as `["channel.k8s.io"]`.
- Headers to send, e.g.: `Authorization: Token xxyyz..` - Headers to send, such as `Authorization: Token xxyyz`.
- Certificate authority to verify `wss` connections with (optional) - Optional. Certificate authority to verify `wss` connections with.
Workhorse periodically re-checks this endpoint, and if it gets an Workhorse periodically rechecks this endpoint. If it receives an error response,
error response, or the details of the terminal change, it will or the details of the terminal change, it terminates the websocket session.
terminate the websocket session.
## Workhorse to the WebSocket server ## Workhorse to the WebSocket server
In GitLab, environments or CI jobs may have a deployment service (e.g., In GitLab, environments or CI jobs may have a deployment service (like
`KubernetesService`) associated with them. This service knows `KubernetesService`) associated with them. This service knows
where the terminals or the service for an environment may be found, and these where the terminals or the service for an environment may be found, and GitLab
details are returned to Workhorse by GitLab. returns these details to Workhorse.
These URLs are *also* WebSocket URLs, and GitLab tells Workhorse These URLs are also WebSocket URLs. GitLab tells Workhorse which sub-protocols to
which subprotocols to speak over the connection, along with any speak over the connection, along with any authentication details required by the
authentication details required by the remote end. remote end.
Before upgrading the browser's connection to a websocket, Before upgrading the browser's connection to a websocket, Workhorse:
Workhorse opens a HTTP client connection, according to the
details given to it by Workhorse, and attempts to upgrade 1. Opens a HTTP client connection, according to the details given to it by Workhorse.
that connection to a websocket. If it fails, an error 1. Attempts to upgrade that connection to a websocket.
response is sent to the browser; otherwise, the browser is - If it fails, an error response is sent to the browser.
also upgraded. - If it succeeds, the browser is also upgraded.
Workhorse now has two websocket connections, albeit with Workhorse now has two websocket connections, albeit with differing sub-protocols,
differing subprotocols. It decodes incoming frames from the and then:
browser, re-encodes them to the channel's subprotocol, and
sends them to the channel. Similarly, it decodes incoming - Decodes incoming frames from the browser, re-encodes them to the channel's
frames from the channel, re-encodes them to the browser's sub-protocol, and sends them to the channel.
subprotocol, and sends them to the browser. - Decodes incoming frames from the channel, re-encodes them to the browser's
sub-protocol, and sends them to the browser.
When either connection closes or enters an error state,
Workhorse detects the error and closes the other connection, When either connection closes or enters an error state, Workhorse detects the error
terminating the channel session. If the browser is the and closes the other connection, terminating the channel session. If the browser
connection that has disconnected, Workhorse will send an ANSI is the connection that has disconnected, Workhorse sends an ANSI `End of Transmission`
`End of Transmission` control code (the `0x04` byte) to the control code (the `0x04` byte) to the channel, encoded according to the appropriate
channel, encoded according to the appropriate subprotocol. sub-protocol. To avoid being disconnected, Workhorse replies to any websocket ping
Workhorse will automatically reply to any websocket ping frame frame sent by the channel.
sent by the channel, to avoid being disconnected.
Workhorse only supports the following sub-protocols:
Currently, Workhorse only supports the following subprotocols.
Supporting new deployment services will require new subprotocols - [`channel.k8s.io`](#channelk8sio)
to be supported: - [`base64.channel.k8s.io`](#base64channelk8sio)
Supporting new deployment services requires new sub-protocols to be supported.
### `channel.k8s.io` ### `channel.k8s.io`
Used by Kubernetes, this subprotocol defines a simple multiplexed Used by Kubernetes, this sub-protocol defines a simple multiplexed channel.
channel.
Control frames have their usual meanings. `TextMessage` frames are Control frames have their usual meanings. `TextMessage` frames are
invalid. `BinaryMessage` frames represent I/O to a specific file invalid. `BinaryMessage` frames represent I/O to a specific file
descriptor. descriptor.
The first byte of each `BinaryMessage` frame represents the file The first byte of each `BinaryMessage` frame represents the file
descriptor (fd) number, as a `uint8` (so the value `0x00` corresponds descriptor (`fd`) number, as a `uint8`. For example:
to fd 0, `STDIN`, while `0x01` corresponds to fd 1, `STDOUT`).
- `0x00` corresponds to `fd 0`, `STDIN`.
- `0x01` corresponds to `fd 1`, `STDOUT`.
The remaining bytes represent arbitrary data. For frames received The remaining bytes represent arbitrary data. For frames received
from the server, they are bytes that have been received from that from the server, they are bytes that have been received from that
fd. For frames sent to the server, they are bytes that should be `fd`. For frames sent to the server, they are bytes that should be
written to that fd. written to that `fd`.
### `base64.channel.k8s.io` ### `base64.channel.k8s.io`
Also used by Kubernetes, this subprotocol defines a similar multiplexed Also used by Kubernetes, this sub-protocol defines a similar multiplexed
channel to `channel.k8s.io`. The main differences are: channel to `channel.k8s.io`. The main differences are:
- `TextMessage` frames are valid, rather than `BinaryMessage` frames. - `TextMessage` frames are valid, rather than `BinaryMessage` frames.
- The first byte of each `TextMessage` frame represents the file - The first byte of each `TextMessage` frame represents the file
descriptor as a numeric UTF-8 character, so the character `U+0030`, descriptor as a numeric UTF-8 character, so the character `U+0030`,
or "0", is fd 0, STDIN). or "0", is `fd 0`, `STDIN`.
- The remaining bytes represent base64-encoded arbitrary data. - The remaining bytes represent base64-encoded arbitrary data.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment