In our home automation series so far we've built our smart home using openHAB
and we've applied enterprise security practices to it; but we have a problem.
The problem is the myopenHAB cloud service, which is an incredibly handy
method of linking our home into services like Amazon Echo and the Google voice assistant.
It's great, but not for its security, and using it backdoors all of the work we've done so far;
but you and I? We're not so easily defeated. So in this video we're going to fix the cloud!
Welcome back to The Pro Tech Show. This is the third in a series of videos about building a smart home
using openHAB. The previous two are linked in the description so I recommend you watch those if
you're new, but here's a quick recap on where we are. We've built our home automation server using
openHAB as the platform. Actually, we built two of them - one on Linux and one on Windows just so you
can see both; but we only actually need one of them. Then in front of openHAB we put a Kemp MoadMaster.
Preferably, this would be in a DMZ; but for the sake of simplicity we didn't actually do that.
The Kemp LoadMaster handles encryption, it adds multi-factor authentication using a username and
password plus a mobile app prompt, and it uses a web application firewall to inspect network
traffic for attacks. Any access to openHAB from the public internet is routed through the Kemp LoadMaster
so that nobody gets access unless we say so. If this seems like a lot of effort, there is a
much easier way: using the myopenHAB cloud service. That's not "my openHAB". It's "myopenHAB", all one word.
This is free and just works without requiring any real knowledge on your part. As I explained in
the last video I don't feel comfortable using it because it uses weak single factor authentication.
A password is not good enough these days. So we built all of this and took control of our own
security, but then we hit a snag. See, one of the really nice things about openHAB is just how
easily it integrates with the Google Assistant and Amazon Echo. Other self-hosted home automation
platforms require you to sign up for developer accounts and build your own custom integrations.
That, or subscribe to a paid cloud service. myopenHAB basically does the latter, but it's
completely free. Unfortunately the myopenHAB cloud service has no real concept of the principle of
least privilege. It requires you to fully trust it with unrestricted access to your smart home.
That's not OK. From a security standpoint it's saying that in order to operate a
light switch it needs access to my heating controls and the video feeds from my cameras.
No! No you don't! This is because the openHAB cloud service that myopenHAB is based on
has just one toggle to enable just notifications or full remote control, and that's it. If you didn't
see the previous video this might sound like I'm just having a grumble about myopenHAB
but the reality is I have no grounds to complain. I don't pay for it. It's free, and it's not one
of those services that's free because you're the product. If I were a professional software
developer the right thing to do at this point would be to start writing the code
to provide the access control I wanted it to have, and contribute that back to the project.
I'm not a developer, and security on a publicly available service probably isn't the best place
to learn; but I do know network infrastructure and we've already built our own security layer.
The interesting thing is that we can actually use this security layer to filter the traffic from the
myopenHAB cloud service and force it to obey our rules of least privilege. So I'm going to show you
how to take advantage of the service provided by myopenHAB, but in a way that keeps you in
control of what it gets access to. That's going to be my contribution. To understand how we're
going to do this we need to know how the openHAB cloud works. In fact, let's quickly enable it. On
your openHAB server go to settings, add-ons, other add-ons, and install the openHAB cloud connector.
Go back to settings and you'll now see openHAB Cloud under Other Services. You should be able to
leave everything at default, but make sure the mode is set to "Notifications & Remote Access". Now go
to myopenhab.org and create an account. You'll need a UUID and a secret. You'll find these in files on
your openHAB server. They'll either be under your userdata folder or /var/lib/openhab, depending
on how you installed it. The UUID is in the root of that folder, and the secret is in a subfolder
called openhab cloud. Now if you click on the link to access your dashboard you'll have remote
access to your openHAB server from the cloud. This is incredibly easy compared to the way we did it, but
it's also a bit scary because there's no way to enable multi-factor authentication and there's
no way to disable this full remote access if we just want to use the Google Assistant or Amazon Echo.
But how did it even work? Unlike the remote access we've built, myopenHAB doesn't require any DNS
records, or any firewall ports to be opened. This is because it does some clever tunnelling. When you use
any website, the conversation between your computer and the website consists of requests and responses.
You send a request asking for a page and the website returns a response containing the page.
This is how it works with our remote access solution. The remote user sends to a request,
Kemp verifies their identity and checks it for threats before passing it to openHAB.
Then openHAB sends back a response which Kemp passes to the original client. When you enable the
openHAB cloud option, your openHAB Cloud Connector makes an outbound connection to myopenHAB. This is
why you don't need to worry about DNS or firewall ports - because you're making the request and the
firewall stuff is happening at the other end. But what requests are you sending when it's a remote
user wanting access to your network? Basically, your openHAB server sends a request saying "What
do you want?" If there is a remote user connecting to myopenHAB they'll also send a request to myopenHAB
asking it for something. Perhaps they want to know the temperature in the living room.
myopenHAB takes the remote user's request, wraps it up inside a response, and sends
that response as an answer to the request from your Cloud Connector that asked "What do you want?"
The Cloud Connector unwraps the response, gets the remote user's request, and passes it to openHAB.
At this point openHAB behaves normally. It receives a request asking for the temperature in the living
room, so it replies with a response that contains the temperature in the living room.
The Cloud Connector receives that response, wraps it up inside a request, and sends it to myopenHAB
saying "I have a request. The living room is 20 degrees. What do you want?" myopenHAB unwraps
the request, sends the response to the end-user, and you're back at the beginning. It's quite clever. The
openHAB Cloud Connector turns the normal request- response cycle upside down by putting requests
inside responses and vice versa. This means you don't have to worry about firewalls and DNS;
but also means that all of these connections have a backdoor into your network that bypasses the
Kemp security layer. These requests are getting transported through the openHAB Cloud Connector
unchecked, and when they pop out the other end they're inside your openHAB server and they're
completely trusted. We have to do something about this so we're going to hijack the cloud requests
as they come through the tunnel. The point at which we want to spring our trap is right
after they exit the tunnel, here. As soon as the original request pops out we're going to kidnap it.
Let's take a closer look at how the openHAB Cloud Connector forwards the external quests to openHAB.
It is hard-coded to use the address http://localhost on whatever HTTP network port openHAB is using. As
I'm sure many of you will know the name of "localhost" resolves to the IP address 127.0.0.1.
This is the loopback address that always refers to the local computer.
127.0.0.1 on your computer is your computer. 127.00.1 on my computer is my computer.
Every computer has this address but they'll also have a normal IP address to use on the network.
Because every computer thinks 127.0.0.1 belongs to them you can't use it to talk to anybody else. By
default openHAB will listen for connections on all IP addresses the computer has, including
this loopback address; but we can change that. We need to edit the file org.ops4j.pax.web.cfg.
Have a look for the line org.ops4j.pax.web.listening.addresses.
By default this will list the IP address 0.0.0.0. That's not a real usable IP address. It's used to
mean "Listen on all IP addresses." Delete the hash sign at the start (that's pound for the Americans),
change it to the specific IP address of your openHAB server, and restart the openHAB service.
This means openHAB will continue listening on that address and will be accessible on the network; but
it will not be listening on the loopback address anymore. When the openHAB Cloud Connector forwards
the external requests to localhost, nothing will be listening for it. That's step one. Step two is we're
going to take openHAB's place on localhost. The way we do this depends on our operating system. Windows
is the easier one here because we just need to run a command. Open a command prompt, running
as administrator, and type the following: "netsh interface portproxy add v4to4 listenaddress=127.0.0.1
listenport=8080 connectport=8080 connectaddress="
and then a spare IP address on your network. If you changed openHAB's default HTTP port then substitute
your port number in for 8080 on the listport parameter. What we've done is we've told Windows to
listen on the localhost port previously occupied by openHAB and forward any traffic it receives to
another IP address on the network. That IP address doesn't exist yet, but first let's repeat this on
Linux I'm going to do it differently this time. Linux can forward ports but it starts getting
complicated quickly, especially when you're trying to mix the loopback address with remote addresses;
and because Linux is much more diverse than Windows the instructions are going to vary
between different computers. Instead I'm going to install HAProxy and let it take care of things.
I do realise that I am now load balancing a load balancer, and you could alternately install
a local instance of Nginx and configure that to get most of the same effect. Deel free to take the
principle and do that if you prefer. I'm doing it this way because we've already got security
filtering set up for openHAB in Kemp and this keeps it all in one place rather than managing
layer 7 HTTP inspection in multiple locations. Install HAProxy. For me that's "dnf install
haproxy", then edit the configuration file which for me is a /etc/haproxy/haproxy.cfg.
In the default section just delete everything before the "timeout queue". We don't want to do any
layer 7 stuff here - only forward the traffic to Kemp. If you're familiar with the options here then tweak
them as you like. Scroll down to the bottom and we're going to delete all of this frontend and
backend stuff, and replace it with our own. Create a frontend section with a name. I'm calling it
"openHAB_Cloud". We'll tell it to bind to 127.0.0.1 using the openHAB HTTP port that is now free,
and we'll tell it to use a default backend called "Kemp". Now we need to create a backend section for
Kemp we'll tell it to use round-robin load balancing (which doesn't really matter as we've
only got one target for it anyway). Add a server called "Kemp_VS" (or whatever you want to name it)
and point it to the spare IP address on your network using port 8080. Save the file, use
"systemctl enable haproxy" to set the service to start automatically, and "systemctl start haproxy"
to start it running now. HAProxy will now catch requests coming from the openHAB Cloud Connector
and fling them out onto the network. Before we deal with this traffic you might need to adjust the
firewall on your openHAB server. If you've followed along with the series so far you'll have locked it
down to just HTTPS traffic, and for this we need it to accept HTTP. On windows if you've already got a
rule to allow HTTPS, go back to "Windows Defender Firewall with Advanced Security" from your Start
menu, find the rule you made previously, and on the "Protocols and Ports" tab where it should say
local port 8443, add a comma and 8080; assuming you used the default port. On our Linux server it's
"firewall-cmd --add-port 8080/tcp --permanent", and then "firewall-cmd --reload" to apply it.
Again, if you changed the port number use it instead of 8080. Right, now
we've hijacked the cloud traffic on either Windows or Linux, we're going to hop over to
our Kemp LoadMaster to make it play by our rules. If at this point you're wondering what
the heck this Kemp thing is, watch the previous video to see what it is and how we deployed it.
In here we're going to add a new virtual service using the spare IP address we just forwarded our
traffic to. This was the "connectaddress" in Windows or the "backend" server IP address in HAProxy.
The port we used was 8080. This was the "connectport" in Windows or the "backend" server port in HAProxy.
I'll call the service "openHAB Cloud". Under Real Servers set the check method to the HTTP protocol.
Click "Add New" and enter the openHAB server's IP address as the real server address, with port 8080
or whatever you've changed openHAB 's HTTP port to. At this point all we've done is create a big loop.
Requests come from the myopenHAB cloud service, pop out of the Cloud Connector, and get sent to localhost
8080; at which point we catch it, and we throw it to Kemp, and Kemp sends it back to openHAB on its
normal IP address. What's the point of that? The point is that it's now going through Kemp, so we
can start applying some security to it. Remember the web application firewall from our last video?
You can turn that on and scan the cloud traffic for threats. One thing you might want to do while
you're in here is go to Advanced Properties and click the Access Control button. If you add your
openHAB server to the allowed addresses that means nothing else can access the service other
than the openHAB server. If you lock port 8080 on the openHAB server's firewall down to Kemp that's
a little bit more secure by forcing everything else in your network to use the encrypted port.
That's by the by. The real reason for creating this loop is content switching. This lets us use rules.
Rules that the myopenHAB service must now obey. For this we head to Rules and Checking in the menu.
Then Content Rules. Click "Create New". The first rule is a special case and I'll explain why later.
Call it "All_Devices". You can't have spaces in the name but you can have underscores.
For the Match String you will need to type... OK, the first character is either called a caret, or
a circumflex, or the hat symbol; depending on your particular allegiance. I'm going to say "hat" because
it's shortest, but for the avoidance of doubt it's this symbol and on my keyboard it's above the
number six. So "^/rest/items/". Tick Ignore Case just in case
and click Create Rule. Now we're going to duplicate that rule a few times. Click the Duplicate button
and we're going to pop over to openHAB and find the items we want to allow access to from
our voice assistant. In openHAB terms it's the item object we want. If using the semantic model
this will be the item that is linked to your point object. In this case if I click through
until I get to the switch for my living room light I'll find the item is called "livingroom_light_switch".
If you've not exposed items through myopenHAB to your voice
assistant before you can do that here as well. In my case I use Google so I go to Add Metadata, then
Google Assistant. I'd select "Light" as the class and then if I click the Show Advanced button
I can give Google some information about the item. I'll tell Google to call it the "Main Light" in the
"Living Room" and save. Back to Kemp, though. The thing we actually need is the name of the item, here.
I'm going to append that to the match string that I duplicated, so my new match string is
"^/rest/items/livingroom_light_switch".
I'll give it the name "Living_Room_Light" and click Duplicate Rule. We'll repeat this for any
item we want Google to have access to. The pattern will be "^/rest/items/" and then the name of the item
from openHAB. Now let's head back to the openHAB Cloud virtual service and scroll down to the real server.
We're going to delete it as I only added it there as a demonstration. Then instead of clicking Add
New we'll click Add SubVS. A SubVS is a sub virtual service. This is like a virtual service inception,
where one virtual service directs traffic to another virtual service instead of our openHAB
server. Click Modify on the new SubVS and we can change its settings. You'll notice this
looks just like the parent virtual service so be careful to keep track of where you are. If you're
confused at this point, we covered the virtual services in the last video; so maybe re-watch
that. We'll give it a name - in this case I'll call it "All Devices" and click Set Nickname, then click Back
to return to the parent virtual service. So this again and create a second sub virtual service.
We'll call this one "Allowed Devices". On this we'll add our openHAB server to the sub virtual
service as a real server. This is just like when I originally added it to the parent virtual service.
We need to do this in the other sub virtual service as well, but to save time we can tick this
box to add it to both at once. Now comes the magic! Expand Advanced Properties on the parent virtual
service and beside Content Switching click the Enable button. You'll notice a new Rules button has
appeared beside each sub virtual service. Click that for the "All Devices" service, select the "All_Devices"
rule from the list and click Add. Then click the Back button to return to the virtual
service. The red warning box is gone. Now do the same for the "Allowed Devices" service. This time
we're going to add all the other rules to this service. You should end up with a table with
each individual openHAB item listed. Do not add the "default" rule or the "All_Devices" rule.
Click Back, and let's talk about what we've done. In fact, let's demonstrate it using the Thunder Client
plugin for VS Code. These rules control what traffic is allowed to the sub virtual service.
Ordinarily the openHAB cloud service has access to every URL on your server; but now we're restricting
it to just the URLs we want it to access. If you try to connect to your dashboard from myopenHAB now it
isn't going to work because we've not allowed it in our rules on Kemp. What we have allowed is
access to specific items using the REST API. This is what Google uses to control your devices. Let me
show you. If I perform a GET request to our openHAB server via Kemp using the URL we explicitly
allowed for the living room light switch, we can see the state is currently "OFF", which matches openHAB.
If I then POST to that same URL and send the word "ON" the light switch switches on an openHAB. If I post
"OFF" it turns off. This is how Google interacts with openHAB so your rules still allow to control these
explicitly listed items, but nothing else. Now what about that "All Devices" service, and why do we need
sub virtual services? Well... we didn't, actually. We could have applied our rules to the real
server directly instead of creating sub virtual services, but well take a look. I don't know if
Amazon works the same because I don't use it, but with Google when you make changes to your devices
you need to tell Google to sync them. When you do this, Google makes a call to the same REST API
but doesn't specify an item name. As you can see that gives it access to every device.
This is necessary for the sync to go through when you've added new devices or removed old ones;
but ordinarily we want to lock it down to just the devices it actually needs to control. That's why
we have two sub virtual services. Because we can put the rule that allows access to everything in a separate
service and hit the Disable button. If I send another request to that URL now, Kemp says "No". If
I need to make changes and resync my devices with Google at some point in the future: all I need to
do is click Enable, tell Google to sync, then click Disable again. And that's it! That's my solution. By
redirecting the traffic from the myopenHAB cloud service through my Kemp LoadMaster I can use these
rules to restrict its permissions to the specific devices I want it to access. No need for full access.
No need for general remote control. No need to open up the web interface. If all I wanted to do is flip
a light switch then that's all it is access to do. If my cloud account gets breached they can
theoretically turn that light on and off, but they don't get any other access to my home. Here
on YouTube of course I am much less restrictive. For example, I have given you full access to the
controls to like the video and subscribe to the channel; and to watch the other videos in this
series. If you're new to openHAB, start with this one; and if you want to know how to build secure
remote access, watch this one. If you've already finished watching both of them then check out
my channel to see what else I do here. Thanks for watching and I'll see you at the next one!