Na IoT mi vadí hlavně to první písmenko - Internet. Nevidím důvod, proč by při budování "chytré domácnosti" měla vnitřní komunikace být závislá na externí internetové službě - a na připojení k Internetu vůbec. Základem pro komunikaci (nejen) IoT komponent je protokol MQTT. Rozhodl jsem si tedy postavit vlastní MQTT server na bázi Orange Pi Zero, miniaturního počítače s ARM.

Co je Orange Pi Zero

Předpokládám že znáte Raspberry Pi, malý a levný počítač s ARM procesorem, na kterém může běžet Linux nebo jiné operační systémy. Řada Orange Pi je něco podobného. Budu používat model Orange Pi Zero, což je ten úplně nejmenší a nejlevnější model, stojí okolo deseti dolarů.

Jeho hardwarové charakteristiky nejsou nijak úchvatné: Procesor Allwinner H2 se čtyřmi jádry ARM Cortex-A7, 256 nebo 512 MB DDR3 SDRAM, jeden USB 2.0 port, jeden MicroUSB pro napájení, RJ45 Ethernet 10/100M, slot na MicroSD kartu a WiFi. Na rozdíl od vyšších modelů (a třeba Raspberry Pi) nemá konektory pro zvukový nebo grafický výstup. Sice jsou k dispozici na zvláštní rozšiřující desce, ale obecně je tento počítač určený spíše pro headless aplikace.

Mám ho tady už několik let, navrhl jsem na něj dokonce i krabičku, vyzkoušel jsem ho, ale pořád jsem nevěděl co s ním. Myslím že pro MQTT server bude zcela ideální.

Co je MQTT?

MQTT (MQ Telemetry Transport) je komunikační protokol, který se používá v IoT sítích a nejen tam. Historicky vznikl v roce 1999 pro potřeby monitorování zařízení na ropovodech v poušti. Dokáže si poradit s pomalým spojením s vysokou latencí (např. satelitním), je to velice jednoduchý protokol přinášející minimální overhead. Právě proto je populární pro IoT.

Existuje mnoho MQTT brokerů (serverů) a klientů. Já budu používat Mosquitto, což je jednoduchý multiplatformní open source broker.

Základní nastavení Armbianu

Kroky v této části nemají nic společného s Mosquittem nebo MQTT. Jde o obecný postup sloužící k nastavení a základnímu zabezpečení v zásadě jakéhokoliv serveru na Linuxu.

Příprava SD karty

Jako operační systém budu používat Armbian, což je ARM klon Debianu. Je udržovaný a desky Orange Pi patří k oficiálně podporovaným. Z webu Armbianu jsem si tedy stáhl aktuální image Armbian Buster.

Image je třeba nahrát na MicroSD kartu o velikosti nejméně 2 GB. K tomu používám utilitu Rufus, ale můžete použít cokoliv pro práci s obrazy disků, populární je třeba Win32DiskImager nebo multiplatformní balenaEtcher.

První boot a přihlášení

První boot nějakou dobu trvá, protože se při něm zvětšují partitions, generují se SSH klíče dělají a další věci, takže buďte trpěliví. Zjistěte, jakou IP adresu přidělil váš DHCP server klientovi. Já jsem mu rovnou nastavil přes DHCP rezervaci a nasměroval na něj DNS jméno, řekněme mqtt.example.com.

Připojte se pomocí příkazu SSH, který je součástí aktuální verze Windows 10 nebo pomocí jiného SSH klienta:

ssh [email protected]

Při prvním připojení budete vyzváni k ověření SSH klíče serveru. Pokud by se v budoucnu změnil, budete varováni - obvykle to věští něco nekalého.

The authenticity of host 'mqtt.example.com (10.7.2.17)' can't be established.
ECDSA key fingerprint is SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
                                
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'mqtt.example.com,10.0.0.1' (ECDSA) to the list of known hosts.

Výchozí heslo uživatele root je 1234 a po prvním přihlášení si ho musíte změnit:

You are required to change your password immediately (administrator enforced)
 ____  ____  _   _____
/  _ \|  _ \(_) |__  /___ _ __ ___
| | | | |_) | |   / // _ \ '__/ _ \
| |_| |  __/| |  / /|  __/ | | (_) |
\____/|_|   |_| /____\___|_|  \___/

Welcome to Armbian buster with Linux 5.4.8-sunxi

System load:   0.00 0.00 0.00   Up time:       1:05 hour
Memory usage:  29 % of 238MB    Zram usage:    17 % of 119Mb    IP:            10.0.0.1
CPU temp:      50°C
Usage of /:    7% of 15G

New to Armbian? Check the documentation first: https://docs.armbian.com
Changing password for root.
Current password: 1234
New password: ********
Retype new password: ********


Thank you for choosing Armbian! Support: www.armbian.com

Zároveň jste vyzváni k vytvoření běžného ne-administrátorského uživatele, což sice není povinné, ale je to dobrý nápad, takže vám to vřele doporučuji:

Creating a new user account. Press <Ctrl-C> to abort

Please provide a username (eg. your forename): altair
Trying to add user altair
Adding user `altair' ...
Adding new group `altair' (1000) ...
Adding new user `altair' (1000) with group `altair' ...
Creating home directory `/home/altair' ...
Copying files from `/etc/skel' ...
New password: ********
Retype new password: ********
passwd: password updated successfully
Changing the user information for altair
Enter the new value, or press ENTER for the default
        Full Name []: Michal Altair Valasek
        Room Number []:
        Work Phone []:
        Home Phone []:
        Other []:
Is the information correct? [Y/n]

Dear Michal Altair Valasek, your account altair has been created and is sudo enabled.
Please use this account for your daily work from now on.

Ukončete SSH session příkazem exit a znovu se přihlašte, tentokrát jako nově vytvořený uživatel (zde altair):

ssh [email protected]

Nastavení přihlášení pomocí klíče

Přihlašování pomocí hesel není ani pohodlné ani bezpečné. Mnohem lepší je vygenerovat si SSH klíče a přihlašovat se pomocí nich. Pokud ještě žádné nemáte, vygenerujte si je (na klientovi, ne na serveru) pomocí příkazu ssh-keygen. Vygenerované klíče najdete v %USERPROFILE%\.ssh a bezodkladně je zálohujte.

Na serveru pomocí následujících příkazů vytvořte soubor ~/.ssh/authorized_keys:

mkdir ~/.ssh
nano ~/.ssh/authorized_keys

Do souboru zkopírujte obsah souboru %USERPROFILE%\.ssh\id_rsa.pub. Měl by vypadat nějak takto:

ssh-rsa xxxxxxxxxx uzivatel@POCITAC

Stiskem Ctrl+S soubor uložte a stiskem Ctrl+X ukončete editor Nano. Následujícími příkazy nastavte práva na tyto soubory tak, abyste se k nim dostali jenom vy:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Příkazem exit se odhlašte a znovu se přihlašte. Pokud jste udělali všechno správně, nebudete tázáni na heslo a budete automaticky přihlášeni pomocí klíče (pokud jste při generování klíče zadali heslo, budete tázáni na toto heslo ke klíči, lokální).

Pozor! Své přihlašovací heslo nezapomínejte! Budete ho moci použít pro přihlášení z konzole (ne po síti) a bude potřeba pro příkaz sudo.

Základní konfigurace

Následujícím příkazem spusťte konfigurační utilitu Armbian Config:

sudo armbian-config

V části System - SSH zakažte přihlášení uživatele root a přihlašování pomocí hesla, povolte jenom přihlášení pomocí klíče:

V části Personal můžete nastavit časovou zónu. Výchozí je UTC, pravděpodobně budete chtít Europe/Prague. OPi Zero nemá zálohované hodiny reálného času a čas si načítá přes NTP ze sítě.

Nejspíš budete chtít změnit i název serveru (hostname) z výchozího orangepizero.

Následujícím příkazem aktualizujte systém:

sudo apt-get update && sudo apt-get upgrade -y

Počítejte s tím, že to bude chvíli trvat, v závislosti na rychlosti sítě, množství aktualizací a rychlosti SD karty.

Nastavení firewallu

Z hlediska bezpečnosti je dobré nastavit firewall tak, aby odmítal všechna spojení zvenčí, s výjimkou výslovně povolených. K tomu použijeme nástroj UFW (Uncomplicated Firewall).

Ten není standardní součástí instalace, takže ho musíme nejprve nainstalovat:

sudo apt-get install ufw -y

Poté povolte OpenSSH a firewall aktivujte. Celé generace správců by mohly vykládat o tom, jak nemoudré je nastavovat pravidla na firewallu po síti, ale nám nic jiného nezbývá. Proto na otázku, zda chcete UFW opravdu povolit, odpovězte y:

sudo ufw allow OpenSSH
sudo ufw enable

Mosquitto

V této části nainstalujeme a zabezpečíme MQTT broker Mosquitto.

Instalace a otestování funkčnosti

Mosquitto nainstalujte pomocí následujícího příkazu:

sudo apt-get install mosquitto mosquitto-clients -y

Následujícím příkazem se přihlašte k odběru topicu test. Příkaz zdánlivě nic neudělá a zůstane "viset". Čeká totiž na příchozí zprávy.

mosquitto_sub -h localhost -t test

Otevřete si druhé SSH spojení na stejný server a v něm zadejte následující příkaz:

mosquitto_pub -h localhost -t test -m "Hello, World!"

Zaslaná zpráva Hello, World! se zobrazí v prvním okně. Tam můžete ukončit mosquitto_sub stiskem Ctrl+C a druhé okno zavřít.

Nastavení autentizace

Nyní je server zcela otevřený, tj. kdokoliv může bez omezení jako anonym posílat a číst jakékoliv zprávy, což nejspíše nechcete. Zkonfigurujeme server tak, aby pro přístup vyžadoval heslo.

Následujícími příkazy vytvoříte konfigurační soubor s uživateli a vytvoříte uživatele developer s heslem pass.word123:

sudo cp /dev/null /etc/mosquitto/passwd
sudo mosquitto_passwd -b /etc/mosquitto/passwd developer pass.word123

Dále vytvořte soubor /etc/mosquitto/conf.d/default.conf a otevřete jej v editoru Nano:

sudo nano /etc/mosquitto/conf.d/default.conf

Zadejte do něj následující obsah, kterým zakážete anonymní připojení a specifikujete, ze kterého souboru se mají brát uživatelé a hesla:

allow_anonymous false
password_file /etc/mosquitto/passwd

Stiskem Ctrl+S soubor uložte a stiskem Ctrl+X ukončete editor Nano. Následujícím příkazem restartujte Mosquitto:

sudo systemctl restart mosquitto

Pokud nyní budete chtít číst nebo posílat zprávy, musíte k příkazům mosquitto_sub a mosquitto_pub přidat parametry -u a -P (case sensitive), pomocí kterých určíte jmého a heslo, například:

mosquitto_sub -h localhost -t test -u developer -P pass.word123

Pokud chcete Mosquitto zpřístupnit ze sítě pomocí nešifrovaného spojení (což může a nemusí být dobrý nápad, v závislosti na tom, jaké použití plánujete), můžete tak učinit na firewallu následujícím příkazem:

sudo ufw allow 1883

Získání TLS certifikátu od Let's Encrypt

Ověření pomocí HTTP challenge

Pokud by na port 80 našeho MQTT serveru byl přímý přístup zvenčí, použil bych nástroj CertBot od Electronic Frontier Foundation:

# Nainstalovat CertBot
sudo add-apt-repository ppa:certbot/certbot -y
sudo apt-get update
sudo apt-get install certbot -y

# Povolit na firewallu port 80 pro HTTP challenge
sudo ufw allow HTTP

# Požádat o certifikát
sudo certbot certonly --standalone --preferred-challenges http-01 -d mqtt.example.com

Poté bych ještě nastavil cron (správce načasovaných úloh) tak, aby každý den v pět ráno obnovil certifikáty, u nichž je to třeba. Pomocí příkazu sudo crontab -e bych spustil editaci seznamu úloh a na konec souboru bych přidal následující řádky:

# Renew certificates using CertBot every day at 05:00
0 5 * * * certbot renew --noninteractive --post-hook "systemctl restart mosquitto"

Ověření pomocí DNS challenge

Ve chvíli, kdy nelze HTTP challenge použít, je nutné ověření realizovat pomocí DNS. Což může být jednoduché nebo komplikované, v závislosti na tom, jaké DNS servery používáte. Já mám své domény u společnosti CloudFlare, jejichž API je v CertBotu podporováno. Bohužel ale CertBot vyžaduje příliš vysoká oprávnění do CloudFlare, protože pracuje s jejich starším API. Proto používám jiného klienta, který se jmenuje Lego.

Nejprve je nutné získat API tokeny od Cloudflare. Budeme potřebovat dva: jeden pro výpis seznamu zón a druhý pro práci s DNS záznamy v konkrétní zóně.

Tokeny můžete generovat ve svém profilu v sekci API Tokens. Začněte tím, že klepnete na tlačítko Create Token:

Token pojmenujte MQTT-ListZones a nastavte práva Zone / Zone / Read pro všechny zóny a klepněte na Continue to summary:

Klepněte na Create Token a poznamenejte si vygenerovaný token - vidíte ho poprvé a naposledy. Stejným způsobem vygenerujte token jménem MQTT-EditDNS a nastavte mu práva Zone / DNS / Edit pro konkrétní zónu (doménu) kterou chcete spravovat:

Bohužel, v repozitáři je historická verze, pročež je nutné nainstalovat aktuální binárku ručně. Na stránce releases najděte adresu distribučního balíčku aktuální verze pro architekturu linux_armv7. V době psaní tohoto článku je to verze 3.3.0. Odpovídajícím způsobem pak upravte příkazy níže, kterými Lego stáhnete a nainstalujete do cesty /opt/lego/lego:

sudo su
mkdir /root/lego
cd /root/lego
wget https://github.com/go-acme/lego/releases/download/v3.3.0/lego_v3.3.0_linux_armv7.tar.gz
tar zxf lego_v3.3.0_linux_armv7.tar.gz
rm lego_v3.3.0_linux_armv7.tar.gz
mkdir /opt/lego
mv * /opt/lego/
cd ..
rm -rf lego
exit

Následujícím příkazem zažádáte o vystavení cerifikátu:

sudo \
CF_ZONE_API_TOKEN=xxxxx 
CF_DNS_API_TOKEN=yyyyy \
/opt/lego/lego -a --path /etc/lego  --dns cloudflare --domains mqtt.example.com --email [email protected] run

Změňte ho tak, aby obsahoval vaše hodnoty:

Vygenerovaný privátní klíč a veřený klíč (certifikát) budou uloženy do /etc/lego/certificates

Certifikát platí 90 dnů a je nutné ho pravidelně obnovovat. To lze udělat pomocí cronu. Spusťte příkazem sudo crontab -e editaci jeho konfiguračního souboru a na konec přidejte následující řádek:

# Renew certificates using Lego every day at 05:00
0 5 * * * CF_ZONE_API_TOKEN=xxxxx CF_DNS_API_TOKEN=yyyyy /opt/lego/lego -a --path /etc/lego --dns cloudflare --domains mqtt.example.com --email [email protected] renew --days 30 --renew-hook "systemctl restart mosquitto"

Každý den v pět hodin ráno se spustí příkaz, který obnoví certifikát v případě, že jeho platnost končí za 30 nebo méně dnů. Pokud je certifikát obnoven, restartuje Mosquitto.

Stiskem Ctrl+S soubor uložte a stiskem Ctrl+X ukončete editor Nano.

Nastavení TLS endpointu

Certifikáty máme, teď je ještě třeba zařídit, aby je Mosquitto používalo. Následujícím příkazem editujte jeho konfigurační soubor:

sudo nano /etc/mosquitto/conf.d/default.conf

Na konec souboru přidejte následující řádky v případě, že jste použili CertBot a HTTP challenge:

# Use non-secure listener
listener 1883

# Use secure listener
listener 8883
certfile /etc/letsencrypt/live/mqtt.example.com/cert.pem
cafile /etc/letsencrypt/live/mqtt.example.com/chain.pem
keyfile /etc/letsencrypt/live/mqtt.example.com/privkey.pem

Pokud jste použili Lego a DNS challenge, přidejte následující řádky:

# Use non-secure listener
listener 1883

# Use secure listener
listener 8883
certfile /etc/lego/certificates/mqtt.example.com.crt
cafile /etc/lego/certificates/mqtt.example.com.issuer.crt
keyfile /etc/lego/certificates/mqtt.example.com.key

Cestu k souborům samozřejmě upravte podle vašeho názvu serveru. Pokud chcete provozovat pouze šifrované rozhraní, můžete vynechat část s listener 1883.

Aby se změny projevily, je nutné následujícím příkazem restartovat Mosquitto:

sudo systemctl restart mosquitto

Aby bylo možné k endpointu přistupovat zvenčí, je nutné povolit port 8883 na firewallu následujícím příkazem:

sudo ufw allow 8883

Otestování funkčnosti TLS

Zbývá jenom otestovat, že vše úspěšně funguje. Můžete tak učinit z libovolného počítače kde je Mosquitto klient (včetně serveru samotného). Příkazům mosquitto_pub a mosquitto_sub je třeba přidat pouze argumenty -p (číslo portu, 8883) a --capath (cesta k seznamu důvěryhodných certifikátů, obvykle --/etc/ssl/certs).

Příkaz pro subscribe je:

mosquitto_sub -h www.example.com -t test -u developer -P pass.word123 -p 8883 --capath /etc/ssl/certs

Příkaz pro publish je:

mosquitto_pub -h www.example.com -t test -u developer -P pass.word123 -p 8883 --capath /etc/ssl/certs -m "Hello, secure world!"

Závěr

Nyní máte funkční MQTT server, běžící na počítači za deset dolarů, zabezpečený pomocí TLS certifikátů a uživatelských jmen a hesel.

Nevíte, k čemu je taková věc dobrá? Nevíte jak lépe nastavit práva, aby každý nemohl dělat všechno? Podrobněji se budu vytváření "Lan of Things" věnovat v několika na sebe navazujících přednáškách na akci TechFurMeet 2020, která se bude konat začátkem února. Co je to za akci, o tom jsem psal před rokem.

Příští ročník TechFurMeetu se připravuje, ale program je už teď docela našlapaný. Moje témata budou:

Tradičním hostem bude Štěpán Bechynský, který bude mluvit o nové tiskárně Prusa Mini a o tom, jak hlídat, že se nehýbe někdo, komu jste to zakázali, pomocí IoT platformy BigClown. Další položky programu jsou v jednání.

TFM není jenom pro furries, pokud vás obsah zajímá, neváhejte se registrovat. Pokud budete hodní a uděláte to rychle, odpustím vám chaosný poplatek za pozdní registraci.