My Habita Installer

Serveur

Sécurité

Comptes, rôles, secrets et exposition réseau.

Comptes et rôles

Rôle Peut
Administrateur Tout : intégrations, appareils, automatisations, comptes, sauvegardes, système, journaux
Utilisateur Commander les appareils et activer les scènes, armer l'alarme et la désarmer avec un code, voir les caméras qui lui sont ouvertes, leurs enregistrements et événements, exporter des séquences, piloter le chauffage, créer et modifier ses tableaux de bord, consulter les automatisations (et les lancer), l'historique et l'export
Écran mural Afficher les tableaux de bord autorisés, commander les appareils courants, armer l'alarme (sans code si autorisé), désarmer avec un code de l'alarme ou le code personnel d'un utilisateur, voir le direct des caméras ouvertes aux écrans muraux

Les règles sont définies dans packages/shared/src/permissions.ts et vérifiées côté moteur à chaque requête et à chaque commande WebSocket.

Authentification

  • Mots de passe : 10 caractères minimum, hachés avec scrypt (sel aléatoire, comparaison à temps constant).
  • Codes PIN : 4 à 8 chiffres, hachés. Les définir ou les changer exige le mot de passe.
  • Sessions : jeton aléatoire de 256 bits dans un cookie HttpOnly, SameSite=Strict (Secure en HTTPS). Seule son empreinte SHA-256 est stockée. Durée : 30 jours renouvelés à l'usage (1 an pour un écran mural). Changer de mot de passe ou désactiver un compte ferme ses sessions.
  • Tentatives limitées pour la connexion, le PIN, le code d'installation et l'appairage : blocage temporaire croissant après 5 échecs. Les codes PIN erronés sont inscrits au journal de l'alarme et à l'audit.
  • Première installation : un code à usage unique est écrit dans /var/lib/myhabita/setup-code, lisible par root et par le service seulement. Un autre appareil du réseau ne peut donc pas créer l'administrateur à votre place.
  • Écran mural : aucun mot de passe. Appairage par un code à usage unique, valable 15 minutes, généré par un administrateur.
  • Application Android : session « mobile » propre à chaque téléphone (1 an renouvelé à l'usage), jeton transmis dans l'en-tête Authorization: Bearer (jamais envoyé automatiquement par un navigateur, donc sans risque CSRF) et chiffré sur le téléphone par une clé du Keystore Android. Révoquée en retirant le téléphone ou en changeant de mot de passe. Chacun ne gère que ses téléphones (tous pour un administrateur). Certificat local accepté seulement après affichage de son empreinte, nom du serveur toujours vérifié. Détails : application-mobile.md.

Protection des requêtes

  • Toute requête modifiante doit porter l'en-tête X-MyHabita: 1, impossible à ajouter depuis un autre site sans autorisation CORS (protection CSRF). L'en-tête Origin est vérifié, y compris pour le WebSocket.
  • En-têtes de sécurité : X-Content-Type-Options, Referrer-Policy, X-Frame-Options, Permissions-Policy.
  • Débit de commandes par connexion WebSocket limité (30 par seconde).

Secrets

  • Stockés dans /etc/myhabita/secrets/ (root 0700, fichiers 0600) et absents du dépôt Git.
  • Le moteur reçoit son mot de passe MQTT par LoadCredential (systemd), ni par l'environnement ni par un fichier qu'il pourrait lire.
  • Les journaux masquent les champs sensibles (password, pin, token, cookie…).
  • Les jetons des canaux de notification ne sont jamais renvoyés à l'interface.
  • La clé réseau Zigbee reste dans les données de Zigbee2MQTT, que le moteur ne peut pas lire.
  • Import d'une sauvegarde Zigbee2MQTT : le moteur l'analyse sans afficher la clé réseau, la dépose dans un dossier privé (/var/lib/myhabita/zigbee2mqtt-import, 0700), sans secret.yaml ; le script privilégié la lit sans suivre de lien symbolique, l'applique, puis la supprime.
  • Téléchargement de la sauvegarde Zigbee2MQTT (administrateurs, journalisé dans l'audit) : elle contient la clé réseau ; le mot de passe MQTT interne en est retiré.
  • Les sauvegardes contiennent les secrets : archives 0640 root:myhabita, à stocker en lieu sûr.

Services et privilèges

  • Moteur sous le compte myhabita, sans privilège, avec un durcissement systemd strict : système de fichiers en lecture seule sauf son dossier de données, aucune capacité, aucun accès aux périphériques, espaces de noms restreints, NoNewPrivileges.
  • Le moteur peut démarrer, arrêter ou redémarrer uniquement myhabita-zigbee2mqtt, myhabita-matter et myhabita-backup, grâce à une règle polkit (deploy/polkit/50-myhabita.rules). Cette restriction a été vérifiée : redémarrer nginx ou s'arrêter lui-même lui est refusé.
  • Les choix de l'assistant Zigbee sont validés par un script privilégié avant d'être appliqués (port, type d'adaptateur, canal). Le moteur n'écrit jamais dans la configuration de Zigbee2MQTT.
  • Zigbee2MQTT tourne sous myhabita-z2m, avec seulement le groupe dialout ; le serveur Matter sous myhabita-matter.
  • Scripts d'automatisation : bac à sable QuickJS sans accès au système, limité en mémoire et en durée. Seuls les administrateurs peuvent en écrire.
  • Cartes d'extension : iframe isolé, uniquement les entités confiées (voir docs/api-extensions.md).

Exposition réseau

Port Service Écoute Pourquoi
80 / 443 nginx (interface, API) réseau local Accès à My Habita
8800 / 8843 nginx (documentation technique) réseau local et VPN seulement (liste d'adresses), mot de passe Documentation pour le développeur (sudo myhabitactl devdoc)
1883 Mosquitto réseau local, authentification obligatoire Appareils MQTT du réseau
22 SSH (préexistant) Administration
5353/udp Avahi (mDNS) réseau local <nom-du-serveur>.local, découverte Matter
8180 moteur 127.0.0.1 Derrière nginx uniquement
5580 serveur Matter 127.0.0.1 Utilisé par le moteur uniquement
8099 interface de Zigbee2MQTT 127.0.0.1 Diagnostic avancé par tunnel SSH

Connexions sortantes du moteur : services de notification configurés (ntfy, webhook) et, pour les photos des produits Zigbee, www.zigbee2mqtt.io (une fois par modèle, seul le nom du modèle est transmis ; la photo est ensuite servie localement). Désactivable : "deviceImages": { "fetch": false } dans /etc/myhabita/myhabita.json.

Ne redirigez aucun de ces ports depuis votre box Internet. Pour un accès à distance, voir docs/hebergement.md (VPN WireGuard ou Tailscale).

Aucun pare-feu local n'est activé : il n'y en avait pas sur la machine, et les services internes n'écoutent qu'en local. Pour en ajouter un, autorisez au minimum 22, 80, 443, 1883 (réseau local), 5353/udp et l'IPv6 local nécessaire à Matter.

Journalisation et audit

  • Journal du moteur : journald, avec les 2 000 dernières entrées visibles dans Réglages → Journaux.
  • Audit, conservé 365 jours : connexions et échecs, installation, comptes, sessions, intégrations, appareils (renommage, suppression, remplacement), automatisations, chauffage, armement et désarmement, échecs de PIN, sauvegardes (lancement, téléchargement).
  • Commandes : 7 jours d'historique par entité, avec leur origine (utilisateur, automatisation…).

Mises à jour

  • Aucune mise à jour automatique susceptible de casser les intégrations : versions épinglées de Node.js (branche 24), Zigbee2MQTT et du serveur Matter ; mise à jour de My Habita à la demande, avec sauvegarde et retour arrière.
  • Les mises à jour de sécurité de Debian restent à votre initiative (apt upgrade). Elles sont recommandées régulièrement, idéalement après une sauvegarde.

Alarme et vidéosurveillance

  • Codes de l'alarme : jamais stockés en clair (HMAC-SHA256 avec la clé <données>/alarm-codes.key, sauvegardée avec les données) ; deux codes identiques sont refusés ; blocage du clavier après plusieurs erreurs (5 par défaut).
  • Code sous contrainte : l'événement n'est montré qu'aux administrateurs ; la notification part discrètement vers les contacts choisis (ou les administrateurs), jamais vers la personne contrainte ni sur les écrans.
  • Désarmement : toujours avec un code pour les personnes ; refusé aux scènes ; refusé aux automatisations sauf autorisation explicite.
  • Caméras : mots de passe conservés dans la base, jamais renvoyés aux interfaces ; la connexion (adresse, identifiant) n'est montrée qu'aux administrateurs ; chaque caméra peut être réservée aux administrateurs ou cachée aux écrans muraux ; les droits sont vérifiés aussi sur les événements temps réel.
  • go2rtc : lancé par le moteur, API et RTSP sur 127.0.0.1 ; l'API exige un mot de passe tiré au hasard à chaque lancement, même en local (local_auth). Le RTSP local de go2rtc n'a pas d'authentification pour les connexions locales (limite de go2rtc) : un programme local du serveur pourrait lire les flux. Seul le port WebRTC 8555 (TCP/UDP) est ouvert au réseau ; le navigateur ne parle jamais directement à go2rtc (le moteur relaie le direct après contrôle de la session et des droits).
  • Audit : création, modification, suppression des caméras, mode privé, exports, personnes et codes, armements et désarmements.

Multimédia

  • Adresses de lecture publiques : les enceintes et téléviseurs ne savent pas s'authentifier. /api/v1/multimedia/stream/<jeton>/… est donc servie sans session, mais chaque jeton est signé (HMAC-SHA256, clé <données>/media-stream.key), désigne un seul contenu (ou un seul flux relayé) et expire (24 h pour les appareils, 12 h pour les navigateurs et téléphones). Les pochettes (art/<id>.jpg) sont servies par un identifiant de 64 bits impossible à deviner ; les images externes (logos de radios, pochettes des enceintes) passent par un relais qui n'accepte que des adresses signées par le moteur (pas de relais ouvert).
  • Spotify : autorisation PKCE, sans secret d'application ; le jeton de rafraîchissement de chaque personne est conservé dans la base (sauvegardée), jamais renvoyé aux interfaces ; « Délier » l'efface. Chacun ne voit et ne commande que son propre compte (la lecture en cours sur une enceinte de la maison peut être mise en pause par toute la maison).
  • The Movie Database : clé conservée dans les réglages, jamais renvoyée (seulement « clé enregistrée »).
  • Dossiers de la bibliothèque : réservés aux administrateurs (media.configure) ; le moteur ne lit que ce que systemd lui laisse voir (ProtectHome, ProtectSystem=strict) ; les dossiers système sont refusés ; les chemins ne sont jamais diffusés aux autres comptes.
  • Écrans muraux : ils lisent et commandent (volume, pause), mais n'ont ni favoris, ni playlists, ni compte Spotify.
  • Enceintes My Habita visibles sur le réseau : comme n'importe quelle enceinte Spotify Connect ou AirPlay, tout appareil du réseau local peut y envoyer du son (à ne cocher que si c'est voulu). Librespot garde dans <données>/multimedia/receivers/<enceinte>/spotify/system/ les identifiants que lui donne l'application Spotify au premier choix de l'enceinte (accès à la lecture de ce compte) : dossier réservé au compte myhabita ; supprimer l'enceinte ou décocher Spotify ne les efface pas (supprimez le dossier). Les événements de librespot arrivent au moteur par une socket Unix locale (receivers/events.sock), les métadonnées AirPlay en UDP sur 127.0.0.1 seulement.
  • Flux des enceintes My Habita : même principe que la diffusion (jeton signé, une enceinte, une diffusion : une adresse d'une diffusion terminée ne sert plus).