Rachio Smart Sprinkler Controller

That‘s anproblem by design and the only way to get Rachio events. If anyone has an idea…

@markus7017

What is the IP or URL the callback comes from? I’m trying to see if I can create a rule in my firewall that will only allow access based on the originating address.

Squid

I added a logging for source address. This would provide an option for a firewall rule, but also requires that those source IPs stay stable.

Since a few days I have problems to access the cloud service. They did a system update. As a result I couldn’t login to the Web interface anymore. Support directed me to remove/re-install the App, login again with the App and then Web login was working.

However, on the API level I still get HTTP 429/“1700 daily calls rate limit exceeded” on the first call. I’m waiting on support to come back. I stopped the binding yesterday evening, but this doesn’t change the situation.

I have also an answer pending to the security topic, maybe they could provide an idea, but I think this is a general problem of a stateless REST API. Otherwise they need to keep an event channel open, but this takes a lot of resources.

Cheers, Markus

I’m still struggling with any API rejected with error call 429.
The reason: Rachio limits to number of API calls to their cloud to 1.700/day (allows about 1 call/min). For some reason I exceeded that limit and can’t do any test.

Please make sure: Don’t use pollingInterval < 90 sec in the binding config to prevent encountering the same problem.

Rachio support stated that it should be possible to use https for the callback. I can’t try again, because my account is locked… In between I’m talking to a developer, so should make progress soon.

Keep you posted.

Rachio support fixed my account, so I could continue with development.

I integrated support for externalId. This is a unique id passed to the cloud when registering the webhook. When the callback is received the binding validates the id and ihnores tge request when it doen‘t math. In addition the unique deviceId must match one of the devices served by the binding. And you already have an option to clear all registered webhooks acoiding someone is controlling your devices from a different instance (the cloud supports multiple end points for events).

I will also make the externalId a hash rather than a textual id - I think this is a pretty good abuse detection from the application side, combined with the fact that you need the app key/token to access the api.

I added logging for the event source and it seems(!) that inbound requests come from ‘79.236.69.83’ all the time. This information could be used to apply filteribg on th network level. In addition I‘ll add an option to configure a subnet and use this as a filter by the binding.

According Rachio support it should also be possible to use a https callback, but I was not able to verify that (maybe a problem with my setup).

@KidSquid: Do you want to give it a try by configuring a port forwarding on the router + adding a firewall rule on the OH device restricting inbound traffic to 79.236.69.83->local port? In this case I could provide an updated version of the binding (also including some minor enhancements).

@mjwedeking: This version also populates the image url. Are you going to create some kind of dashboard, e.g for HABPanel? That’s what I@m looking for, but my css knowlegde is very limited for a pretty layout :wink:

If your are looking for more info/data: There is a lot more information provided by the API (see way up in tgis thread), which could be populated through channels, but a bigger number of channels confuses the user during setup. Unfortunately there seems no way to access thing properties from a OH rule, otherwise I could populate more or less static information by thung properties.

Another option could be to provide one channel wirh a JSON string including all properties. That‘s what I already added in between for the event channel. You‘ll receive a JSON string containig attributes like timestamp, topic, type, sub-type beside the message itself. So you get a trigger each time an event is received, but also get some additional information without implementing additional channels. The string could be parsed directly in the rule or using the JSON parser and additional properties could be added without new channels / breaking tge existing format.

What do you think?

This is perfect, and makes me feel a bit more comfortable.

I’ll put in a rule to allow access to my oh installation port 8080 from the IP address you provided above.

Let me know when you have a new version of the binding compiled.

Squid

here we are: org.openhab.binding.rachio-2.3.0-SNAPSHOT.jar.zip.pdf (266.0 KB)

You need to remove all things, install the update (keep a backup), wait a minute and rescan. This will provide all channels incl the new event channel.

Add callbackUrl to your thing defintion.

Bridge rachio:cloud:1 [ apikey="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx", callbackUrl="http://mydomain.com:myport/rachio/webhook", clearAllCallbacks=true, pollingInterval=120, defaultRuntime=120  ]
{

}

callbackUrl is the callback URL, replace “mydomain.com” with you dyndns name, and my port with the external port (in my case 50080, should be > 1024). Maybe allow the complete subnet (79.236.69.83/24) in the firewall rule rather than a single ip (79.236.69.83).

You could set clearAllCallbacks to make sure than all registered callbacks will be cleared (I suppose than non is registered with your account). Set this option if you change the callbackUrl - see included README.MD (first draft of the documentation).
one known bug: the event channel receives a JSON string with some event attributes. The included timestamp is nonsense. I’m still missing an answer from Rachio who to convert the integer value to localtime (it’s not a ‘seconds since 1970’…).

Don’t use a polling interval < 90s as discussed above - took me 5 days to re-activate my account :wink:

I got another idea: Use UPnP to open the port when registering the callback (configurable), but this requires some additional research.

@mjwedeking, @KidSquid: any updates?

fyi: I added a new option to the bridge config, which allows to configure a single ip, a ip subnet or a list of subnets for the mentioned IP validation of inbound requests. However, the Eclipse startup task messed up my development environment again and I need to fix that before I’m able to test that change - that’s really nagging.

I had a crazy weekend…will probably be tomorrow before I can spend a few minutes and test.

Squid

I got some answers from Rachio support:

Does the Rachio Cloud has a static ip / an ip subnet / a list of IPs, which could be used to filter incoming requests (use this list of IPs for a firewall rule, making sure that only requests sent by the Rachio cloud will be passed to the smart home gateway)?

Our IPs change all of the time due to the way our infrastructure is set up. The best way to validate that a request is coming from us would be to make sure the cert is valid from Rachio.

Does the cloud uses a certificate, which can be validated in incoming webhook calls?

Yes the cloud uses certificates, your http library should provide support

Is there any information on the JSON format for the webhooks?

Our documentation team is still working on updating our documentation for V3, I will reach out once we get that updated

How to converted the timestamps (e.g. createdDate, eventDate, lastUpdateDate) to a LocalTime format.

All of our times are sent back to clients in UTC. I cannot advise you on the specifics of converting local time. It looks like you're doing a great amount of low level work for time conversions.

This means I should be able to to use https for the event call back and verify the certificate. I’ll keep tze ipFilter functionality - then it’s up to the user if he wants to do the network level check or not.

Well I finally had a few minutes to sit down and install the new version.

Couple of items:

  1. You referenced a README file, there is not one included in the zip file you posted above.

image

  1. I am unclear what I need to do with clearAllCallbacks - can you exlain further please?

  2. I am seeing new PROPERTIES information inside of the Controller THING, does this indicate I am receiving the callbacks correctly?

Squid

Hi,

the doc includes a description on the clearCallbackUrl, I need to upload a new zip, but struggled with the dev environment, because I did a pull to verify with the current codebase and resulted in 900 errors…
For now the clearCallbackUrl to true

  1. No, those are just static information I add to thing properties. If the events are working you should see RachioEvent: xxxx messages in the log and the event channel of the device or zone.

Best regards, Markus

FYI -

I’m getting alot of these in my logs…

2018-03-16 09:16:22.647 [hingStatusInfoChangedEvent] - 'rachio:cloud:1' changed from ONLINE to OFFLINE (COMMUNICATION_ERROR): RachioBridge: Unable to refresh device status: Unable to connect to Rachio Cloud!

try a restart

I published the current code base on GitHub:

Here is the README.MD

https://github.com/markus7017/org.openhab.binding.rachio/blob/master/README.md

Tried rebooting…seeing this in the log…

18-Mar-2018 12:47:02.823 [DEBUG] [openhab.binding.rachio.handler.RachioBridgeHandler] - RachioBridgeHandler: Connecting to Rachio cloud
18-Mar-2018 12:47:03.278 [ERROR] [org.openhab.binding.rachio.internal.api.RachioApi ] - RachioApo: Unable to get personId: Server returned HTTP response code: 429 for URL: https://api.rach.io/1/public/person/info, data=''
18-Mar-2018 12:47:03.278 [ERROR] [org.openhab.binding.rachio.internal.api.RachioApi ] - RachioApi.initialize(): API initialization failed!
18-Mar-2018 12:47:03.278 [ERROR] [openhab.binding.rachio.handler.RachioBridgeHandler] - Unexpected error while communicating with Rachio cloud: Unable to connect to Rachio Cloud!

Not good, this means that your account is locked due to too many API calls (I had the same problem). Contact Rachio support, I think that their counting is buggy. Stop the binding for now - they say that it should reset after 24h.

Hi @KidSquid,

I hope you are doing well and your account could be unlocked.

here is an update on the binding:
Security:

  • IPs of call back events are changing, I put in some code to filter requests on application level and if the serverlet provides the correct IP I see a lot of changing IPs, this was confirmed by support.
  • We could do https for the event interface :slight_smile: However, in this case the port MUST be 443. A different port (https://mydomaind.com:xxxx/rachio/webhook) will be ignored by the Rachio system :frowning:
  • According to Rachio support the https REST call should have a certificate. I hacked something together to check for the cert, but so far I saw nothing. This would be a good option to verify.
  • I implemented a check for the URL path, so https://mydomain.com/rachio/webhook gets process whereas …/test23 will not be processed.
  • I added the externalId to each request and this will be verified on inbound event messages;-)

So I would say, I’m checking everything, which is available to the application level. You can’t create a rule on the firewall, because the IPs are changing (seems to be some AWS based service). Even ping rapi.rach.io provides a different IP than the REST call (I though to do a DNS lookup and use this as filter). There is an option to set IP address(es) or Subnet, but I don’t think that this is reliable.

The API:

  • Rachio changed the API to version 3. This general REST interface is the same. However they changed the complete event format (JSON) AND messed up the JSON encoding like this
"{\"var1\":\"value1\",\"var2\":\"value2\"}"

yes the " and \" chars a part of the string - total nonsense. I have a flexible fix, which removes the chars, so should work with and without. ´Took a while to find out. :-( The also renamed event names and still do not include the JSON format in the public documentation.
  • Various events get processed and display some information. There is a event channel receiving a JSON with some information. This messes up the framing in PaperUI->Control (not handled nicely). So a OH rule could receive and process events incl. timestamp, type, summary, zone names etc.
  • If found more information on the API rateLimit. Each http response and event contains additional header attributes, which reflect the rateLimit, the remaining calls and the timestamp for a reset (seems to be midnight). This can be used to stop polling in case the limit is eaten up. They said the intention is to have 1 call every minute. However, each “logical” command can consume more than 1 call. The current values are shown in the log and I will include a hard limit to stop polling at < 200 available calls until the limits gets reset. This protects the account from being blocked.
  • I implemented the option to read a cfg. This allows to set the apikey there and crate the cloud thing automatically. Still needs some work; callback url and some global settings might also go there. A dynamic cloud thing has in addition the advantage that you can edit it in PaperUI let’s see how that works out.

I published the code on github (https://github.com/markus7017/org.openhab.binding.rachio).
However it needs some refactoring and optimizations (e.g. using gson to parse JSON rather than decoding the JSON myself - works nice).

The binary could be found here: https://github.com/markus7017/org.openhab.binding.rachio/blob/master/target/org.openhab.binding.rachio-2.3.0-SNAPSHOT.jar
README: https://github.com/markus7017/org.openhab.binding.rachio/blob/master/README.md
Next step (after refactoring) is the Eclipse IoT Market and entering the regular beta program.

Please provide feedback on how to proceed.

fyi: I’m still working on version 3 compatibility trying and making the binding more secure.

  • Rachio significantly changed the event’s json format with version 3 of the cloud API. I made necessary changes and working on completing the event parsing and mapping to appropriate channel updates in the binding.

  • I found out that Rachio is operating the complete infrastructure in amazon’s AWS. So fire-walling could be limited to AWS’ IP address ranges. AWS provides the option to retrieve the current list of ip address ranges: https://docs.aws.amazon.com/general/latest/gr/aws-ip-ranges.html. I’ll will add support to the binding. This list could be loaded when the binding initializes, so the application level IP filtering becomes dynamic. Maybe a daily refresh or so is required to prevent necessary OH restarts when amazon adds more address ranges to the list.

  • I found the a document on Rachio Cloud security: https://support.rachio.com/hc/en-us/articles/115010541948-How-secure-is-my-Rachio-controller-

  • After turning on jetty debugging I saw the following error when the event REST calls comes in:

javax.net.ssl.SSLException: Received fatal alert: certificate_unknown

which is an explanaitin why the binding doesn’t sees the incoming request.

  • Connections are initiated from https://mqtt.rach.io by the Cloud platform. I get a warning using this link in Apple’s Safari. The certificate is marked unsecure. Firefox display a error code ‘SSL_ERROR_BAD_CERT_DOMAIN’, which is a clear indicator that something is broken in the certificate chain.

  • Running curl provides a similar result:

curl -IS https://mqtt.rach.io

curl: (60) SSL certificate problem: Invalid certificate chain
More details here: https://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle" of Certificate Authority (CA) public keys (CA certs). If the default bundle file isn't adequate, you can specify an alternate file using the --cacert option.

If this HTTPS server uses a certificate signed by a CA represented in  the bundle, the certificate verification probably failed due to a  problem with the certificate (it might be expired, or the name might  not match the domain name in the URL).

If you'd like to turn off curl's verification of the certificate, use  the -k (or --insecure) option.
  • Using Safari on iPhone I found out that those certificates are signed by a Symatec CA. As we know the Symantec root certs are no longer treated as secure. Google will start on 03/15/2018 (so 2 weeks ago) with removing them from new Chrome version.

  • After adding an exception to Firebox I was immediately able to connect to https://mqtt.rach.io

  • I contacted Rachio support to clarify the problem.


@KidSquid: Do you see the described security pattern sufficient for you?

  • using https with TLS 1.2 for the event interface in general
  • validation of the originator’s IP address (based on up-to-date AWS address ranges)
  • validation of certificate parameters
  • validation of the URI (/rachio/webhook)
  • validation of the externalId (generated by the binding)
  • validation of the deviceId / zoneId
  • with the option to do firewall-based filtering for AWS address ranges connecting to port 443 (with a forward to 8443 on the OH device)

Update on Port Mapping: There is already to pull changes for jupnp from the cling project to integrate the port mapping feature. cling is part of the OH framework. So it will take some time, but on a mid-term I would be able to use the build-in port mapping feature, which means that the binding would be able to create the port forward on it’s own. The last open point would be to verify that the Rachio Cloud also connects using a different port than 443 (I don’t like to user the standard https port. This create conflicts if there is already a port forward active on the router and also exposes that there is a https server listening).