Files
snek/README.md
T
tarcourt 2e24415224 docs: precise SNEK callback config for JSON POST body
- Step-by-step field-by-field callback config table
- Common pitfall documented: don't paste JSON body in "Line pattern"
- Exact JSON body template format (spaces around colons required)
- Companion dashboard deployment steps

Backend also cleaned up: unified query-string / JSON body parsing
via a single _num() helper instead of nested try/except blocks.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-03 17:58:05 +02:00

447 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SNEK — Sigfox Network Emulator (K8s Edition)
Une stack complète pour émuler un réseau Sigfox depuis n'importe quel cluster Kubernetes, avec un dongle SDR physique.
**Cas d'usage** : recevoir en direct les messages Sigfox de tes devices (Sens'it, capteurs custom, etc.) sans passer par le backend public Sigfox, décoder les payloads, et transférer les données vers n'importe quel service (API, base, webhook, etc.).
---
## Sommaire
1. [Vue d'ensemble](#vue-densemble)
2. [Hardware — le dongle Sigfox SDR](#hardware--le-dongle-sigfox-sdr)
3. [Software — SNEK](#software--snek)
4. [Déploiement](#déploiement)
5. [Configuration des devices](#configuration-des-devices)
6. [Réception de messages (uplink)](#réception-de-messages-uplink)
7. [Décodage des payloads](#décodage-des-payloads)
8. [Callbacks HTTP (intégration avec autres services)](#callbacks-http-intégration-avec-autres-services)
9. [Sécurité, vie privée & aspects légaux](#sécurité-vie-privée--aspects-légaux) ⚠️
10. [Downlink (messages serveur → device)](#downlink-messages-serveur--device)
11. [Persistance & backups](#persistance--backups)
12. [Dépannage](#dépannage)
---
## Vue d'ensemble
```
┌─────────────┐
│ Device │ (Sens'it, capteur custom, etc.)
│ Sigfox │
└──────┬──────┘
│ RF 868 MHz (EU)
┌─────────────┐
│ Dongle │ (NXP LPC 2cc1:8001 → 2cc1:0001 après flash)
│ SDR USB │
└──────┬──────┘
│ USB via /dev/bus/usb (hostPath mount)
┌──────────────────────────┐
│ Pod SNEK (K8s) │
│ - Container privileged │
│ - Ubuntu 20.04 │
│ - SNEK 2.3.4 │
│ - Web UI :8085 │
└──────┬───────────────────┘
│ HTTP callback (JSON)
┌─────────────┐
│ Ton API, │ (Elasticsearch, Postgres,
│ ton bot, │ Telegram, Kafka, …)
│ ton front │
└─────────────┘
```
---
## Hardware — le dongle Sigfox SDR
**Modèle utilisé** : Sigfox SDR Dongle officiel (chipset NXP LPC).
**Identifiants USB** :
- Au boot : `2cc1:8001` (mode DFU / bootloader)
- Après flash firmware par `foxctl` : `2cc1:0001` (mode opérationnel)
**Prérequis** :
- Port USB 2.0 High Speed (les ports 3.0 rétrocompatibles marchent aussi)
- Antenne 868 MHz (fournie ou fil quart d'onde 8.6 cm)
- **Zone RC1** pour l'Europe (868 MHz), autres zones (RC2 US, RC3 Asie, etc.) configurables dans SNEK
**Le firmware n'est pas persistent** : à chaque unplug ou reboot du dongle, il repart en mode DFU. Le script `entrypoint.sh` du container reflashe automatiquement au démarrage via `foxctl`.
---
## Software — SNEK
**SNEK 2.3.4** (Sigfox Network Emulator) est le logiciel officiel Sigfox qui :
- Pilote le dongle SDR
- Émule le réseau Sigfox localement
- Enregistre les devices (up to 5 par défaut, extensible à 10 via config)
- Reçoit et décode les messages uplink
- Envoie des messages downlink
- Expose une interface web (port 8085)
- Envoie des callbacks HTTP en JSON à des URL externes
**Dépendances système** (pré-installées dans l'image Docker) :
- Ubuntu 20.04 (obligatoire — `libgtk2-perl` n'existe pas en 22.04+)
- `libgtk2-perl`, `zenity`, `python`, `libdbus-glib-1-2`, `usbutils`
- `libffi6` (ABI 18.04, downloadée depuis les archives Ubuntu)
**Configuration** : fichier `/root/Snek/snek.conf` (JSON), monté sur PVC pour persistance.
---
## Déploiement
### Prérequis cluster
1. **Kubernetes** ≥ 1.24
2. **Namespace privileged** (le container a besoin de `securityContext.privileged: true`)
3. **StorageClass** pour PVC (ex: `local-path`)
4. **MetalLB ou équivalent** pour exposer le service en LoadBalancer (optionnel — `ClusterIP` suffit si tu accèdes depuis un autre pod)
5. **Node avec le dongle physiquement branché** en USB
### Build & Push
```bash
cd snek/build
docker build --platform linux/amd64 -t 192.168.1.100:30500/snek:latest .
docker push 192.168.1.100:30500/snek:latest
```
Remplace `192.168.1.100:30500` par ton registry.
### Deploy
```bash
kubectl apply -f snek/deploy/snek.yaml
```
Le manifest crée :
- Namespace `snek` avec label `pod-security.kubernetes.io/enforce=privileged`
- PVC `snek-data` (2 Gi, persistance de la config `/root/Snek`)
- Deployment `snek` avec `securityContext.privileged=true` + `hostPath /dev/bus/usb`
- Service LoadBalancer sur port 8085
### Vérification
```bash
kubectl get pods -n snek
kubectl logs -n snek -l app=snek --tail=30
```
Tu dois voir dans les logs :
```
==> Dongle detected
==> Flashing firmware (foxctl)...
==> Starting SNEK main_sigfox on port 8085...
INFO - SNEK Software Version : "2.3.4"
INFO - Server started at http://0.0.0.0:8085
INFO - Frequence RX : 868130000
INFO - Frequence TX : 869525000
```
### Accès UI
- **Depuis ton LAN** : `http://<LoadBalancer-IP>:8085/staticv234/index.html`
- **Depuis un autre pod K8s** : `http://snek.snek.svc.cluster.local:8085`
---
## Configuration des devices
Chaque device Sigfox a un **identifier hex 6 caractères** (ex: `B440C7`).
Tu le trouves :
- Sur l'autocollant du device
- Ou dans le backend Sigfox → Device details
- Ou sur les Sens'it via l'app mobile
Dans SNEK → onglet **Configuration** → Devices → ajoute jusqu'à **5 devices** (limite hardcodée dans le frontend JS de SNEK 2.3.4). Pour plus, lancer plusieurs instances de SNEK.
**Important** : SNEK ne décode que les messages des devices enregistrés dont l'auth passe. Un message d'un ID non enregistré est simplement logué comme `Receiving message with bad authentication (not public key)` sans détail sur l'ID ni le payload.
### Authentication auto-disabled au démarrage
Par défaut SNEK exige que chaque message soit **signé HMAC** avec la clé publique du device (Network Access Key). Les Sens'it de démo n'ont pas leur clé disponible → messages rejetés en `bad authentication`.
L'entrypoint du container **désactive automatiquement** l'auth via un POST sur `/ned/saturation` juste après le démarrage de SNEK. Ainsi tes devices enregistrés fonctionnent immédiatement au premier boot, et à chaque restart du pod (SNEK ne persiste PAS l'état auth ailleurs).
Si tu veux la garder activée (ex: prod avec devices dont tu as les clés publiques), commente le bloc `for i in $(seq 1 30)` dans `build/entrypoint.sh` et rebuild l'image.
⚠️ Voir la section [Sécurité, vie privée & aspects légaux](#sécurité-vie-privée--aspects-légaux) pour les implications.
---
## Réception de messages (uplink)
Une fois un device configuré, dès qu'il émet un message Sigfox (bouton double-clic, réveil périodique, alerte capteur…) :
1. **Réception RF** par le dongle SDR
2. **Décodage bas-niveau** par le firmware
3. **Traitement** par SNEK (auth, dedup, décodage payload)
4. **Affichage** dans l'onglet **Messages** de la web UI
5. **Envoi callback** vers ton service si configuré (voir section suivante)
Format d'un message dans la UI SNEK :
```
Date Device Payload RSSI SNR
2026-07-03 15:23 B440C7 b60dc86e -85 dBm 12 dB
```
**Debug** : si un device n'apparaît pas, vérifie dans les logs :
```bash
kubectl logs -n snek -l app=snek -f | grep -i "receiving\|register"
```
---
## Décodage des payloads
Les payloads Sigfox sont **binaires**, format propre à chaque device. Voici le décodage pour un **Sens'it Discovery en mode Temperature** (4 bytes).
### Structure
| Byte | Bits | Contenu |
|------|------|---------|
| 0 | 7-3 | Battery Level (5 bits, formule `V = val × 0.05 + 2.7`) |
| 0 | 2-0 | Reserved `0b110` |
| 1 | 7-3 | Mode (`00001` = Temperature) |
| 1 | 2 | Temperature MSB (1 bit) |
| 1 | 1 | Spare |
| 1 | 0 | Button Alert Flag (1 si double-clic) |
| 2 | 7-0 | Temperature LSB (formule `T = (val 200) / 8` °C) |
| 3 | 7-0 | Humidity (formule `H = val / 2` %) |
### Exemple
Payload hex : `b60dc86e`
- Byte 0 = `0xB6` = `1011 0110` → Battery = 22 × 0.05 + 2.7 = **3.8V**
- Byte 1 = `0x0D` = `0000 1101` → Mode = 1 (Temperature), Temp MSB = 1, Button = 1
- Byte 2 = `0xC8` = 200 → Temp raw = 1×256 + 200 = 456 → **32°C**
- Byte 3 = `0x6E` = 110 → Humidity = 110 / 2 = **55%**
### Doc officielle Sens'it
[Sens'it Discovery Payload Structure (PDF)](https://storage.googleapis.com/public-assets-xd-sigfox-production-338901379285/build/4059ae1jy7g2jmg/sensit-discovery-payload.pdf)
Le document décrit les 6 modes (Standby, Temperature, Light, Door, Vibration, Magnet) et le format Config payload pour les downlinks.
---
## Callbacks HTTP (intégration avec autres services)
C'est là que ça devient puissant : à chaque message reçu, SNEK peut faire un **POST HTTP** vers une URL de ton choix avec le payload en JSON.
### Configuration
### Configurer un callback dans la UI SNEK
Dans SNEK → onglet **Callbacks****New** (dans la section DATA callbacks) :
| Champ | Valeur |
|-------|--------|
| **Type** | `UPLINK` |
| **Channel** | `URL` |
| **Send duplicate** | décoché |
| **Url pattern** | ton endpoint (ex: `http://mon-service.mon-ns.svc.cluster.local/webhook`) |
| **Line pattern** | *(vide)* |
| **Content type** | `application/json` |
| **Method** *(apparaît selon channel)* | `POST` |
| **Body** *(apparaît une fois Method=POST + Content-Type=application/json)* | template JSON ci-dessous |
Body template (format exact — respecter les espaces autour des `:`) :
```json
{
"device" : "{device}",
"data" : "{data}",
"time" : {time},
"rssi" : {rssi},
"snr" : {snr},
"seqNumber" : {seqNumber}
}
```
⚠️ **Piège classique** : ne pas coller le JSON dans **Line pattern**. Ce champ fait une substitution char-par-char et refuse tout ce qui n'est pas un nom de variable connu (tu obtiens `Wrong values. Please fix: ...`). Le body JSON va dans le champ **Body** qui n'apparaît qu'après avoir choisi Method=POST et Content-Type=application/json.
### Variables disponibles
`{device}` `{time}` `{data}` `{rssi}` `{snr}` `{seqNumber}` `{duplicate}` `{station}` `{avgSnr}` `{LQI}`
### Cas d'usage
- **Dashboard live** : dossier [`dashboard/`](./dashboard/) — Flask + SQLite + SSE + Chart.js prêt à l'emploi
- Ingérer dans une base (Postgres, InfluxDB via une API custom)
- Alertes Telegram / Slack / Discord
- Bus de messages Kafka / RabbitMQ
### Dashboard intégré (companion service)
Un mini dashboard responsive est fourni dans [`dashboard/`](./dashboard/) — il reçoit les callbacks SNEK, décode les 6 modes Sens'it (Standby, Temperature, Light, Door, Vibration, Magnet), stocke en SQLite et affiche en temps réel via Server-Sent Events. Tailwind + Chart.js.
Pour le déployer :
```bash
cd dashboard
docker build --platform linux/amd64 -t 192.168.1.100:30500/snek-dashboard:latest .
docker push 192.168.1.100:30500/snek-dashboard:latest
kubectl apply -f ../deploy/dashboard.yaml
```
Puis configure le callback SNEK avec l'URL :
```
http://snek-dashboard.snek.svc.cluster.local/webhook
```
et le body JSON template ci-dessus. Ouvre `http://<lb-ip-dashboard>` dans un navigateur.
---
## Sécurité, vie privée & aspects légaux
⚠️ **À lire attentivement avant tout déploiement en production ou test avancé.**
### Ce que capte physiquement le dongle
Le dongle SDR est un **récepteur RF broadcast** sur 868 MHz (RC1). Il capte **tous les messages Sigfox** dans sa portée physique :
- ~1-5 km en zone urbaine dense
- ~10-40 km en zone dégagée avec bonne antenne
Cela inclut les émissions **de tes voisins, entreprises locales, capteurs de ville, alarmes, trackers…** — pas seulement les tiens.
### Ce que SNEK expose
Par défaut (auth activée, device non enregistré) :
- Message logué comme `bad authentication`
- **Aucun** ID, **aucun** payload, **aucun** RSSI accessible
- Bonne protection par défaut
Attaque théoriquement possible :
1. Enregistrer l'ID d'un device tiers dans SNEK (ID trouvé sur l'étiquette, dans une doc leakée, en OSINT)
2. Désactiver globalement l'authentification (option UI)
3. Recevoir les payloads bruts en clair du device cible tant qu'il est à portée radio
**Limites de l'attaque** :
- Nécessite la proximité physique (pas d'attaque via internet)
- Le format des 12 bytes de payload est propriétaire à chaque vendor — inutile sans reverse engineering
- Certains devices utilisent le chiffrement AES-128 optionnel de Sigfox → payload chiffré
### Aspect légal (France)
-**Réception passive du spectre RF public** : légal
-**Analyse de tes propres devices** : légal
-**Recherche sécurité en labo perso** : légal
-**Interception intentionnelle de communications tierces** : **délit pénal**
- Article 226-15 du Code Pénal → 1 an prison + 45 000 € d'amende
-**Collecte / redistribution / monétisation** des données captées : illégal (RGPD si données identifiantes, concurrence déloyale si usage commercial)
### Bonnes pratiques
- **Ne configure QUE tes propres devices** dans SNEK
- **Laisse l'auth activée** (paramètre par défaut)
- **Baisse le log level à WARNING** pour ne pas garder de trace des messages non-authentifiés :
```json
"root": {"level": "WARNING", ...}
```
- **Purge régulièrement** `/root/Snek/snek.log`
- Pour tes propres devices sensibles → utilise l'**AES-128 optionnel** de Sigfox
Sigfox est conçu pour l'**authenticité** (HMAC empêche l'usurpation), pas la **confidentialité par défaut**. C'est une limitation connue du protocole, à prendre en compte pour tout use case impliquant des données sensibles.
---
## Downlink (messages serveur → device)
Sigfox permet 4 downlinks max par device par jour. Envoi depuis SNEK :
1. Web UI → onglet **Devices** → sélectionner un device
2. Zone **Downlink** → 8 bytes hex à envoyer
3. Le message sera envoyé lors du prochain uplink (le device demande un downlink en cours d'émission)
**Note** : le downlink est utilisé pour reconfigurer un Sens'it (changer de mode, ajuster seuils, etc.) via le format Config payload documenté dans le PDF Sens'it.
---
## Persistance & backups
Le PVC `snek-data` (2 Gi, `local-path`) monté sur `/root/Snek` contient :
- `snek.conf` : configuration serveur (devices, callbacks, MAX_DEVICES, radio)
- `snek.log` : logs applicatifs (rotation 5 × 10 MB)
- Historique des messages reçus
**Backup** : Velero avec `defaultVolumesToFsBackup: true` dans les schedules capture le PVC.
Pour un backup manuel :
```bash
kubectl exec -n snek -l app=snek -- cat /root/Snek/snek.conf > snek-backup-$(date +%F).json
```
---
## Dépannage
### Le dongle n'est pas détecté
```bash
kubectl exec -n snek -l app=snek -- lsusb | grep 2cc1
```
Rien ? Vérifier :
- Le dongle est branché physiquement sur le node
- `hostPath /dev/bus/usb` est bien monté (dans la spec du deployment)
- Le pod est en `privileged: true`
- Sur Talos : `talosctl ls /sys/bus/usb/devices` sur le node depuis le Mac
### Erreur "libxfox.so.0: cannot open shared object file"
Manque de `LD_LIBRARY_PATH=/opt/snek`. L'image Docker le définit dans le `ENV`, si tu débug en shell interactif, il faut le re-exporter.
### Erreur "cannot import name Fox, introspection typelib not found"
Manque de `GI_TYPELIB_PATH=/opt/snek`. Idem que ci-dessus.
### "No xfox device detected" en boucle
Le dongle est branché mais le firmware ne s'est pas flashé. Causes possibles :
- Autre process tient le lock USB (ex: si tu as tenté KubeVirt USB passthrough avant)
- Débranche/rebranche physiquement le dongle
- Restart le pod : `kubectl delete pod -n snek -l app=snek`
### Interface UI affiche 5 devices même après passer MAX_DEVICES à 10
Cache navigateur. **Hard refresh** (`Cmd+Shift+R` / `Ctrl+Shift+R`) ou fenêtre privée.
### Le pod crash au démarrage
```bash
kubectl logs -n snek -l app=snek --previous
```
Le plus souvent : dongle absent → foxctl fait `sys.exit(1)`. Solution : brancher le dongle, redémarrer le pod.
---
## Structure du repo
```
snek/
├── README.md ← ce document
├── build/
│ ├── Dockerfile ← image Ubuntu 20.04 + SNEK + deps
│ └── entrypoint.sh ← flash firmware + start server
└── deploy/
└── snek.yaml ← namespace + PVC + deployment + service
```
## Ressources
- [Support Sigfox SDR](https://support.sigfox.com/products/SDR-dongle)
- [Doc SNEK PDF](https://storage.googleapis.com/public-assets-xd-support-sigfox-production-338901379285/61071942-SIGFOX%20Network%20Emulator(19-11-2019).pdf)
- [Sens'it Discovery Payload](https://storage.googleapis.com/public-assets-xd-sigfox-production-338901379285/build/4059ae1jy7g2jmg/sensit-discovery-payload.pdf)
---
Made with Kubernetes, some patience, and a Sigfox dongle. 📡