eclipse-ditto / ditto

Eclipse Ditto™: Digital Twin framework of Eclipse IoT - main repository
https://eclipse.dev/ditto/
Eclipse Public License 2.0
666 stars 218 forks source link

Provide CoAP endpoint in Ditto's gateway, providing the Ditto HTTP API via CoAP #1582

Open thjaeckle opened 1 year ago

thjaeckle commented 1 year ago

Currently, Ditto provides a very advanced (e.g. supporting partial requests/updates, "PATCH" updates, server sent events for notifications) HTTP API to be consumed by either frontends, mobile applications or even backends.

Ditto currently does not directly provide an API for devices to interact with. That was envisioned to be encapsulated via a separate "Device Connectivity Layer" (like an MQTT broker or an Eclipse Hono or a Apache Kafka). That way, a managed Ditto "connection" would be responsible for connecting to this extra device connectivity layer and translating from/to the specific protocol (e.g. MQTT, AMQP, Kafka).

This has the downside that a (productive) setup with Ditto always requires an additional broker or infrastructure for connecting devices.

CoAP

CoAP, as an equivalent to HTTP for constrained devices, could be a perfect addition to Ditto in order to enable an IoT backend without the need for an additional broker. As a CoAP API can be implemented basically the same as an HTTP API, Ditto's gateway service (currently responsible for providing the HTTP API, the WebSocket API and the SSE API) would be the perfect location for additionally providing CoAP endpoints.

Devices could then, using CoAP, directly interact with their "twins" (things), e.g. retrieving a property, updating a property, or even retrieving the "desired" state, etc.

CoAP Endpoints

The most straight forward way of defining the CoAP endpoints to support is to basically support all of the existing "/api/2/things" HTTP endpoints also as CoAP endpoints:

Authentication

CoAP supports different authentication mechanisms, as far as I know:

I currently do not have more knowledge - I assume that Ditto could configure a Certificate Authority (CA) for e.g. authenticating client certificates. Or that PSKs could be configured as well via configuration or a file mount.

Feedback on that would be appreciated :)

Authorization

Once a device via CoAP was authenticated (see above), a "subjectId" would be determined from the authenticated device, e.g.:

coap-psk:<the-authenticated-device-id>

Using this subject in a Ditto Policy, a device can get authorized to e.g. read/write its twin data or to send/receive messages.

Example policy:

{
  "policyId": "namespace:the-policy",
  "entries": {
    "DEVICE": {
      "subjects": {
        "coap-psk:my-device-4711": {
          "type": "CoAP PSK based authentication"
        }
      },
      "resources": {
        "thing:/": {
          "revoke": [],
          "grant": [
            "READ",
            "WRITE"
          ]
        },
        "message:/": {
          "revoke": [],
          "grant": [
            "READ",
            "WRITE"
          ]
        }
      }
    },
    "DEFAULT": {
      "subjects": {
        "nginx:ditto": {
          "type": "Authenticated user via nginx htpasswd"
        }
      },
      "resources": {
        "policy:/": {
          "revoke": [],
          "grant": [
            "READ",
            "WRITE"
          ]
        },
        "thing:/": {
          "revoke": [],
          "grant": [
            "READ",
            "WRITE"
          ]
        },
        "message:/": {
          "revoke": [],
          "grant": [
            "READ",
            "WRITE"
          ]
        }
      }
    }
  }
}

Implementation

The implementation would rely on our sister project, Eclipse Californium. I see 2 options how to do the implemtation:

Testing

Is there an equivalent to e.g. cURL (or httpie / Postman / etc.) for doing CoAP requests to test during development? For unit testing I assume that we also can use Californium as the client?

Deployment

I also have no idea what CoAP (UDP) endpoints means to a (cloud) deployment, e.g. regarding loadbalancers.

Maybe someone has more input on that, what to consider, etc.?

Please provide feedback / input

To anyone who is interested in this topic, directly providing CoAP endpoints in Ditto, please comment and provide feedback.

May I invite @boaks, the project lead of Eclipse Californium and the CoAP expert, to join the discussion? :) Especially regarding the authentication of devices and what Ditto would have to support to configure credentials I do currently not know ..

boaks commented 1 year ago

A lot of questions :-).

Let's start:

Authentication:

Californium itself uses "callbacks" to fetch the credentials. The callbacks may be used in a synchronous or asynchronous manner. Californium comes also with some demo-implementations. I don't know, how ditto keeps/manages the credentials, but maybe it's possible to implement the Californium interfaces using the current ditto credentials implementation.

PSK: similar to "username/password", but the password is not exchanged and must be available on both sides (server and client) in clear.

AdvancedPskStore

certificate based: Californium supports x509 and RFC7250 (Raw public key).

To authenticate Californium uses CertificateProvider

To verify the authentication of the other peer NewAdvancedCertificateVerifier

(Just to mention: the curios names are the result of a development over several periods. Maybe with Californium 4.0 we polish the names. It will then be some "typing work", but I guess it would make it easier afterwards. For now, no schedule for 4.0 exists).

All the interfaces are in java packages and comes there with demonstration implementations. Though I consider it valuable to use Californium on its own, I added a MultiPskFileStore and I will see, what I can provide in the future. Anyway, for upstream projects it's mostly preferable to implement these interfaces using the own infrastructure.

boaks commented 1 year ago

Authorization:

Each Message comes with it's EndpointContext. Depending on incoming or outgoing messages, the context is the destination or source context. For incoming request it will be the sourcecontext, and that keeps the Principal, if DTLS (or TLS) is used. If not encrypted communication is to be used, the id/principal must be sent e.g. as URI query parameter or part of the URI path.

boaks commented 1 year ago

Implementation:

I guess, that's also a question, if the common functionality could be extracted in a reusable module and each connector then mainly implements it's own mapping layer. I guess, a first PoC directly using Californium will provide then the information to decide, what's the best approach.

boaks commented 1 year ago

Testing:

I use several clients. The most clients are very simple and I'm used to adapted or extend them for special tests.

UI Client

CLI Client

JMeter Client Plugin

Benchmark Client

boaks commented 1 year ago

Deployment:

At least today, many clouds provide UDP support. Also the main tools, docker and k8s comes with UDP support.

Californium offers some help here, see Wiki

cf-extplugtest-server

One common pitfall is the use of NATs (maybe even not intended and more unware done by some part of the infrastructure). For UDP that quite often comes with short timeouts. For "single endpoints", using RFC 9146 (DTLS 1.2 CID) is a great solution here. For clusters it's more complicated. I've used

built-in dtls cid cluster support cf-cluster

My impression of the past years was, that it was frequently requested, but close to no deployment reached even the limit of a single connector.

thjaeckle commented 1 year ago

Tanks, Achim @boaks for the many inputs 👍

I guess, a first PoC directly using Californium will provide then the information to decide, what's the best approach.

Yes, that would be my next step .. :)

Regarding

I don't know, how ditto keeps/manages the credentials, but maybe it's possible to implement the Californium interfaces using the current ditto credentials implementation.

Ditto currently does not keep/manage credentials - for HTTP it relies on OpenId Connect providers to authenticate (and then "just" uses the JWT issued by those providers to apply authorization). Or it relies on a reverse proxy (e.g. a "nginx") to manage credentials.

I would probably want to avoid adding a separate persistence for credentials and go into the direction of providing them also as kind of a ".htpasswd" file format (like "nginx" also does) which can be provided via volume mount.

boaks commented 1 year ago

I would probably want to avoid adding a separate persistence for credentials and go into the direction of providing them also as kind of a ".htpasswd" file format (like "nginx" also does) which can be provided via volume mount.

Yes. Therefore I implemented the MultiPskFileStore. It offers automatic reloading from files, but you may also provide a InputStream. The secrets maybe encrypted, if wanted.

thjaeckle commented 1 year ago

I managed to add a first rough implementation in #1588 Still a lot of TODOs open, but conceptually this works really really well ..

boaks commented 1 year ago

Happy to read that.

Do you need any help for tests with coap-clients? Or you OK with it?

thjaeckle commented 1 year ago

Happy to read that.

Do you need any help for tests with coap-clients? Or you OK with it?

I think I will manage, thanks. 👍

Do you from the top of head know what a server has to respond when a client sends an "observe cancel"? The resource it asked for, or a "valid" response code? Couldn't find that specified.. 😕

boaks commented 1 year ago

A cancel observe is a GET with Option OBSERVE 1. AFAIK, that is a simple response (resource) with CONTENT (2.05) and no Option OBSERVE.

RFC7641

In this case, a client MAY explicitly deregister by issuing a GET request that has the Token field set to the token of the observation to be cancelled and includes an Observe Option with the value set to 1 (deregister). All other options MUST be identical to those in the registration request except for the set of ETag Options. When the server receives such a request, it will remove any matching entry from the list of observers and process the GET request as usual.

thjaeckle commented 1 year ago

When the server receives such a request, it will remove any matching entry from the list of observers and process the GET request as usual.

Ah, there it is, yes .. thanks :)

n-deliyski commented 1 year ago

Hi, I came here as this issue looks like a replacement or an alternative of Hono CoAP adapter. In general it will be convenient to have direct device endpoint without broker in Ditto. Looking what the connectivity service gives somehow following two functionalities are missing when the gateway service is used:

The issue title is already noting that those points are out of the scope. Anyway, have you thought about that?

thjaeckle commented 1 year ago

Hi @n-deliyski I wouldn't say that it is a replacement for Hono's CoAP adapter. Hono is payload agnostic and does not provide entities which could be interacted with using CoAP. Ditto however has those, and can offer direct APIs for devices supporting CoAP.

It can really be seen as an equal alternative to Ditto's Http Api, but with focus for devices instead of eg Web or Mobile Applications.

I would therefore not see that the payload mapping and other connectivity features of Ditto is required. The devices must not even speak Ditto Protocol with the proposed CoAP APIs, but can make use of partial request and partial updates, eg. only updating a scalar property value.

Devices can also receive events and even messages, using CoAP observe which provides equal functionality as the SSE API of Ditto Http.

However, I am no longer working on this functionality, as my employer is not interested in CoAP.

n-deliyski commented 1 year ago

I see, @thjaeckle thanks for your reply.