@madpilot makes

Garage Door Opener – So, the ESP8266 does support TLSv1.2

I was digging around the ESP8266 github page, as there was an announcement that it now supports CA verification (it ALMOST does – there was a regression bug that means it’s still not working), but I noticed that the last release (2.3.0 at time of writing) is actually quite old – it was released in June.

I was going through recent commit messages, and noticed there had been quite a bit of work around integrating axTLS, which is a tiny TLS library specifically designed for computers with small memory footprints (AKA microcontrollers).

This piqued my interest.

It has been said that the ESP8266 doesn’t support TLSv1.2, because of a buggy implementation in the firmware, which I had verified earlier in testing and hence configuring MQTT to force TLSv1.1.

By including axTLS as part of the Arduino library, and not relying on the ESP8266 API, I wondered if TLS1.2 was now supported.

Turns out it is!

It does mean we’ll have to use a “unstable” version of ESP8266 library. This means other stuff might not work, we need to install it using git, and it’s up to us to keep everything up to day.

Thankfully, it isn’t that difficult to install the library using git.

First, remove the existing ESP8266 library using the board manager – find it using the search function, then hit remove.

Now, you can manually install – Instructions are here. Don’t forget to re-select the right board and to set the CPU frequency to 160Mhz!

Now, that regression bug. It stops client certificates being sent for verification, which is bad for us, so we’ll need to roll back a couple of commits. To do that:

cd ~/Arduino/hardware/esp8266com/esp8266
git checkout -b pre-axtls-2 d6e38f0abd2e1bf796a32e8b1a24d37fdc7daaf8

This creates a new branch based on an older revision that seems to still work ok.

Modify mosquitto to use TLSv1.2

This one’s pretty easy: Remove the tls_version line

#tls_version tlsv1.1

Then remove

"--tls-version", "tlsv1.1", 
from both mosquitto-client/Dockerfile.pub and mosquitto-client/Dockerfile.sub.

Run

docker-compose build
and start the server with
docker-compose up

You should see “Hello world” in there somewhere. Next up, the Arduino side of things…

Garage Door Opener – Adding TLS

While having a username and password is better than an open MQTT server, the credentials are still being sent in plain text, so anyone with a packet sniffer could intercept them and then gain access to our server.

To avoid this, let’s setup TLS (commonly, but maybe not correctly known as SSL) to encrypt the connection and kept the credentials secret.

TLS 101

TLS provides a chain of trust, allowing two computers that know nothing about each other to trust each other.

Let’s look at how this works in the context of a browser hitting a secure website, as this is an example we should all be familiar with.

At a really high level, both your computer and the server have a Certificate Authority (CA) that they both implicitly trust (there is actually more than one, but let’s keep it simple). When you hit a website that is secured the server sends it’s certificate to the browser, which does some complicated maths and verifies the certificate against the CA – if the numbers work out, your browser can be confident that the certificate is legitimate and that the secure site is who it says it is.

Web servers generally don’t care who the client is, so they rarely ask for a client certificate, but in other applications (like this garage door opener) the server will also verify the client against it’s CAs.

After both computers are happy they are talking to the right people, they will exchange a set of symmetric keys that both computers will use to communicate. Symmetric keys are much faster to encrypt and decrypt that asymmetric keys, but it requires both sides to have the same key.

To do that securely, a symmetric key is generated, encrypted using the other computer’s public key, sent securely across the wire and then decrypted – now both computers have the key and they can start talking.

Setting up our own trust network

Enough theory! The first thing to do is generate a CA. If you have OpenSSL installed on your computer (Linux and OSX users more than likely will), you can generate a CA on the command line:

openssl req -new -x509 -days 3650 -extensions v3_ca -keyout ca.key.pem -out ca.crt.pem

You’ll be asked for a PEM password – make this something difficult to guess and then store it in your password manager.

This is literally the key to the city. If someone gets hold of your ca.crt.pem and ca.key.pem, and can guess your password, then they can generate their own valid certificates – not good. Fill in the details as you are asked – these details aren’t super important as this is a personal CA, but make the values something you will recognise. The common name can be whatever you want. Now that you have your CA certificate (ca.crt.pem) and your CA.key (ca.key.pem) you can generate some certificates.

An aside: If you have a password manager that supports notes, I’d recommend saving these files in the there and deleting them from your file system after you have generated any certificates that you need.
Generate a server certificate

For this to work, it’s best to use actual domain names for servers and clients – the easiest way to do this is setting up mDNS, via avahi (linux) or bonjour (OSX) – Windows probably has something too – let’s assume that the name of the computer running MQTT is mqtt.local and the name of the garage door opener will be garage.local.

Generate a server certificate

For this to work, it’s best to use actual domain names for servers and clients – the easiest way to do this is setting up mDNS, via avahi (linux) or bonjour (OSX) – Windows probably has something too – let’s assume that the name of the computer running MQTT is mqtt.local and the name of the garage door opener will be garage.local.

# Generate a key
openssl genrsa -out mqtt.local.key.pem 2048
# Create a Certificate signing request
openssl req -out mqtt.local.csr.pem -key mqtt.local.key.pem -new

Again, fill in the details as you are asked.

The MOST important one is Common Name. IT MUST MATCH THE DOMAIN. So in our case: mqtt.local

# Sign the certificate
openssl x509 -req -in mqtt.local.csr.pem -CA ca.crt.pem -CAkey ca.key.pem -CAcreateserial -out mqtt.local.crt.pem -days 365

You will be asked for the password you entered when you created your CA.

Note that the certificate is valid for 365 days – you’ll have to generate a new one in a years time. You can decide if you want to make it longer or shorter.

Generate the client certificate

Generating a client certificate looks a lot like the generation of a server certificate – just with different filenames.

# Generate a key
openssl genrsa -out garage.local.key.pem 2048
# Create a Certificate signing request
openssl req -out garage.local.csr.pem -key garage.local.key.pem -new

The common name this time should be garage.local

# Sign the certificate
openssl x509 -req -in garage.local.csr.pem -CA ca.crt.pem -CAkey ca.key.pem -CAcreateserial -out garage.local.crt.pem -days 365

Setting up Mosquitto

Now we have all of the certificates that we need, let’s setup mosquitto to use it.

Create a ca_certificates and certs directory in the mosquitto/etc docker folder

mkdir mosquitto/etc/ca_certificates
mkdir mosquitto/etc/certs

and copy ca.crt.pem to the ca_certificates folder and mqtt.local.crt.pem and mqtt.local.key.pem to the certs folder. Finally update the etc/mosquitto.conf file to look something like this:

persistence true
persistence_location /var/lib/mosquitto/
password_file /etc/mosquitto/passwd
allow_anonymous false
cafile /etc/mosquitto/ca_certificates/ca.crt.pem
certfile /etc/mosquitto/certs/mqtt.local.crt.pem
keyfile /etc/mosquitto/certs/mqtt.local.key.pem
port 8883
tls_version tlsv1.1
include_dir /etc/mosquitto/conf.d

Notice we have changed the port to 8883, which is the standard port for secure MQTT. We are also still authenticating via username and password.

We are explicitly forcing TLS version 1.1 because the ESP8266 implementation of 1.2 is buggy, and will fail.

Save the file, and run

docker-compose build mosquitto

Because we aren’t verifying the certificates, we can use the garage.local certificate on the command line to test everything out.

Create the ca_certificates and certs directories, this time in mosquitto-client

mkdir mosquitto-client/ca_certificates
mkdir mosquitto-client/certs

Copy ca.crt.pem to the ca_certifications directory and both the garage.local.crt.pem and garage.local.key.pem files to the certs directory.

Now, modify the Dockerfile.sub ENTRYPOINT to look like this:

ENTRYPOINT [ "mosquitto_sub", "-h", "mosquitto", "-p", "8883", "--tls-version", "tlsv1.1", "--cafile", "/ca_certificates/ca.crt.pem", "--insecure", "--cert", "/certs/garage.local.crt.pem", "--key", "/certs/garage.local.key.pem" ]

and the Dockerfile.pub ENTRYPOINT to look like:

ENTRYPOINT [ "mosquitto_pub", "-h", "mosquitto", "-p", "8883", "--tls-version", "tlsv1.1", "--cafile", "/ca_certificates/ca.crt.pem", "--insecure", "--cert", "/certs/garage.local.crt.pem", "--key", "/certs/garage.local.key.pem" ]

Finally, run

docker-compose build

and

docker-compose up

And everything should connect again, but this time securely!
All of these instructions gratuitously stolen from: https://mosquitto.org/man/mosquitto-tls-7.html

Garage Door Opener – Using a username and password for MQTT

Now we have the ESP8266 talking to the MQTT broker, let’s have a look at adding some authentication. The simplest form of authentication is a username and password, which Mosquitto supports. It uses a password file that has a list of all the usernames allowed to login as well as hashes of their passwords.

We will need to create a configuration file to tell mosquitto where to find that file – create an etc directory in the mosquitto directory that is in the root of the docker project:

mkdir mosquitto/etc

create a file called passwd and enter the following:

password_file /etc/mosquitto/passwd
allow_anonymous false

We are simply telling Mosquitto to

  • look for the passwd file in /etc/mosquitto, and
  • we only want to allow users that have authenticated.

Next, let’s use docker to run the mosquitto_passwd command

docker-compose mosquitto run /bin/bash -c "touch /tmp/passwd && mosquitto_passwd -b /tmp/passwd [username] [password] && cat /tmp/passwd && rm /tmp/passwd" > mosquitto/etc/passwd

Let’s break that monstrosity down.

Since we haven’t set up a docker volume, we can’t easily fetch the file from the container, so instead we run bash and pass it a mini script that:

  1. creates a temporary passwd file,
  2. runs mosquitto_passwd (Don’t forget to change [username] and [password] to real values),
  3. prints out the file, then
  4. deletes the temporary file.
  5. Finally, (back on the host computer) we pipe the result in to a file mosquotto/etc/passwd.

If that worked, you should see a new file called passwd in the mosquitto/etc folder.

Next, we need to tell docker to copy our new config file and password file into the container when it builds. Open mosquitto/Dockerfile and add the following lines before the EXPOSE command

COPY ./etc/mosquitto.conf /etc/mosquitto/mosquitto.conf
COPY ./etc/passwd /etc/mosquitto/passwd

We should also update the client containers so they can login when we bring docker-compose up

The ENTRYPOINT line should like this for mosquitto-client/Dockerfile.pub

ENTRYPOINT [ "mosquitto_pub", "-h", "mosquitto", "-u", "[username]", "-P", "[password]" ]

and for mosquitto-client/Dockerfile.sub

ENTRYPOINT [ "mosquitto_sub", "-h", "mosquitto", "-u", "[username]", "-P", "[password]" ]

Those are capital Ps – and don’t forget to update [username] and [password] to match the values you saved in the passwd file!

Rebuild, and run docker, and you should see the two clients successfully connect, and “Hello World” being printed to the console.

docker-compose build
docker-compose run

Setup Ardiuno

This is a fairly easy modification. Find the line that looks like:

if(!pubSubClient.connect(MQTT_NAME)) {

and change it to

if(!pubSubClient.connect(MQTT_NAME, "[username]", "[password]")) {

Push the code to the ESP8266, run it, and you should see it connect just like it did before! Except this time, it will be using a username and password.You can verify that by using an incorrect username or password – the ESP8266 will never be able to connect.

Garage Door Opener – How can I make this garage door opener secure?

One of the design goals for this project was a secure system – this device can open my garage, and I don’t really want just any person to be able to do that!

Disclaimer: I’m not a security expert! If you find holes in my logic, please let me know!

The most obvious way to interface with the controller was to setup some sort of server that a controller (eg an iPhone app) talks to. The problem with this is that an open network port is an attack vector – if a good guy can connect to a network port, so can a bad guy. While I could set up a username or password, having a port open potentially means buffer overflows, and since I’m not really a C/C++ guy this a big problem for me – I mean a lot of really smart people avoid writing servers in C…

After some research, I found this paper (PDF) talking about the use of the publish/subscriber pattern (AKA pub/sub), where a device connects to as server and subscribes to event notifications. This means there is no port open on the device, so even if an attacker found it’s IP address, there is no way for them to connect to it. It seems MQTT is the IOT pub/sub system of choice (it drives a lot of public IOT platforms).

This of course is not without other issues (not exhaustive – please suggest more if you can think of others):

  1. Problem: If an attacker got on the network, they could just connect to the MQTT server and send a “open” command.

    Solution: Access control lists (ACL): Only allow certain MQTT clients send certain commands. We would need some way for the server to verify who the client is…

  2. Problem: What if an attacker managed to spoof the IP or MAC address (perhaps through ARP poisoning) they could become a legitimate MQTT server, allowing them to send an open command.

    Solution: Verify the identity of the server. TLS certificates can help with that. Using a certificate signed by a custom Certificate Authority (CA) means we can guarantee the server is who it says it is. This also fixes the client verification problem from point 1.

After a quick search, I found the mosquitto project – an open source MQTT server which looked pretty easy to setup, supports ACLs and TLS.

Docker all the things

I decided to set it up a test environment using docker, as it makes dependency management much easier, and also makes the setup scriptable, so when it comes time to deploy to “production” I know exactly what I need to do.

You can clone my git repository to get started (I’ve tagged each of the steps so you can play at home – we’ll start with mqtt-1):

git clone https://github.com/madpilot/home-automation
cd home-automation
git checkout -b mqtt-1 mqtt-1
docker-compose build
docker-compose up

Congratulations! You now have a working MQTT server running on port 1883. Because it hasn’t configured it yet, the server has no authentication or authorisation – that’s fine for the moment – let’s get the baseline working, then we can add the security layers later.The easiest way to test the server is to use the mosquitto command line client. I’ve included these as part of the docker-compose cluster – in fact, when you ran docker-compose, a subscriber was automatically created (subscribed to the “test” channel) and publisher publishes the first message.

Look through the output on the screen. If you see:

mosquitto-subscriber_1  | Hello World

Everything has worked!You can try it out yourself – run the following commandm*:

docker-compose run mosquitto-publisher -t "test" -m "Hello again"

And you should see

mosquitto-subscriber_1  | Hello again

* I’ve been a bit tricky by using a docker entry point to hide away the full command to make sending messages much easier. The full command that gets called is

mosquitto_pub -h mosquitto -t test -m "Hello again"

Which tells mosquitto_pub to connect to the server with a host name of mosquitto (Which is set by docker), publish to the “test” channel, and send the message “Hello again”.Now that we have a working MQTT server and a way of testing things, we can start looking at the Arduino side of things.

Garage Door Opener – Setting up the Arduino IDE for the ESP8266

Thankfully, setting up the Arduino IDE to program the ESP8266 this bit was way easier than I thought – the team behind the Arduino library have done a pretty awesome job. I’m not going to duplicate the steps here – the Github page does a great job of explaining it.

Now you should be able to pick the “Generic ESP8266 Module” from the board list. I had to make a guess at the settings, as I had no idea what type I had – the defaults were fine in my case.

Don’t forget to select the right port after you have plugged in the FTDI cable (Tools > Port) – on my Ubuntu machine it switches between /dev/ttyUSB0 and /dev/ttyUSB1 every time I plugged it in, so if you can’t upload code – check that the port hasn’t changed.

Garage Door Opener – Programming the ESP8266

I has an ESP8266-01 kicking around my workshop just waiting for a project, so the garage door opener was a perfect choice – it only needed one output, and two inputs.

The first problem I had though, was I needed to program it. I read that you can use a 3.3V FTDI cable, which I already had – sort of. It’s will interface at 3.3V, but the VCC is 5V.

My first attempt was just using a simple resistor divider, which didn’t work – I’m guessing the load caused to much of a voltage drop.

So I grabbed a LM317T, a couple of switches, and some resistors to build a small interface board. This is based on a number of different circuits I found searching the internet.

ESP8266 Programmer scematic

It’s super simple. I feed in 5V to VCC, and the LM317T outputs 3.3V. The resistors values were calculated using the look up table here.

The two 3k3 resistors ensure that GPIO0 and RST (Reset) pins are held HIGH during boot up (which is required for normal operation).

To program the device, (and you’ll quickly get used to this little dance) you push the RESET, then the PROGRAM button, then release the RESET button first, followed by the PROGRAM button. This ensures that the GPIO0 pin is held LOW during the device boot sequence, putting us in programmer mode!

If all goes well you should see a constant red LED, and a flickering blue LED when you are talking to the device over FTDI.

Here is an artistic picture of my cobbled-together breadboard (it’s the blurry bit down the bottom):

My Ghetto ESP8266 programmer

Previous