Skip to content

Alternavive GLSI léger : Grabber

Introduction au projet

Besoin

Grabber part d'un besoin de gérer un petit parc informatique (dizaines d'ordinateurs) dans un espace de travail (type entreprise). On peut déja noter certains besoins majeurs : gérer des ordinateurs à distance, connaitre d'un coup d'oeil leur état (updates, hardware, software) et établir une liste de liens entre ordinateurs et employés.

Solution

Grabber donc serait dans l'idée capable de répondre à ces besoins, via une interface web administrateur consultable par la DSI d'une entreprise par exemple. Pour la gestion d'un parc informatique de petite taille/toute petite, on proposera un produit simple, léger et plug and play. Via une simple commande de terminal et une configuration au préalable des ordinateurs cibles capable de recevoir des requêtes SSH, Grabber sera capable de couvrir un maximum des besoins.

Choix de la stack technologique

Le projet en général se basera sur du Bash et du Python pour gérer l'API et le panel Admin.

-> Bash

Les scripts Bash se contenteront de "fetch" les données système (via des commandes comme lscpu ou la librairie inxi). Ils auront aussi le rôle d'empaqueter ces données en format JSON (via jq) à un serveur qui les enregistrera dans une BDD. Et enfin, ils seront un peu l'agent qui contrôle la machine via des contrôles du systeme (vérif. admin, réglages du serveur, lanceur du serveur...)

-> Python (Django + Gunicorn + Whitenoise)

Django s'occupera de traiter les données brutes du script Bash et les enregistrer dans une BDD. De plus, il s'occupera de la logique du panel Admin via ses outil préfabriqués. Pour servir notre app Django en production, on utilise la dépendance Gunicorn qui pourra gérer le multi-tâches via des "Workers" et tenir les standards d'une webapp classique. Gunicorn sera accompagné de Whitenoise, un outil en plus qui gèrera les fichiers statiques (CSS/JS) que ne gère pas nativement Gunicorn.

-> SQLite

Pour le moment, SQLite est le meilleur choix pour sa simplicité et sa taille minimale, elle est stockée en local, ne nécessite pas plus de préparations spécifiques car déjà prête à l'emploi avec Django.

Architecture technique

Chef du service : grabber.sh

Ce script aura un rôle majeur dans le projet, il sera le chef d'orchestre du service et se chargera de gérer le panel admin et le serveur Django ainsi que l'environnement Python et d'être le centre des commandes en sécurisant le service avec un token dynamique

Entre autre, il devra :

-> Initialiser l'environnement virtuel Python (nommé gbvenv)

-> Gérer les dépendances requises (fonction requirements qui vérifie si l'hôte a bien les requis tels que jq, sqlite3 et python)

-> Génère si besoin les clés SSH nécessaires pour sécuriser la connexion de l'API

-> Vérifier la présence d'un compte super-user dans Django (important pour se logger au panel Admin, via le lancement d'un script Python "lib/check_admin.py")

-> Collecte les fichiers statiques depuis un dossier "static" afin de préparer le CSS pour Whitenoise

-> Automatiser le processus de "migrations" (python manage.py migrate)

-> Créer un token de session qui sécurisera les communications entre Django <-> Grabber <-> Alfred

L'agent de collecte : alfred.run

Info

Au départ une fonction inclue dans grabber.sh, il a finalement été décidé de séparer les deux pour plus de lisibilité et de facilité sur la suite pour être executé en autonomie dans une machine cible.

Son rôle est de fetch les données de la cible dont on a besoin pour répondre au besoin, données Hardware et Software. Toujours en Bash pour sa facilité d'utilisation dans des tâches d'extraction de données de l'OS.

En résumé, il devra donc :

-> Récupérer les données système (avec des outils natifs comme les netutils ou bien la librairie externe inxi)

-> Empaqueter ces données en un objet JSON (avec jq)

-> Envoyer les données via une requête à l'endpoint du serveur Django (avec une requête HTTP POST curl)

-> Renvoyer le token de session pour sécuriser la communication

-> Le tout est codé dans un paquet .run qui contient les librairies requises comme jq et inxi pour être utilisable sur un maximum d'ordinateurs Linux (compilé avec makeself)

Cerveau serveur : API via Django

Déploiement d'Alfred

Pour déployer Alfred sur une machine, on utilisera SSH pour envoyer le script dans un PC cible. On veut que cette tâche soit lancée directement depuis le panel Admin.

Pour se faire, on délègue celle-ci à Python avec Django qui l'effectuera avec un formulaire qui contient l'IP sur laquelle aller, le username et le mot de passe du SSH cible.

Warning

Puisque l'IP envoyée à la machine cible est récupérée via request.get_host(), l'administrateur doit impérativement accéder au panel Admin de Grabber via l'adresse IP réseau de la machine (ex: 192.168.x.x:8000) et non via localhost:8000. Sinon, le PC cible tentera d'envoyer ses données à son propre localhost et la base de données restera vide.

Requête avec Paramiko

C'est la solution trouvée pour gérer cette demande, Paramiko est une librairie Python installable avec pip qui peut établir la session SSH et le fera depuis le backend Django (dans actions.py -> fonction deploy_ssh)

Le cycle de la requête est le suivant :

-> Connexion SSH à la cible. 
   (Via la clé SSH d'abord, puis si refus, via les identifiants donnés par l'utilisateur)

-> Si l'authentification via la clé n'a pas fonctionné, 
   on envoie notre clé SSH hôte sur la machine distante

-> Transfert SFTP de l'exécutable alfred.run vers le répertoire temporaire /tmp/.

-> Exécution distante en injectant l'IP du serveur (request.get_host()) et 
   le Token de session.

-> Nettoyage des traces (rm /tmp/alfred.run).

Protection de l'API avec un Token d'Authentification (SESSION_TOKEN)

Info

Ne faut pas confondre ce token destiné aux communications inter-machines avec les sessions administrateurs du panel web, qui sont elles gérées directement par la base de données SQLite.

Comme dit plus tôt, grabber.sh génère un token unique (faisant office de clé d'API) avec la commande openssl rand à chaque lancement du serveur. Ce token est temporairement stocké dans settings.json et est automatiquement réinitialisé à la fermeture du script (via la commande trap qui lance la fonction cleanup).

Du côté serveur, lors du démarrage de l'application par Gunicorn, le fichier config/settings.py se charge de lire settings.json et de charger cette clé en mémoire dans la variable globale SESSION_TOKEN.

Enfin, lorsqu'une requête est émise par Alfred, Django intercepte et vérifie cette clé via la vue dédiée dans web/api.py. Il extrait la clé envoyée de manière sécurisée dans les en-têtes HTTP de la requête curl (précisément le header HTTP_X_API_KEY) et la compare avec celle en mémoire. Si les clés correspondent, les données matérielles sont traitées, sinon, la connexion est immédiatement rejetée avec une erreur 401 (Accès refusé).

Panel Admin

Il est une partie de l'app Django et présente dans une interface HTML/CSS/JS, une liste des ordinateurs enregistrés, la table SystemInfo avec l'adresse MAC comme clé principale pour chaque PC. Le tout est sécurisé avec le login admin de Django.

Gestion des "employés"

Via un petit modal, il est possible d'administrer en plus des ordinateurs, une BDD d'employés (stockée dans la même BDD SQLite) et d'y effectuer des tâches basiques comme créer un employé, le supprimer, l'éditer et l'assigner à un ordinateur depuis la page d'un PC.

Gestion des sessions

Pour gérer les sessions sur le Panel et éviter que les administrateurs soient déconnectés en naviguant (problème lié à la séparation des mémoires des différents workers de Gunicorn), le système de session de Django a été configuré pour être stocké directement dans la base de données SQLite (SESSION_ENGINE = "django.contrib.sessions.backends.db").

Design

Le CSS et le design utilisent la librairie Tailwind en mode CLI. Concrètement, on télécharge la librairie en local pour travailler avec, on la lance via une commande qui surveille "watch" les modifications, puis lorsque l'on a fini, on empaquete notre CSS avec le strict necessaire "minify". En savoir plus

Vue d'ensemble du projet et Conclusions (Projet à finir avant)

Difficultés rencontrées et apprentissages

Bilan

Axes d'amélioration