De technologische en veiligheidsgrondslagen van PiCockpit

Ik werd gevraagd om wat dieper in te gaan op de veiligheid & technologische fundamenten van PiCockpit.

De onderdelen die betrokken zijn

PiCockpit bestaat uit verschillende onderdelen:

  • picockpit-client
  • picockpit-frontend
  • picockpit-backend
  • picockpit-api ("papi")
  • de databank
  • de MQTT-server
  • de picockpit pakket repository

De MQTT-server

Data tussen de picockpit-frontend en picockpit-client wordt uitgewisseld met de MQTT server (genaamd "broker"), via Websockets. We gebruiken VerneMQ voor dit doel.

MQTT is een communicatieprotocol, vergelijkbaar met HTTP. Het is gebaseerd op de publish / subscribe metafoor, in tegenstelling tot HTTP (dat is gebaseerd op request / response).

In MQTT kunnen clients verbinding maken met de broker, en zich abonneren op topics - in dat geval zullen ze berichten ontvangen die over deze topics zijn gepubliceerd. Zij kunnen ook berichten publiceren over onderwerpen.

Berichten kunnen worden gepubliceerd als behouden - in dat geval bewaart de makelaar het bericht, en stuurt het naar elke client die zich abonneert op het specifieke onderwerp waarop het bericht werd gepubliceerd.

Periodieke keep-alives worden verstuurd van de makelaar naar de client en terug, om de verbinding in stand te houden en ervoor te zorgen dat de client nog steeds online is.

Bovendien kan een Last Will & Testament-bericht worden opgesteld, dat zal worden gepubliceerd als de makelaar de verbinding met de klant verliest.

Elke Raspberry Pi krijgt zijn eigen berichtonderwerppad, hiervoor wordt het serienummer van de Raspberry Pi gebruikt.

Volgens mijn onderzoek is VerneMQ een perfecte keuze om te dienen als MQTT makelaar voor PiCockpit:

  • het is strikt gericht op MQTT (in tegenstelling tot RabbitMQ)
  • het is gericht op hoge prestaties en hoge beschikbaarheid

VerneMQ is geschreven in Erlang. Erlang is een goede taal/omgeving voor telecommunicatietoepassingen - het is ontworpen om zeer concurrent, schaalbaar te zijn, zelfs over meerdere nodes. Het is ook zeer licht voor het verwerken van individuele berichten, waardoor het kan schalen tot miljoenen berichten. De VerneMQ blog is fascinerende lectuur.

Beveiligingsfuncties

VerneMQ laat ons toe om verschillende mountpoints te gebruiken, om het verkeer voor individuele gebruikers volledig te scheiden.

De picockpit-client en picockpit-frontend van een gebruiker zullen enkel berichten kunnen zien in het mountpoint van die gebruiker.

Het mountpoint wordt toegewezen op basis van de authenticatie gegevens, die een Rasberry Pi (picockpit-client) of een JavaScript connectie (picockpit-frontend) identificeert als behorend tot die bepaalde gebruiker.

Waarom Websockets?

Websockets maken het mogelijk dat het verkeer firewalls passeert en is de enige manier waarop de JavaScript client kan communiceren.

De picockpit-client

De picockpit-client is geschreven in Python, met gebruik van talrijke bibliotheken. Het is verpakt als een Debian pakket (.deb), en uitgerold naar onze publieke repository, ondertekend met onze sleutel.

Voor MQTT communicatie gebruiken we Paho. Paho is een open source bibliotheek. We gebruiken Paho ook in de picockpit-frontend.

De picockpit-client wordt verbonden met PiCockpit in een setup proces (sudo picockpit-client connect).

In dit proces communiceert de client met de API met behulp van een API-sleutel, om MQTT-referenties te verkrijgen.

Zowel de API-sleutel als de MQTT-gegevens worden op de Raspberry Pi van de gebruiker opgeslagen, in een versleutelde vorm (inclusief zout!).

De picockpit-client is ontworpen om constant online te zijn, als de Pi van de gebruiker online is en verbonden met het internet. Op deze manier kunnen de gegevens van de Pi worden gelezen en kan de gebruiker deze op een veilige manier controleren.

Door de picockpit-client service uit te schakelen, zal picockpit.com geen verbinding meer kunnen maken met de Pi en geen data meer ontvangen / controle commando's meer kunnen versturen.

De picockpit-client slaat lokale configuratie bestanden op in de map /etc/picockpit-client

afbeelding

U ziet een voorbeeldstructuur in de hierboven gegeven mapstructuur.

Het belangrijkste configuratie bestand is picockpit-client.config.json. Dit bestand bevat de nodige gegevens voor picockpit-client om verbinding te maken met PiCockpit.com.

Daarnaast zie je twee directories apps/com.picockpit/picontrol/modules en apps/com.picockpit/pidoctor/modules

Deze bevatten .json bestanden, definitie bestanden voor de commando's die je actief kan uitvoeren vanaf de frontend in deze specifieke apps.

In het geval, bijvoorbeeld, dat u de reboot of shutdown knoppen niet wilt tonen in PiCockpit.com en PiCockpit niet wilt toestaan die functionaliteit te gebruiken, zou u het volgende bewerken

apps/com.picockpit/picontrol/modules/core.json

en herstart de picockpit-client service

afbeelding

Zoals u kunt zien, bevatten deze bestanden definities voor de commando's die in de web-frontend worden getoond.

Ook als u de functionaliteit wilt uitbreiden en extra commando's of modules (groepen van commando's) wilt toevoegen, kunt u dat hier doen door de .JSON-bestanden te bewerken of nieuwe aan te maken.

Commando's worden standaard uitgevoerd met de gebruiker "pi". Met de .JSON syntaxis kunt u opgeven onder welke gebruiker het commando moet worden uitgevoerd.

picockpit-client draait als root, voor het uitvoeren van commando's wordt een nieuwe Thread gesponnen waarbij de gebruikersrechten worden gewijzigd naar de gespecificeerde gebruiker. Deze wijziging voor die specifieke thread is onomkeerbaar, dus de applicatie kan de root-rechten niet meer opeisen.

Dezelfde JSON-bestanden, met een iets andere syntaxis, kunnen worden aangemaakt voor PiDoctor.

afbeelding

Beveiligingsfuncties

Zoals hierboven vermeld, kunnen de gebruikersrechten voor PiControl gewijzigd worden door de gebruiker in te stellen. De tests van PiDoctor worden momenteel allemaal als root uitgevoerd.

Er kunnen geen commando's worden gedefinieerd door de gebruiker in de Webinterface, deze commando's moeten bestaan als JSON-bestanden op de specifieke Pi die u wilt controleren.

Dit is ontworpen om veiligheidsproblemen te voorkomen, zelfs in het geval dat iemand toegang krijgt tot uw webfrontend.

De gebruiker kan bepalen hoeveel blootstelling hij bereid is te geven.

Standaard zijn alleen "poweroff", "reboot" en "update picockpit-client" controle acties toegevoegd aan de webinterface.

De andere modules (GPIO & PiStats) laten niet toe om willekeurige commando's uit te voeren. PiStats is momenteel hard gecodeerd naar de informatie die wordt weergegeven in de webinterface:

pistolen

En GPIO maakt het mogelijk om Raspberry Pi pinnen aan te sturen (inclusief Software PWM), en ze te lezen. Zorgvuldigheid is geboden bij het toewijzen van de pinnen (alleen voor picockpit-client, het is zich niet bewust van andere toepassingen - dus daar moet je voorzichtig zijn), en het synchroniseren van toestanden over meerdere webinterfaces die toegang hebben tot dezelfde Pi (bijv. als de gebruiker tegelijkertijd meerdere browservensters of apparaten gebruikt).

Al deze informatie wordt verzonden met behulp van het MQTT publish / subscribe algoritme.

Tenslotte loopt de websocketcommunicatie over een beveiligde poort, zodat alle gegevens die de Pi uitgaan of binnenkomen in de communicatie met de makelaar versleuteld zijn.

picockpit-backend & databank

De picockpit backend is geschreven in het Crystal programmeertaal, met behulp van Kemal als een kader.

Crystal werd gekozen vanwege zijn prestaties, ontwikkelaarsvriendelijkheid en type-veiligheid.

Crystal praat met de database. We gebruiken MongoDB als database.

Beveiligingsfuncties

  • wachtwoorden worden gehasht en gezouten
  • de gebruiker kan meerdere API-sleutels aanmaken, en deze weer intrekken (verwijderen)
  • de API-sleutel wordt ook alleen in een gehashte en gezouten vorm opgeslagen - hij kan niet worden omgekeerd, zelfs niet als iemand toegang krijgt tot de database

Zowel aan de kant van de picockpit-client, als in de database, worden alleen hashed afgeleiden van de API sleutel opgeslagen.

En - nee - ze komen niet rechtstreeks overeen (dit zou een beetje de plank misslaan Glimlach), moeten zij verder worden verwerkt om de inloggegevens van de gebruiker / de toegang van de Pi te valideren.

Het verwijderen van de API-sleutel verwijdert ook de MQTT-gegevens voor een bepaalde Pi.

picockpit-frontend

De picockpit frontend is nu meer en meer gebaseerd op JavaScript, ook voor rendering doeleinden.

Er is gekozen om PiCockpit meer in de richting van een SPA (single page application) te sturen.

Verdere integratie van de pagina's die momenteel door de backend worden weergegeven, zal in de toekomst in de frontend worden gemaakt.

We gebruiken Vue.js en Vuetify.js, en enkele andere bibliotheken voor de frontend, waaronder Paho voor de MQTT verbinding via Websockets.

Afhankelijk van welke Pi u bekijkt, abonneert de webfrontend zich op de juiste MQTT-paden.

Als je bijvoorbeeld op de "shutdown" knop drukt, zal een bericht gepubliceerd worden op een gedefinieerd MQTT pad, waar picockpit-client op luistert.

Beveiligingsfuncties

De frontend dwingt het gebruik van een HTTPS verbinding af. Net als bij de picockpit-client worden gegevens via websockets naar de broker verzonden, waarbij alleen versleutelde verbindingen worden gebruikt (https://).

In PiControl wordt het uitvoeren van "gevaarlijke" acties, zoals opnieuw opstarten of afsluiten, beschermd door extra bevestigingsdialogen:

afbeelding