Cloud Remote Access API

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.

Overview

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
(SSH, Telnet, VNC...) U -->> CL: Start Client session
(Example: SSH) CL ->> CRA: Connect WebSocket
to CRA configuration activate CRA CRA ->> CORE: Create
c8y_RemoteAccessConnect
operation CORE -->> DA: Push operation DA ->> CORE: Mark operation as EXECUTING DA ->> CRA: Connect WebSocket
to connection key
of operation activate DA loop DA <<-->> S: Forward data packets
to WebSocket
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 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 ).
  • The device agent. A common open-source agent is 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:

Field
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:
-NONE: No credentials required.
-PASS_ONLY: Only password is required.
-USER_PASS: Both username and password are required.
-KEY_PAIR: Both username and privateKey are required.
-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
"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.

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

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 in the Cumulocity OpenAPI Specification.

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

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.

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:


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

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:

{
  ...
  "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

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:
    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

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

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

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.