Can the gateway see a webthing that is using ssl?

Hi,

I’m currently using the webthings python framework. I passed ssl_options to the tornado server and have the thing running.

I can connect to the thing via https and wss, but the thing-url-adapter does not see my things when I try to add them to the gateway.

If I do not use ssl I can add my things to the gateway as usual.

Does the thing-url-adapter support webthings using ssl? Looking at the thing-url-adapter code makes me think it does not but I am not sure.

Thanks

Yes, but with a caveat. The problem is that the web things need to have certificates that are trusted by the gateway, which is hard on a home network. You either have to give them certificates generated by an actual CA, e.g. Let’s Encrypt, or you generate self-signed certificates from your own CA, and then put the CA’s public certificate on the gateway for verification.

Alternatively, we could add an option to the adapter to allow self-signed certificates, which obviously has its own set of security issues.

Hi,

I am using self signed certificates. I added the root certificate to the gateway following the instructions here which are:

sudo mkdir /usr/local/share/ca-certificates/extra
sudo cp root.cert.pem /usr/local/share/ca-certificates/extra/root.cert.crt
sudo update-ca-certificates

Is there some other way I need to be doing this?

A few of data points:

  1. The gateway and thing are running on the same raspberry pi. I am not using the Mozilla image. I built the gateway and uploaded the required sources to the pi.

  2. When I try to add the thing by url


    I see this in the log :

    2020-06-17 08:31:07.849 INFO : Opened a new things socket
    2020-06-17 08:31:07.859 INFO : About to call startPairing on ThingURLAdapter
    2020-06-17 08:31:18.047 INFO : thing-url-adapter: Failed to connect to http://raspisentry.local:8888: FetchError: request to http://raspisentry.local:8888/ failed, reason: read ECONNRESET

  3. I can wget and curl to the thing from gateway successfully using https://raspisentry.local:8888

Thanks

Is your code available somewhere so that I can do some local testing?

Also, is your server creating one web thing or multiple? If only one, there should not be a /0 at the end of your URL.

Hi,

I’m using a multiple thing.

Which part of the code would you need? The thing server implements a pca9685 and ads1015 as two web things. Both only use properties. I could try to recreate the issue using one of the examples from the python webthings repo. I can also zip up my stuff and send you a link.

I’d also be glad to do the leg work, but I would need to get up to speed on debugging a gateway plugin.

Also – could this kind of thing be node version sensitive? I built the gateway using 10, but the gateway is running on 12.

Can you try to replicate the issue with the multiple-things example, and show me the changes you made? That would be the easiest.

In the multiple thing example I added code at line 161 and 167. Just added ssl_options and passed them to the webthing server.

The thing shares the certs with the gateway.

The gateway and thing url adapter are un modified.

Here is the thing:

from __future__ import division, print_function
from webthing import (Action, Event, MultipleThings, Property, Thing, Value,
                      WebThingServer)
import logging
import random
import time
import tornado.ioloop
import uuid


class OverheatedEvent(Event):

    def __init__(self, thing, data):
        Event.__init__(self, thing, 'overheated', data=data)


class FadeAction(Action):

    def __init__(self, thing, input_):
        Action.__init__(self, uuid.uuid4().hex, thing, 'fade', input_=input_)

    def perform_action(self):
        time.sleep(self.input['duration'] / 1000)
        self.thing.set_property('brightness', self.input['brightness'])
        self.thing.add_event(OverheatedEvent(self.thing, 102))


class ExampleDimmableLight(Thing):
    """A dimmable light that logs received commands to stdout."""

    def __init__(self):
        Thing.__init__(
            self,
            'urn:dev:ops:my-lamp-1234',
            'My Lamp',
            ['OnOffSwitch', 'Light'],
            'A web connected lamp'
        )

        self.add_property(
            Property(self,
                     'on',
                     Value(True, lambda v: print('On-State is now', v)),
                     metadata={
                         '@type': 'OnOffProperty',
                         'title': 'On/Off',
                         'type': 'boolean',
                         'description': 'Whether the lamp is turned on',
                     }))

        self.add_property(
            Property(self,
                     'brightness',
                     Value(50, lambda v: print('Brightness is now', v)),
                     metadata={
                         '@type': 'BrightnessProperty',
                         'title': 'Brightness',
                         'type': 'integer',
                         'description': 'The level of light from 0-100',
                         'minimum': 0,
                         'maximum': 100,
                         'unit': 'percent',
                     }))

        self.add_available_action(
            'fade',
            {
                'title': 'Fade',
                'description': 'Fade the lamp to a given level',
                'input': {
                    'type': 'object',
                    'required': [
                        'brightness',
                        'duration',
                    ],
                    'properties': {
                        'brightness': {
                            'type': 'integer',
                            'minimum': 0,
                            'maximum': 100,
                            'unit': 'percent',
                        },
                        'duration': {
                            'type': 'integer',
                            'minimum': 1,
                            'unit': 'milliseconds',
                        },
                    },
                },
            },
            FadeAction)

        self.add_available_event(
            'overheated',
            {
                'description':
                'The lamp has exceeded its safe operating temperature',
                'type': 'number',
                'unit': 'degree celsius',
            })


class FakeGpioHumiditySensor(Thing):
    """A humidity sensor which updates its measurement every few seconds."""

    def __init__(self):
        Thing.__init__(
            self,
            'urn:dev:ops:my-humidity-sensor-1234',
            'My Humidity Sensor',
            ['MultiLevelSensor'],
            'A web connected humidity sensor'
        )

        self.level = Value(0.0)
        self.add_property(
            Property(self,
                     'level',
                     self.level,
                     metadata={
                         '@type': 'LevelProperty',
                         'title': 'Humidity',
                         'type': 'number',
                         'description': 'The current humidity in %',
                         'minimum': 0,
                         'maximum': 100,
                         'unit': 'percent',
                         'readOnly': True,
                     }))

        logging.debug('starting the sensor update looping task')
        self.timer = tornado.ioloop.PeriodicCallback(
            self.update_level,
            3000
        )
        self.timer.start()

    def update_level(self):
        new_level = self.read_from_gpio()
        logging.debug('setting new humidity level: %s', new_level)
        self.level.notify_of_external_update(new_level)

    def cancel_update_level_task(self):
        self.timer.stop()

    @staticmethod
    def read_from_gpio():
        """Mimic an actual sensor updating its reading every couple seconds."""
        return abs(70.0 * random.random() * (-0.5 + random.random()))


def run_server():
    # Create a thing that represents a dimmable light
    light = ExampleDimmableLight()

    # Create a thing that represents a humidity sensor
    sensor = FakeGpioHumiditySensor()

    # If adding more than one thing, use MultipleThings() with a name.
    # In the single thing case, the thing's name will be broadcast.
    ssl_options = {
        "certfile": "/home/pi/.mozilla-iot/ssl/certificate.pem",
        "keyfile": "/home/pi/.mozilla-iot/ssl/privatekey.pem"
    }
    server = WebThingServer(MultipleThings([light, sensor],
                                           'LightAndTempDevice'),
                            port=8888, ssl_options=ssl_options)
    try:
        logging.info('starting the server')
        server.start()
    except KeyboardInterrupt:
        logging.debug('canceling the sensor update looping task')
        sensor.cancel_update_level_task()
        logging.info('stopping the server')
        server.stop()
        logging.info('done')


if __name__ == '__main__':
    logging.basicConfig(
        level=10,
        format="%(asctime)s %(filename)s:%(lineno)s %(levelname)s %(message)s"
    )
    run_server()

Ah, so it turns out that Node.js uses its own separate list of CA certificates. You can append to the list by setting the NODE_EXTRA_CA_CERTS environment variable: https://nodejs.org/api/cli.html#cli_node_extra_ca_certs_file

If that fails, and you want to just accept anything, you can also set NODE_TLS_REJECT_UNAUTHORIZED=0: https://nodejs.org/api/cli.html#cli_node_tls_reject_unauthorized_value

Hi,

Setting NODE_EXTRA_CA_CERTS did the trick, mostly. I can add things by url but the gateway does not see them when it is scanning for things.

Looks to be working as expected otherwise.

Never occurred to me that node would have it’s own CA stuff.

Thanks !! I have become reliant on the auto-generating ui the gateway provides.

Let me know if I can do anything else.

Hi again,

I did a quick hack on one of the thing-url-adapter files and was able to get the gateway to scan and see my things with ssl enabled.

In the file thing-url-adapter.js lines 744 and 748 changed http to https :

Of course my things that are not using ssl do not show up while scanning for things.

Yeah, I noticed this issue the other day when I was looking at this. Basically, the mDNS record needs a way to show if it’s using TLS or not.

We could add a tag to the properties to flag it:

Screen Shot 2020-06-18 at 11.17.35 AM

It is only a couple of lines of code in the python implementation in server.py:

Alternately the property could be added only of ssl is enabled. Gonna have to check that it is there anyway.

Then the thing-url-adapter could look for the tag and use the correct protocol.

Already done: https://github.com/mozilla-iot/webthing-python/pull/75

Adapter support is now merged as well: https://github.com/mozilla-iot/thing-url-adapter/pull/93

New versions are available of both. :smiley:

1 Like

Sweet!! Life just got a little better!

Thanks