As bases tecnológicas e de segurança do PiCockpit
Pediram-me para elaborar um pouco sobre as bases de segurança e tecnologia do PiCockpit.
As partes que estão envolvidas
PiCockpit é composto por várias partes:
- picockpit-cliente
- picockpit-frontend
- picockpit-backend
- picockpit-api ("papi")
- a base de dados
- o servidor MQTT
- o repositório de pacotes picockpit
O servidor MQTT
Os dados entre o picockpit-frontend e o picockpit-cliente são trocados usando o servidor MQTT (chamado "broker"), via Websockets. Nós usamos VerneMQ para este fim.
O MQTT é um protocolo de comunicação, semelhante ao HTTP. É baseado na metáfora publicar/assinar, ao contrário do HTTP (que é baseado em pedido/resposta).
No MQTT, os clientes podem conectar-se ao corretor, e subscrever tópicos - nesse caso receberão mensagens que são publicadas sobre esses tópicos. Eles também podem publicar mensagens sobre tópicos.
As mensagens podem ser publicadas como retidas - neste caso, o corretor guardará a mensagem e a enviará a qualquer cliente que se inscreva no tópico específico para o qual a mensagem foi publicada.
As vidas periódicas são enviadas do corretor para o cliente e de volta, para manter a conexão funcionando e garantir que o cliente ainda esteja online.
Adicionalmente, uma mensagem de Last Will & Testament pode ser configurada, que será publicada se o corretor perder a conexão com o cliente.
Cada Raspberry Pi recebe o seu próprio caminho tópico de mensagem, o número de série do Raspberry Pi é usado para este fim.
De acordo com minha pesquisa VerneMQ é a escolha perfeita para servir como corretor MQTT para PiCockpit:
- está focado estritamente no MQTT (ao contrário do RabbitMQ)
- tem um foco em alta performance e alta disponibilidade
VerneMQ está escrito em Erlang. Erlang é uma boa linguagem / ambiente para aplicações de telecomunicações - é projetado para ser altamente concorrente, escalável, mesmo através de vários nós. É também muito leve para o processamento de mensagens individuais, o que lhe permite escalar até milhões de mensagens. O Blog VerneMQ torna a leitura fascinante.
Características de segurança
VerneMQ permite-nos usar diferentes pontos de montagem, para separar completamente o tráfego para usuários individuais.
O cliente do picockpit e o front-end do picockpit de um usuário só poderá ver mensagens no ponto de montagem desse usuário.
O ponto de montagem é atribuído com base nos dados de autenticação, que identifica um Rasberry Pi (picockpit-cliente) ou uma conexão JavaScript (picockpit-frontend) como pertencente a esse usuário em particular.
Porquê Websockets?
Os Websockets permitem que o tráfego atravesse Firewalls & é a única forma que o cliente JavaScript pode comunicar.
O cliente-picockpit
O cliente do picockpit é escrito em Python, usando numerosas bibliotecas. Ele é empacotado como um pacote Debian (.deb), e implementado em nosso repositório público, assinado com a nossa chave.
Para a comunicação MQTT, usamos o Paho. Paho é uma biblioteca de código aberto. Usamos o Paho no front-end do picockpit também.
O Picockpit-cliente é conectado ao PiCockpit em um processo de configuração (sudo picockpit-cliente connect).
Neste processo, o cliente se comunica com a API usando uma chave API, para obter credenciais MQTT.
Tanto a chave API como as credenciais MQTT são guardadas no Raspberry Pi do utilizador, de uma forma encriptada (incluindo sal!).
O picockpit-cliente é projetado para estar constantemente online, se o Pi do usuário estiver online e conectado à Internet. Desta forma, os dados do Pi podem ser lidos & podem ser controlados pelo utilizador de forma segura.
Ao desativar o serviço picockpit-cliente, picockpit.com não será mais capaz de se conectar ao Pi e receber dados / enviar comandos de controle.
O picockpit-cliente salva arquivos de configuração local na pasta /etc/picockpit-cliente
Você vê uma estrutura de exemplo na árvore de diretórios dada acima.
O arquivo de configuração principal é picockpit-client.config.json. Este arquivo contém as credenciais necessárias para que o picockpit-client se conecte ao PiCockpit.com.
Além disso, você vê dois diretórios aplicativos/com.picockpit/picontrol/módulos e aplicativos/com.picockpit/pidoctor/módulos
Estes contêm arquivos .json, arquivos de definição para os comandos que você pode executar ativamente a partir do frontend nestas aplicações em particular.
No caso, por exemplo, de não querer mostrar os botões de reiniciar ou desligar no PiCockpit.com e desativar o PiCockpit usando essa funcionalidade, você editaria
aplicações/com.picockpit/picontrol/módulos/core.json
e reiniciar o serviço picockpit-cliente
Como você pode ver, estes arquivos contêm definições para os comandos que são mostrados no web-frontend.
Da mesma forma, se você quiser expandir a funcionalidade e adicionar comandos ou módulos adicionais (grupos de comandos), você pode fazê-lo aqui editando os arquivos .JSON ou criando novos.
Os comandos são executados usando o usuário "pi" por padrão. A sintaxe .JSON permite especificar em qual usuário o comando deve ser executado.
Picockpit-cliente é executado como root, para executar comandos, um novo Thread é rodado com os direitos do usuário sendo alterados para o usuário especificado. Esta alteração para essa thread em particular é irreversível, portanto a aplicação não pode recuperar os direitos de root.
Os mesmos arquivos JSON, com uma sintaxe um pouco diferente, podem ser criados para o PiDoctor.
Características de segurança
Como mencionado acima, os direitos do usuário podem ser modificados para que o PiControl defina o usuário. Os testes do PiDoctor são todos executados atualmente como root.
Nenhum comando pode ser definido pelo utilizador na interface Web, estes comandos têm de existir como ficheiros JSON no Pi específico que se pretende controlar.
Isto é projetado para evitar problemas de segurança, mesmo no caso de alguém ter acesso ao seu webfrontend.
O usuário pode controlar a quantidade de exposição que está disposto a dar.
Por padrão, apenas ações de controle "poweroff", "reboot" e "update picockpit-client" foram adicionadas à interface web.
Os outros módulos (GPIO & PiStats) não permitem a execução de comandos arbitrários. PiStats é atualmente codificado para a informação que é exibida na interface web:
E o GPIO permite controlar os pinos Raspberry Pi (incluindo o Software PWM), e lê-los. É tomado cuidado ao alocar os pinos (apenas para picockpit-cliente, ele não está ciente de outras aplicações - então você tem que ter cuidado lá), e sincronizar estados através de várias interfaces web acessando o mesmo Pi (por exemplo, se o usuário usa simultaneamente várias janelas ou dispositivos do navegador).
Todas estas informações são transmitidas usando o algoritmo de publicação/assinatura MQTT.
Finalmente, a comunicação websocket passa por uma porta segura, de modo que todos os dados que saem ou entram no Pi em comunicação com o corretor são criptografados.
picockpit-backend & banco de dados
O backend do picockpit está escrito no Crystal linguagem de programação, usando Kemal como um enquadramento.
A Crystal foi escolhida pelo seu desempenho, facilidade de desenvolvimento e segurança de tipo.
A Crystal fala para a base de dados. Nós usamos o MongoDB como base de dados.
Características de segurança
- as palavras-passe são salgadas e salgadas
- o usuário pode criar várias chaves API, e revogá-las (apagá-las)
- a chave API também só é salva na forma de hashed e salgado - não pode ser invertida, mesmo que alguém tenha acesso à base de dados
Tanto no lado do picockpit-cliente, como na base de dados, apenas são guardadas as derivadas em hash da chave API.
E - não - eles não combinam diretamente (isso seria meio que perder o ponto ), eles precisam de processamento adicional para validar as credenciais do usuário / o acesso do Pi.
A eliminação da chave API também elimina as credenciais MQTT para um determinado Pi.
picockpit-frontend
O frontend do picockpit é agora cada vez mais baseado em JavaScript também para fins de renderização.
Foi feita uma escolha para mover o PiCockpit mais para ser um SPA (aplicação de página única).
Uma maior integração das páginas atualmente renderizadas pelo backend será feita no frontend ao final da linha.
Nós usamos Vue.js e Vuetify.jse algumas outras bibliotecas para o frontend, incluindo Paho para a conexão MQTT usando Websockets.
Dependendo de qual Pi você está olhando, o webfrontend subscreve os caminhos MQTT apropriados.
Se você, por exemplo, pressionar o botão "shutdown", uma mensagem será publicada em um caminho MQTT definido, que o picockpit-cliente está escutando.
Características de segurança
O frontend força o uso de uma conexão HTTPS. Da mesma forma que o picockpit-cliente, os dados são transmitidos ao corretor via websockets usando apenas conexões criptografadas (https://).
No PiControl, a execução de ações "perigosas" como reiniciar ou desligar é protegida por diálogos de confirmação adicionais: