# Cloud Remote Access API

Source: https://cumulocity.com/docs/cloud-remote-access/cra-api/
Sector: Device management & connectivity
Release: Latest

Cumulocity provides a public API for the Cloud Remote Access feature. This API is designed for two different integration scenarios:

1. Development of **device-side components** which enable access to services like VNC, SSH, Telnet, or arbitrary TCP-based protocols (passthrough) using the device as a gateway.
2. Integration and development of **native TCP protocol clients or forwarding proxies**, for example as an alternative to the commonly used [C8Y Cli passthrough support](https://goc8ycli.netlify.app/docs/examples/remoteaccess/).



## Overview

```mermaid
sequenceDiagram
    actor U as User
    participant CL as Client
    participant CRA as CRA microservice
    participant CORE as Cumulocity Core
    participant DA as Device agent
    participant S as External endpoint <br/>(SSH, Telnet, VNC...)
    
    U -->> CL: Start Client session <br />(Example: SSH)
    CL ->> CRA: Connect WebSocket <br /> to CRA configuration
    activate CRA
    CRA ->> CORE: Create <br />c8y_RemoteAccessConnect<br/> operation
    CORE -->> DA: Push operation
    DA ->> CORE: Mark operation as EXECUTING
    DA ->> CRA: Connect WebSocket <br /> to connection key <br > of operation
    activate DA
    loop
        DA <<-->> S: Forward data packets <br /> to WebSocket <br /> and vice versa   
    end
    DA ->> CORE: Mark operation as SUCCESSFUL
    deactivate CRA
    deactivate DA
```

The diagram above illustrates the end-to-end integration between a user's client and the corresponding external server via Cloud Remote Access (CRA).
In such a setup, we distinguish between the following participants and components:

- A **Cumulocity user**, who wants to access an external service like a web server or SSH that is only reachable from a remote device.
- The **client**. This often is a shell in the Cumulocity UI; alternatively it can be a forwarding proxy like [C8Y Cli](https://goc8ycli.netlify.app/docs/examples/remoteaccess/) or even a custom client application seeking access to the external service.
- The **Cloud Remote Access (CRA)** microservice at `/service/remoteaccess`.
- The **Cumulocity Core platform** (see also the [Cumulocity OpenAPI Specification ](https://cumulocity.com/api/)).
- The **device agent**. A common open-source agent is [thin-edge.io](https://thin-edge.io/).
- An arbitrary **external endpoint**, typically a SSH, HTTP, Telnet or VNC server.

In the following, we now describe both how to implement a device-side agent and a client application that connects to the device-side agent via CRA.

## Client implementation

Each device supporting Cloud Remote Access uses a fragment called `c8y_RemoteAccessList` to hold a list with the configured
endpoints that can be accessed via this device.

An entry in the `c8y_RemoteAccessList` fragment holds the following fragments:

| <div style="width: 20em;">Field </div> | Data Type | Mandatory | Details                                                                                                                                                                                                                                                                                                                                              |
|----------------------------------------|---|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id`                                   | String | Yes | Identifies the configuration of the remote access entry.                                                                                                                                                                                                                                                                                             |
| `port`                                 | Number (Integer) | Yes | The port number of the service on the remote device.                                                                                                                                                                                                                                                                                                 |
| `name`                                 | String | Yes | A user-friendly name for the remote access entry.                                                                                                                                                                                                                                                                                                    |
| `hostname`                             | String | Yes | The hostname or IP address of the service to be accessed remotely.                                                                                                                                                                                                                                                                                   |
| `protocol`                             | String | Yes | The protocol used to access the service.  Either `PASSTHROUGH`, `TCP`, `VNC`, `TELNET` or `SSH`.                                                                                                                                                                                                                                                     |
| `credentials`                          | Object | Yes | An object containing authentication credentials for accessing the remote service.                                                                                                                                                                                                                                                                    |
| `credentials.privateKey`               | String | See details | **Mandatory if `credentials.type` is `KEY_PAIR` or `CERTIFICATE`.** Otherwise, not applicable.                                                                                                                                                                                                                                                       |
| `credentials.password`                 | String | See details | **Mandatory if `credentials.type` is `PASS_ONLY` or `USER_PASS`.** Otherwise, not applicable.                                                                                                                                                                                                                                                        |
| `credentials.certificate`              | String | See details | **Mandatory if `credentials.type` is `CERTIFICATE`.** Otherwise, not applicable.                                                                                                                                                                                                                                                                     |
| `credentials.publicKey`                | String | No | Public key for authentication, if applicable.                                                                                                                                                                                                                                                                                                        |
| `credentials.hostKey`                  | String | No | Host key for authentication, if applicable.                                                                                                                                                                                                                                                                                                          |
| `credentials.type`                     | String | Yes | Type of credentials. Possible values are: <br/>-`NONE`: No credentials required. <br />-`PASS_ONLY`: Only `password` is required. <br />-`USER_PASS`: Both `username` and `password` are required. <br />-`KEY_PAIR`: Both `username` and `privateKey` are required. <br />-`CERTIFICATE`: `username`, `privateKey`, and `certificate` are required. |
| `credentials.username`                 | String | See details | **Mandatory if `credentials.type` is `USER_PASS`, `KEY_PAIR`, or `CERTIFICATE`.** Otherwise, not applicable.                                                                                                                                                                                                                                         |


##### Example 

```json
"c8y_RemoteAccessList": [
        {
            "hostname": "localhost",
            "protocol": "PASSTHROUGH",
            "credentials": {
                "privateKey": null,
                "password": null,
                "certificate": null,
                "publicKey": null,
                "hostKey": null,
                "type": "NONE",
                "username": null
            },
            "port": 33123,
            "name": "My HTTP Echo Server",
            "id": "1"
        }
    ]
```

In the example above a local HTTP echo server is reachable from the device at *http://localhost:33123*. The configuration ID is `1`.
To connect a client application, the client must open a WebSocket to the following URL.


```http
wss://<tenant domain>/service/remoteaccess/client/<device id>/configurations/<configuration id>
```

### WebSocket HTTP headers {#websocket-http-headers}

The Cloud Remote Access service uses the binary WebSocket subprotocol, regardless of which protocol is requested by the client.  Hence, we recommend pinning the WebSocket subprotocol to `binary` using the `Sec-WebSocket-Protocol` header.
Additionally,  a valid authorization header is required, see [Authentication](https://cumulocity.com/api/core/#section/Authentication) in the Cumulocity OpenAPI Specification.

```http
Sec-WebSocket-Protocol: binary 
Authorization: <auth header>
```

### Sending and receiving traffic {#sending-and-receiving-traffic}

Once the WebSocket connection is established, traffic for the endpoint can simply be sent to the WebSocket. Data from the endpoint can be consumed by reading from the WebSocket.
To implement a local forwarding proxy, data from a local server socket must be written to the WebSocket and vice versa.

## Device agent implementation

This section describes how to implement a device agent deployed on a gateway. The device agent is responsible for
creating the device part of the tunnel between a TCP/IP connection at private network and the secure device WebSocket
endpoint.

### Enable Cloud Remote Access in supported operations {#supported-operation}

A device agent that implements an integration with Cloud Remote Access must consume `c8y_RemoteAccessConnect` operations. They are used to inform the
device about tunnel connections it should establish. In order to mark your device being capable of handling them, it needs to include `c8y_RemoteAccessConnect` as
a supported operation in its managed object:

```json

"c8y_SupportedOperations" : [
...
"c8y_RemoteAccessConnect",
...
]
```

### Connect operation {#connect-operation}

This operation is created when the application generates a connect request. The operation is then sent to the device
agent, which establishes a connection between the WebSocket endpoint at the server and the local network endpoint.

Example of an `c8y_RemoteAccessConnect` operation:

```json
{
  ...
  "c8y_RemoteAccessConnect": {
    "hostname": "10.0.0.67",
    "port": 5900,
    "connectionKey": "eb5e9d13-1caa-486b-bdda-130ca0d87df8"
  }
  ...
}
```

| Field         | Data type | Details                                                                |
|:--------------|:----------|:-----------------------------------------------------------------------|
| connectionKey | String    | Shared secret to authenticate the connection request from device side  |
| hostname      | Number    | Endpoint on the local network to connect to                            |
| port          | String    | Port to be used on local network endpoint                              |

### Connecting to a new endpoint {#connecting-to-a-new-endpoint}

For each `c8y_RemoteAccessConnect` operation the device agent receives, it opens a TCP client socket to the provided
hostname and port. Using the provided ConnectionKey the agent also securely connects to the WebSocket endpoint on server
side.

The following steps need to be implemented by the device agent when it receives a `c8y_RemoteAccessConnect` operation.

* The device sets the operation status to EXECUTING.
* The device establishes a connection to the Cumulocity Cloud Remote Access WebSocket at the following URL:
  ```http
  wss://<hostname>/service/remoteaccess/device/<connectionKey>
  ```
* The device sets up a TCP connection to the given hostname and port. Depending on the protocol (VNC, Telnet, SSH) the device will
  initiate a protocol-specific handshake. All data should be forwarded directly to the WebSocket endpoint (if already
  established).
* The operation status is set to SUCCESSFUL or FAILED based on the status of the previous steps.

#### Operating a connected endpoint {#operating-a-connected-endpoint}

When both connections are established and fully functional the agent simply must forward all binary packets between the
TCP connection and the WebSocket in both directions.

#### Disconnecting an endpoint {#disconnecting-an-endpoint}

Whenever one of the connections is terminated (WebSocket or TCP) the device agent should consider the session as ended
and should also terminate both connections associated with the tunnel.

#### Recommendations {#recommendations}

It is highly recommended to implement a small buffer especially for bootstrapping when one connection is already
functional while the other is not setup yet.

### Declaring supported protocols {#declaring-supported-protocols}
A device should declare which remote access protocols it supports. This allows the UI to show only compatible options
when a user configures a remote endpoint. If a device doesn't declare its protocols, the platform will display all
available types by default.
You can declare protocols using either the REST API or SmartREST.

#### Using the REST API {#using-the-restapi}
To declare protocols via REST, set the device's managed object with the `c8y_RemoteAccessSupportedProtocols` fragment.
This fragment holds an array of strings listing the supported protocols.

Valid protocol values are `SSH`, `TELNET`, `VNC`, and `PASSTHROUGH`.

##### Example 
```json
{
    "c8y_RemoteAccessSupportedProtocols": ["PASSTHROUGH", "SSH"]
}
```

#### Using SmartREST {#using-smartrest}
Alternatively, the set of supported CRA protocols can be configured using the SmartREST template 150. It sets the fragment using a message containing a list
of supported protocol names. See [MQTT Static template 150](https://cumulocity.com/docs/smartrest/mqtt-static-templates/#150) for more information.
##### Example
```text
150,SSH,PASSTHROUGH
```
