Secure access to a smart home in the absence of a public IP (part 1)

Introduction


It is difficult to choose a capacious title that reflects the meaning, so I will immediately describe the task that I set for myself.

There is a "smart home". In my case, this is a fanless home server with ioBroker, although this is not important. In addition to home stuff, I want to hook sensors to it from the outside (for example, on an ESP32 from a remote greenhouse). I decided to do this through mqtt. Access to the interface from the Internet.

The usual thing. But there are nuances:

  • The provider has no way to give me a public IP address. And there are no other providers.
  • I do not like binding to specific cloud services. An external service may also close (as gbridge recently sent a notification). And just in case of failure it is not clear what to do. I prefer my own, which can be transferred, redone with little blood if something happens.
  • Safety is important. Not paranoia, but putting ioBroker on the Internet, especially considering that there are several services exhibited (flot ...). No, really.

Further I want to show not immediately the result, but the process. How it went, how Wishlist were transformed, decisions changed. It is possible that some points can be solved more correctly / efficiently (I am not a system administrator, not a developer). Or maybe someone will not go this far and take advantage of an interim solution that, for example, I did not find safe or convenient for myself. Actually, what is described in this part is quite a working option, but for me it’s “intermediate”.

Public address for mqtt


At home, the public IP on the router does not shine (I'm not talking about fixed, this can be solved through dyndns and analogues), namely, the provider gives 10.x.x.x, without options. So, you need to rent a small VPS, and make a probros through it.

The easiest way is to tunnel through ssh. On the home server (I will call it iob.xxx.xx) I execute:

ssh -N -T -R pub.xxx.xx:1883:127.0.0.1:1883 a@iob.xxx.xx 

Connecting to port 1883 of the external server pub.xxx.xx, in reality you find yourself on your home iob: 1883 with the mqtt (mosquito) container running.

Naturally, it is necessary that this starts automatically, the connection is restored after a failure. Therefore, I used autossh, designing it as a service.

/etc/systemd/system/ssh_mqtt.service:

[Unit]
Description=SSH Tunnel mqtt
After=network.target

[Service]
Environment="AUTOSSH_GATETIME=0"
ExecStart=autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -NR pub.xxx.xx:1883:127.0.0.1:1883 -i /home/a/.ssh/id_rsa a@pub.xxx.xx

[Install]
WantedBy=multi-user.target

All sorts of systemctl enable / restart, etc. I will not describe.

Unfortunately, despite autossh, I was haunted by constant freezes. So I decided that there is no need to produce entities and settled on the usual ssh:

/etc/systemd/system/ssh_mqtt.service:

[Unit]
Description=SSH Tunnel mqtt
After=network.target

[Service]
Restart=always
RestartSec=20
User=anri
ExecStart=/bin/ssh -N -T -R pub.xxx.xx:1883:127.0.0.1:1883 -i /home/a/.ssh/id_rsa a@pub.xxx.xx

[Install]
WantedBy=multi-user.target

Subsequently, by the way, it turned out that this provider is so oblique. Well, or the cheapest rate for 45 rubles / month so crookedly works for him. The session hangs periodically, even when it is simply connected via ssh (MobaXterm). So in the end I ordered myself a VPS from another (55 rubles / month), and the problems with freezes disappeared.

By the way, I chose for myself where to take VPS not only on the basis of price, but also taking into account ping (10-20ms).

In general, this option is quite normal, especially considering that subsequently I made an Internet connection exclusively to port 8883 via TLS. Those. The password for mosquitto was transmitted encrypted.

Subsequently made client certificates mandatory. Those. first, at the TLS level, you need to present a client certificate, and then log in with the password name specified in mosquito. Those. it’s not so easy to get to the password search phase.
Accordingly, at first I used the server certificate from LetsEncrypt, then, due to the need for client certificates, I switched to self-signing.

Since in the process I also worked on the software for ESP32, I noticed (or just thought, I don’t remember anymore) that when there are problems with the VPN connection, the battery will be consumed much faster. During normal operation, the cycle: Wake up, apply power to the sensors, connect to WiFi, establish a connection with the mqtt server, read the sensor readings as they are ready, transfer to mqtt, turn off the power from the sensors, go into deep sleep for 10 minutes.

Normally, such a cycle takes about 4 seconds. 1.5-2 seconds - connection to WiFi, an extra second due to the transition to mqtt over TLS. It suits you for 4 seconds, all the same, the sensors need time to wake up. But if the VPN went down (it was clearly visible when autossh fell off), what should I do? Of course, I set up so that after 20 seconds the system would fall asleep anyway. But 20 seconds instead of 4 is very noticeable.

In general, I decided that it is better to keep the mqtt server on an external VPS. Now that everything works like a clock, I'm not sure if this is necessary. But I don’t see any sense in remaking it back.

Public Address for Vis


Vis is a popular visualization system at ioBroker. You could not bother, and configure it yourself on https and likewise just forward the port. Moreover, she can ask for a password at the application level.

But that is not cool. Especially considering that for work it connects additional services. Say, I draw graphs in flot, relatively speaking, I connect to vis.xxx.xx : 8082 / vis / index.html, but inside there are links to vis.xxx.xxx graphs : 8082 / flot / index.html. At some point, it turned out that when connecting to / vis, a password is asked, and the graph interface is accessible without a password.

At some moments it was generally strange - I was authorized on vis, I see the graph, but at the bottom right there is a translucent “No connection to server” window. Rewrote for this css block to hide it. But, as soon as I began to use frame to switch between graphs on the same screen, it turned out that my overlap on the display in the frame did not work. (As it turned out later, it should be so). I turn off authorization - everything is fine, no swearing.

So I decided on the same external server to raise nginx in reverse proxy mode. And already make authorization on it.

From the browser it worked. But the native application iobroker.vis from the Play Market could not log in this way. And I wanted to use it. Although this is actually a browser in the window, but it has a number of nice features. Let's say set the scale (93% in vertical mode), and the image fits. On another device with a different screen resolution, you simply select the coefficient, and that’s it. And in the browser you need to adjust every time ...

Okay, I think. I’ll add a tricky code in the URL instead of the password. Type vis.xxx.xx : 8082 / <long sequence> /vis/index.html. Often such a trick is used.

Almost earned. But with glitches, digging showed that this web application was not written correctly. Many links inside it are not relative, but from the root.

Okay, I found several links, I wrote for them, they say, if the referrer contains such code, still trust, rewrite the URL, etc. But they gradually came to light. So I decided that this is crooked, wrong, and a different approach is needed.

VPN


I decided to sacrifice a little access universality. Let me have access from my laptop, smartphone. But from other people's devices, from the Internet cafe is not necessary, I’ll manage. Then you can put a small client that will install the VPN. And inside it, neither SSL nor authorization is required. And at the same time you do not need to redo the links.

The simplest way for me was Zerotier. For my OS (Windows, Android, Linux) there are clients. And there are even ready-made ones in the docker. Yes. I run everything in docker, about the features of this later.

You install the client, enter a unique code for your network, then confirm it in the Web interface on my.zerotier.com , if necessary, set a static address from your personal private network (a la 10.20.30.0), and that’s it. All connected clients see each other.

The only thing I had to deal with a bit was "how to connect to a remote server from a device in your home WiFi without starting the client." Well, my home server is already a client, even if it routes itself. It turned out that everything is simple. The home network 192.168.x.0 should be registered on my.zerotier.com in the Managed Routes section, specifying as my gateway, of course, this is my home server. Well, in the WiFi network, configure the route accordingly (on the WiFi router, statics 10.20.30.0 on the home server).

When connecting a Zerotier client, you can specify a different DNS server. Those. I connected the client, and the domain name resolves not to a public address, but to a private one, because DNS now points to the home server, where dnsmasq palm off for individual IP records from the Zerotier private network.

Even Zerotier pleased with the effective choice of a route for connection. If I activate a Zerotier client in home WiFi, ping to the home computer (its IP address issued by Zerotier) is the same couple of milliseconds as without a client (just via WiFi). Those. Connecting to the cloud is only at the first moment. Further traffic exchange is carried out directly, not through the cloud. If you install, for example, OpenVPN on VPS, the same traffic ran from the client to VPS, and then back to the same WiFi network to the home server.

In principle, there is even a chip to put your moon servers. Almost in a network cut off from the Internet, all this economy is to be deployed.

What is the result?


ESP32 sends its data to the mqtt server deployed on the VPS. Over TLS, client certificate required.

A VPN through Zerotier is installed with the home server. Sonoff rfBridge communicates with the Tasmota firmware through this home server from mqtt to VPS. There is no way to set TLS with a client certificate, so the usual MQTT is configured for 1883. Anyway, after all, the home server will encrypt this traffic using Zerotier.

Well, I connect to vis from the home network directly, and from the Internet by activating the Zerotier client. You can not turn it off at all, this also works. But only sometimes I need other VPN clients (for example, go to the "ILV-forbidden"). Two VPNs on one smartphone did not immediately become friends, but I did not understand.

Everything is very simple. But the worm swallowed the soul. Although I do not have a nuclear reactor, but all of a sudden? There was a case when they broke TeamViewer (a company, not specifically client software), and through them gained access to many accounts. And in general, I wrote at the very beginning that I love everything mine.
So the next step, I switched from Zerotier to OpenVPN. Everything is in my hands.

The only “alien” is the provider’s VPS. Well, I’m specially launching everything in docker containers to be able to instantly move.
If I knew how much I would have to deal with OpenVPN, maybe I would not. In fairness - the main problems were precisely because of the containers.

Conclusion


In the next article I’ll talk about OpenVPN and the configuration features in my conditions (containers, routing of other devices from the home network). There will be more configs, technical details and difficulties. But immediately the second part did not begin to write without this. It would not be clear why such perversions are necessary at all.

And just in case, a question for those who know: although I have a VPS and a small one (512MB RAM), it is used less than 1%. docker stats:

image

And I got the idea to launch it all just like a container on some Google Cloud Run, Amazon Fargate, or something similar. Deploying a server with all sorts of fail2ban through ansible is not a problem. Install Docker too. But why, if you need only a small fraction of its resources?

However, according to my calculations, the same Fargate would cost me many times more.

Maybe I didn’t understand something? So it would be interesting to have a small container purely for forwarding the port home, and not a whole VPS. There's no such thing?

All Articles