Principes généraux

Goupile permet de concevoir un eCRF avec une approche un peu différente des outils habituels, puisqu'il s'agit de programmer le contenu des formulaires, tout en automatisant les autres aspects communs à tous les eCRF :

En plus des fonctionnalités habituelles, nous nous sommes efforcés de réduire au maximum le délai entre le développement d'un formulaire et la saisie des données.

Il s'agit d'un outil en développement et certains choix sont encore en cours. Les indications [WIP] désignent des fonctionnalités ou options en cours de développement ou d'amélioration.

Même si une version publique compilée n'est pas encore disponible, vous pouvez tester la démo en ligne.

Domaines et projets

Chaque service Goupile dispose d'un domaine (ou sous-domaine). Par exemple, demo.goupile.fr et psy-lille.goupile.fr sont des services distincts avec des bases de données séparées et des utilisateurs différents (même si possiblement hébergés sur un même serveur).

Lors de la création d'un domaine, un (ou plusieurs) administrateurs de confiance sont désignés pour en gérer les projets et les utilisateurs. Une paire de clé de chiffrement est générée pour réaliser les backups des bases de données du domaine. La clé publique est stockée sur le serveur pour créer les backups. La clé privée est confiée aux administrateurs désignés et n'est pas stockée; sa perte entraîne la perte de tous les backups de ce domaine.

Les détails sur le chiffrement utilisé sont détaillés dans la section sur les choix architecturaux.

Ce sont les administrateurs qui peuvent créér les projets et leur affecter des utilisateurs, soit pour qu'ils conçoivent les formulaires, soit pour qu'ils y saisissent les données.

Gestion des utilisateurs

Gérez vos projets et vos utilisateurs au sein du module d'administration.

Chaque domaine Goupile contient une liste d'utilisateurs.

Ces comptes utilisateurs sont gérés par le ou les administrateurs désignés pour ce domaine, qui peuvent les créer, les modifier et les supprimer.

Chaque utilisateur peut être affecté à un ou plusieurs projets, avec un ensemble de droits en fonction de ses préprogatives. Il existe deux ensembles de droits :

Ces droits sont détaillés dans les tableaux qui suivent :

Droit Explication
Develop Modification des formulaires
Publish Publication des formulaires modifiés
Configure Configuration du projet et des centres (multi-centrique)
Assign Modification des droits des utilisateurs sur le projet
Droit Explication
Load Lecture des enregistrements
Save Modification des enregistrements
Export Export facile des données (CSV, XLSX, etc.)
Batch Recalcul de toutes les variables calculées sur tous les enregistrements

Par défaut l'authentification des utilisateurs repose sur un couple identifiant / mot de passe. Ce mot de passe est stocké hashé en base (libsodium pwhash).

Plusieurs modes d'authentification forte sont disponibles ou prévus :

Utilisation de Goupile

Goupile est codé comme une application web de type SPA (Single Page Application). Un bref aperçu des différentes facettes de Goupile est donné ci-après; référez-vous au manuel utilisateur pour des informations plus détaillées.

Conception d'un eCRF

Programmez la page dans l'éditeur (gauche), et le résultat (droite) s'affiche immédiatement

Lors de la conception d'un formulaire, l'utilisateur le programme en Javascript via un éditeur texte en ligne, en appelant des fonctions prédéfinies qui génèrent les champs voulus par l'utilisateur. L'eCRF s'affiche directement sur le même écran.

Pour créer un eCRF, l'utilisateur commence par définir l'organisation et la succession des pages de saisie et des tables de données sous-jacentes. Il est possible de créer simplement des eCRF à plusieurs tables avec des relations 1-à-1 et 1-à-N (parent-enfants) à partir de ce mode.

Le contenu des pages est également défini en Javascript. Le fait de programmer nous donne beaucoup de possibilités, notamment la réalisation de formulaires complexes (conditions, boucles, calculs dynamiques, widgets spécifiques, etc.), sans sacrifier la simplicité pour les formulaires usuels.

Validation des données

La vérification de la validité des données par rapport aux contraintes imposées a lieu côté client (systématiquement) et côté serveur (sur les pages où cette option est activée). Celle-ci repose sur le code Javascript de chaque page, qui peut définir des conditions et des erreurs en fonction des données saisies.

Ces erreurs alimentent la base de contrôles qualités qui peuvent ensuite être monitorées [WIP].

Pour assurer la sécurité du serveur malgré l'exécution de code Javascript potentiellement malveillant, plusieurs mesures sont prises et détaillées dans la section Architecture du serveur.

Support hors ligne

Les eCRF développés avec Goupile peuvent fonctionner en mode hors ligne (si cette option est activée). Dans ce cas, Goupile utilise les fonctionnalités PWA des navigateurs modernes pour pouvoir mettre en cache ses fichiers et les scripts des formulaires, et être installé en tant que pseudo-application native.

Dans ce cas, les données sont synchronisées dans un deuxième temps lorsque la connexion est disponible.

Les données hors ligne sont chiffrées symétriquement à l'aide d'une clé spécifique à chaque utilisateur et connue du serveur. Cette clé est communiquée au navigateur après une authentification réussie.

Pour que l'utilisateur puisse se connecter à son application hors ligne, une copie de son profil (dont la clé de chiffrement des données hors ligne) est stockée sur sa machine, chiffrée par une clé dérivée de son mot de passe. Lorsqu'il cherche à se connecter hors ligne, son identifiant et son mot de passe sont utilisés pour déchiffrer ce profil et pouvoir accéder aux données locales.

Si le client installable est utilisé (basé sur Electron), l'authentification hors ligne peut aussi être configurée en mode fort, avec nécessité de brancher une clé USB contenant une seconde clé de chiffrement pour pouvoir se connecter à l'eCRF.

Architecture du serveur

Vue générale

Le serveur Goupile est développé en C++. Le binaire compilé contient directement le moteur de base de données (SQLite), un serveur HTTP (libmicrohttpd) ainsi que le code HTML/CSS/Javascript envoyé aux navigateurs web.

Plusieurs bases de données SQLite sont créées et utilisées pour chaque domaine. Tout d'abord, il existe une base maitre qui contient la liste des projets, des utilisateurs et les permissions. Ensuite, chaque projet utilise 1 à plusieurs bases (1 pour le projet + 1 par centre en cas de projet multi-centrique). Par exemple, un domaine avec 2 projets dont un multi-centrique pourrait utiliser les fichiers suivants :

goupile.db # Base principale
instances/projet1.db
instances/projet2.db
instances/projet2@lille.db
instances/projet2@paris.db

Le support de PostgreSQL pour pouvoir déporter la base de données sur une autre machine est prévu pour plus tard [WIP].

Isolation des services

Chaque domaine est géré par un service dédié (par exemple lancé par systemd), qui est capable de s'auto-containériser sur Linux (utilisation des capabilities POSIX, des namespaces et filtres seccomp) dans un namespace avec pratiquement aucun accès sauf au fichier SQLite.

Ce service peut utiliser un seul (mode mono-processus) ou plusieurs processus (mode multi-processus [WIP]) pour gérer chaque projet. Dans le mode multi-processus, la communication HTTP est relayée par le processus maître au processus en charge de la gestion du projet concerné.

Dans tous les cas, lorsque le serveur valide les données du formulaire (option non systématique selon les besoins de validation de données d'un formulaire), le code Javascript est exécuté par le moteur SpiderMonkey dans un processus forké avec des droits complètement restreints (pas d'accès au système de fichier ou à la base de données).

Options de compilation

En plus de la containerisation, plusieurs options de compilation Clang sont utilisées pour mitiger la vulnérabilité du serveur en cas de faille. Lors de la compilation de Goupile décrite plus loin, il s'agit du mode Paranoid.

Plusieurs mesures sont destinées à empêcher les attaques par corruption de la pile d'appels ou de détournement du flux d'exécution :

Par ailleurs, pendant le développement nous utilisons différents sanitizers (ASan, TSan et UBsan) pour détecter des erreurs d'accès mémoire, de multi-threading et l'utilisation de comportements non définis en C/C++.

Format des données

Chaque base de données Goupile est chiffrée au repos (SQLite3 Multiple Ciphers). La clé de chiffrement de la base principale est communiquée à Goupile lors du lancement par un moyen à déterminer par la personne qui administre le serveur. Chaque autre base a une clé spécifique stockée dans la base principale.

Le script des formulaires d'un projet sont stockés et versionnées dans les bases SQLite.

Les données saisies dans un projet sont stockées dans la base SQLite correspondante (pour les études multi-centriques, chaque centre dispose d'une base séparée). Deux tables SQLite sont utilisées pour les données :

La clé principale d'un enregistrement est au format ULID. Ceci permet de générer les identifiants d'enregistrement client (avec risque infinitésimal de collision) ce qui simplifie l'implémentation du mode hors ligne, tout en évitant les problèmes de performance posés par l'indexation des identifiants UUID.

Installation de Goupile

Compilation

Le serveur Goupile est multi-plateforme, mais il est recommandé de l'utiliser sur Linux pour une sécurité maximale.

En effet, sur Linux Goupile peut fonctionner en mode sandboxé grâce à seccomp et les espaces de noms Linux (appel système unshare). Le support du sandboxing est envisagé à long terme pour d'autres systèmes d'exploitation mais n'est pas disponible pour le moment. L'utilisation de la distribution Debian 10+ est recommandée.

Goupile repose sur du C++ (côté serveur) et HTML/CSS/JS (côté client). La compilation de Goupile utilise un outil dédié qui est inclus directement dans le dépôt.

Commencez par récupérer le code depuis le dépôt Git : https://framagit.org/interhop/goupile

git clone https://framagit.org/interhop/goupile
cd goupile

Linux

Pour compiler une version de développement et de test procédez comme ceci depuis la racine du dépôt :

# Préparation de l'outil Felix utilisé pour compiler Goupile
./bootstrap.sh

# L'exécutable sera déposé dans le dossier bin/Debug
./felix

Pour une utilisation en production, il est recommandé de compiler Goupile en mode Paranoid à l'aide de Clang 11+ et le lieur LLD 11+. Sous Debian 10, vous pouvez faire comme ceci :

# Préparation de l'outil Felix utilisé pour compiler Goupile
./bootstrap.sh

# Installation de LLVM décrite ici et recopiée ci-dessous : https://apt.llvm.org/
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"
sudo apt install clang-11 lld-11

# L'exécutable sera déposé dans le dossier bin/Paranoid
./felix -pParanoid --host=,clang-11,lld-11

Autres systèmes

Pour compiler une version de développement et de test procédez comme ceci depuis la racine du dépôt :

Systèmes POSIX (macOS, WSL, etc.)

# Préparation de l'outil Felix utilisé pour compiler Goupile
./bootstrap.sh

# L'exécutable sera déposé dans le dossier bin/Debug
./felix

Windows

# Préparation de l'outil Felix utilisé pour compiler Goupile
# Il peut être nécessaires d'utiliser l'environnement console de
# Visual Studio avant de continuer
bootstrap.bat

# L'exécutable sera déposé dans le dossier bin/Debug
felix

Il n'est pas recommandé d'utiliser Goupile en production sur un autre système, car le mode bac à sable (sandboxing) et la compilation en mode Paranoid n'y sont pas disponibles pour le moment.

Cependant, vous pouvez utiliser la commande ./felix --help (ou felix --help sur Windows) pour consulter les modes et options de compilations disponibles pour votre système.

Déploiement manuel

Une fois l'exécutable Goupile compilé, il est possible de créer un domaine Goupile à l'aide de la commande suivante :

# Pour cet exemple, nous allons créer ce domaine dans un sous-dossier tmp
# du dépôt, mais vous pouvez le créer où vous le souhaiter !
mkdir tmp

# L'exécution de cette commande vous demandera de créer un premier
# compte administrateur et de définir son mot de passe.
bin/Paranoid/goupile init tmp/domaine_test

L'initialisation de ce domaine va créer une clé de récupération d'archive que vous devez stocker afin de pouvoir restaurer une archive créée dans le panneau d'administration du domaine Goupile. Si elle est perdue, cette clé peut être modifiée mais les archives créées avec la clé précédente ne seront pas récupérables !

Pour accéder à ce domaine via un navigateur web, vous pouvez le lancer à l'aide de la commande suivante :

# Avec cette commande, Goupile sera accessible via http://localhost:8889/
bin/Paranoid/goupile -C tmp/domaine_test/goupile.ini

Pour un mise en production, il est recommandé de faire fonctionner Goupile derrière un reverse proxy HTTPS comme par exemple NGINX.

Pour automatiser le déploiement d'un serveur complet en production (avec plusieurs domaines Goupile et NGINX configuré automatiquement), nous fournissons un playbook et des rôles Ansible prêts à l'emploi que vous pouvez utiliser tel quel ou adapter à vos besoins.

Déploiement Ansible

Les scripts Ansible fournis sont adaptés à un déploiement sécurisé sur Debian 10+. Ils peuvent théoriquement être utilisés et/ou adaptés pour d'autres systèmes mais ceci n'est pas testé régulièrement.

Ce playbook PKnet est configuré pour installer les services suivants :

Dans ce playbook, ces services sont répartis sur 3 machines :

Vous pouvez tester rapidement ce playbook à l'aide du script Vagrant qui est inclus dans le dépôt à l'aide des commandes suivantes :

cd deploy
vagrant up --no-provision
vagrant provision

Les domaines de test suivants seront alors configurés et accessibles sur la machine locale :

Le playbook est défini par deploy/pknet.yml et l'inventaire Vagrant qui sert d'exemple est défini dans deploy/inventories/vagrant/hosts.yml. Vous pouvez copier l'inventaire et l'adapter pour configurer votre propre environnement de production, avec vos propres machines et vos propres domaines. Celui-ci contient des commentaires qui expliquent les différents réglages disponibles.

Développement de Goupile

Code et licence

Goupile est une application libre, sous licence AGPL 3. Vous pouvez librement télécharger et utiliser le code source de Goupile. Tout le monde a l'autorisation d'exécuter, de copier, de modifier, et de redistribuer des versions modifiées de ce code.

Le code source est disponible dans notre dépôt git.

Attention, il s'agit d'un dépôt unique (monorepo) qui contient plusieurs projets. Le code spécifique de Goupile est disponible dans src/goupile, mais il fait référence à plusieurs autres librairies (internes ou externes) dans ce dépôt.

Bugs et demandes

Utilisez notre outil de suivi de tickets en ligne pour demander de nouvelles fonctionnalités ou rapporter des bugs.

Configuration serveur HDS

Environnements et serveurs

Nos serveurs HDS sont déployés automatiquement à l'aide de scripts Ansible, qui sont exécutés par notre hébergeur GPLExpert (sous-traitant HDS et infogérance).

Nous utilisons deux environnements de déploiement : un environnement de pré-production (qui gère les sous-domaines *.preprod.goupile.fr) et un environnement de production. L'environnement de pré-production est identique à la production et nous permet de tester nos scripts de déploiement. Il ne contient que des domaines et données de test.

Chaque environnement utilise deux serveurs :

La communication entre le serveur proxy et le serveur back-end a lieu via un canal sécurisé (IPSec et TLS 1.2+). Les échanges entre les deux services NGINX sont protégés par des certificats serveur et client signés par un certificat interne créé au moment du déploiement (et donc la clé privée est supprimée immédiatement).

Plan de reprise d'activité [WIP]

Les environnements serveur sont configurés intégralement par des scripts Ansible automatisés et peuvent être reproduits à l'identique en quelques minutes.

La restauration des données après perte du serveur principal peut être effectuée à partir de plusieurs sources :

  1. Bases répliquées en continu sur un autre serveur [WIP]
  2. Backup nocturne chiffré des bases SQLite réalisé et copié sur un serveur à part dans un autre datacenter [WIP]
  3. Snapshot des VPS réalisé chaque nuit et conservé 14 jours, qui peut être restauré rapidement par GPLExpert