11 KiB
Belenios
Belenios est un service développé par l'Inria recréant un bureau de vote complet en ligne avec autant voire plus de sécurité grâce à des bulletins chiffrés. Il a été mis en place pour l'assemblée générale du Crans en 2020, alors que la pandémie de Covid-19 empêchait toute réunion physique.
Installation
On considère que l'on dispose d'une machine virtuelle neuve installée sous Debian Bullseye.
Belenios n'est malheureusement pas encore packagé dans Debian. Par chance, toutes ses dépendances le sont. On installe donc toutes les dépendances via APT, puis on compile Belenios manuellement.
Dépendances
Belenios est intégralement écrit en OCaml. On installe toutes ses dépendances Debian, comme indiqué dans le README :
sudo apt update
sudo apt install --no-install-recommends bubblewrap build-essential ca-certificates cracklib-runtime jq libgd-securityimage-perl libgmp-dev libncurses-dev libpcre3-dev libssl-dev libsqlite3-dev m4 pkg-config unzip wget zip zlib1g-dev
On installe ensuite toutes les dépendances OCamL depuis les paquets Debian, ce qui est plus économe par rapport à compiler toutes les bibliothèques avec Opam :
sudo apt install --no-install-recommends dune eliom libatdgen-ocaml-dev libcalendar-ocaml-dev libcmdliner-ocaml-dev libcryptokit-ocaml-dev libcsv-ocaml-dev libgettext-ocaml-dev libzarith-ocaml-dev
On doit enfin installer ocsigenserver
, qui sert de serveur web tel Nginx ou
Apache adapté pour les projets OCaml :
sudo apt install --no-install-recommends ocsigenserver
Compilation de Belenios
On commence par cloner le dépôt dans un endroit adapté :
sudo git clone https://gitlab.inria.fr/belenios/belenios.git /opt/belenios -b 1.14
Il suffit ensuite dans le dossier /opt/belenios
de lancer
make build-release-server
.
Liens symboliques
Pour avoir des binaires à des endroits cohérents avec la distribution Debian, on ajoute des liens symboliques à des endroits pertinents :
sudo ln -s /opt/belenios/_run/usr/lib/belenios /usr/lib/ocaml/belenios
sudo ln -s /opt/belenios/_run/usr/lib/belenios-platform /usr/lib/ocaml/belenios-platform
sudo ln -s /opt/belenios/_run/usr/lib/belenios-platform-js /usr/lib/ocaml/belenios-platform-js
sudo ln -s /opt/belenios/_run/usr/lib/belenios-platform-native /usr/lib/ocaml/belenios-platform-native
sudo ln -s /opt/belenios/_run/usr/lib/belenios-server /usr/lib/ocaml/belenios-server
sudo ln -s /opt/belenios/_run/usr/lib/belenios-tool /usr/lib/ocaml/belenios-tool
sudo ln -s /opt/belenios/_run/usr/share/belenios-server /usr/share/belenios-server
Configuration de ocsigenserver
On commence par créer les dossiers de données qui nous intéressent, avec les bonnes permissions :
sudo -u ocsigen mkdir -p /etc/ocsigenserver/conf.d /var/lib/belenios /var/lib/belenios/data /var/lib/belenios/upload /var/lib/belenios/spool /var/log/belenios
On ajoute enfin le fichier de configuration fourni par Belenios dans
/etc/ocsigenserver/conf.d/belenios.conf
, en remplaçant les bonnes
variables :
<!-- -*- Mode: Xml -*- -->
<ocsigen>
<server>
<port>8001</port>
<logdir>/var/log/belenios</logdir>
<datadir>/var/lib/belenios/data</datadir>
<uploaddir>/var/lib/belenios/upload</uploaddir>
<!--
The following limits are there to avoid flooding the server.
<maxuploadfilesize> might need to be increased for handling large
elections.
<maxconnected> is related to the number of simultaneous voters
visiting the server.
-->
<maxuploadfilesize>1024kB</maxuploadfilesize>
<maxconnected>500</maxconnected>
<commandpipe>/var/run/ocsigenserver_command</commandpipe>
<charset>utf-8</charset>
<findlib path="/usr/lib/ocaml"/>
<extension findlib-package="ocsigenserver.ext.staticmod"/>
<extension findlib-package="ocsigenserver.ext.redirectmod"/>
<extension findlib-package="ocsigenserver.ext.ocsipersist-sqlite">
<database file="/var/lib/belenios/data/ocsidb"/>
</extension>
<extension findlib-package="eliom.server"/>
<extension findlib-package="belenios-platform-native"/>
<host charset="utf-8" hostfilter="*" defaulthostname="{{ belenios.domain }}">
<!-- <redirect suburl="^$" dest="http://www.example.org"/> -->
<site path="static" charset="utf-8">
<static dir="/usr/share/belenios-server" cache="0"/>
</site>
<site path="monitor">
<eliom findlib-package="eliom.server.monitor.start"/>
</site>
<eliom findlib-package="belenios-server">
<!-- Domain name used in Message-ID -->
<domain name="https://{{ belenios.domain }}/"/>
<!--
The following can be adjusted to the capacity of your system.
If <maxrequestbodysizeinmemory> is too small, large elections
might fail, in particular with so-called alternative questions
with many voters.
<maxmailsatonce> depends heavily on how sending emails is
handled by your system.
-->
<maxrequestbodysizeinmemory value="1048576"/>
<maxmailsatonce value="1000"/>
<uuid length="14"/>
<gdpr uri="https://www.belenios.org/rgpd.html"/>
<contact uri="mailto:{{ belenios.email_contact }}"/>
<server mail="{{ belenios.email_from }}"/>
<auth name="{{ belenios.cas.name }}"><cas server="{{ belenios.cas.server }}"/></auth>
<source file="/usr/share/belenios-server/belenios.tar.gz"/>
<default-group file="/usr/share/belenios-server/groups/default.json"/>
<nh-group file="/usr/share/belenios-server/groups/rfc3526-2048.json"/>
<log file="/var/log/belenios/security.log"/>
<locales dir="/usr/share/belenios-server/locales"/>
<spool dir="/var/lib/belenios/spool"/>
<!-- <warning file="/opt/belenios/belenios/_run/warning.html"/> -->
</eliom>
</host>
</server>
</ocsigen>
Il faut noter que ocsigenserver
n'est pas lancé au démarrage par défaut
et un systemctl start ocsigenserver
ne fonctionne pas, un
service ocsigenserver start
en revanche si. Pour autoriser ocsigenserver
à
se lancer au démarrage, il faut modifier le fichier
/etc/default/ocsigenserver
pour écrire LAUNCH_AT_STARTUP=true
. Il sera
ensuite possible de démarrer le serveur grâce à systemd.
En redémarrant ocsigenserver
, Belenios sera accessible sur son port 80.
Après une configuration de reverse-proxy près, Belenios est déjà accessible :)
Pour modifier le logo, il faut regarder autour de
/usr/share/belenios-server/logo.png
.
Déployer avec Ansible
L'ensemble des opérations décrites sont déployées par le rôle Ansible du Crans https://gitlab.crans.org/nounous/ansible/-/tree/newinfra/roles/belenios.
Il prend en configuration le nom de domaine utilisé (belenios.domain
), des
adresses mails de contact (belenios.email_contact
et belenios.email_from
)
ainsi que les paramètres du CAS (belenios.cas.name
et belenios.cas.server
,
voir ci-dessous).
Utilisation
Toute personne ayant un compte Crans peut créer une élection sur Belenios en se connectant via le CAS du Crans. Cela se configure sur la ligne du fichier de configuration ocsigenserver :
<auth name="{{ belenios.cas.name }}"><cas server="{{ belenios.cas.server }}"/></auth>
On peut remplacer le nom par CAS Cr@ns
et le serveur par
https://cas.crans.org/.
Une fois connecté, on peut créer et administrer une élection.
Gestion des mots de passe
Belenios propose deux modes pour gérer les mots de passe :
-
Automatique (mode dégradé : les codes de vote seront générés par le serveur)
-
Manuel (mode sécurisé : un tiers se chargera des codes de vote)
Dans le premier mode, Belenios se charge de générer les codes de vote et les envoie lui-même par mail. Dans le second mode, recommandé, c'est vous qui envoyez vous même les codes de vote aux électeurs.
Le code de vote ne servant qu'à débuter l'élection pour une personne, la sécurité ne réside pas ici. C'est plus ou moins l'équivalent de donner son nom au bureau de vote. Ce n'est pas une information sensible. À la fin du vote on demande aux votants de réellement s'identifier avant de glisser les bulletins dans l'urne. On dispose de deux moyens d'authentification :
-
Générer des mots de passe confidentiels (hashés sur le serveur) par personne, géré par Belenios ;
-
Utiliser un CAS.
Il va de soi qu'on vous recommande la deuxième option. Cela peut être n'importe quel serveur CAS, pas nécessairement celui du Crans. Par exemple, on utilisera https://cas.crans.org/ pour le Crans, https://note.crans.org/cas pour la Note Kfet, ou https://cas.inria.fr/cas pour celui de l'Inria.
Pensez bien à autoriser Belenios à utiliser votre serveur CAS, et à faire attention à ce que les pseudos renvoyés soient en ASCII !
Plus d'informations ici : https://www.belenios.org/setup.html.
Inscription des électeurs
La liste des électeurs doit être arrêtée avant la création de l'élection.
La liste doit être donnée sous la forme ADRESSE@EMAIL,PSEUDO
.
Depuis la dernière version, il est également possible de définir un poids par électeur.
Atention :
Belenios ne supporte pas l'UTF-8 dans les pseudos. Assurez-vous de garder des pseudos ASCII. Et dans les adresses e-mail, mais cela va de soi.
Générer la liste des adhérents avec Re2o
Pas besoin de plus d'une ligne :
"\n".join(map(lambda user: f"{user.get_mail},{user.pseudo}", utils.all_adherent(including_asso=False).filter(adherent__isnull=False)))
C'est faux : il faut importer utils
et sans doute écrire dans un fichier.
Déclaration des autorités de déchiffrement
Il s'agit de l'équivalent des assesseurs qui vont contrôler et dépouiller l'élection. Il peut y en avoir plusieurs. Chacun aura sa clé, et la clé sera nécessaire pour le dépouillement. Dans tous les cas, Belenios lui-même est une autorité de déchiffrement mais il est recommandé d'avoir une autorité extérieure.
IMPORTANT :
Cela tient plus du mauvais pressentiment que de l'expérience, mais pensez bien à VRAIMENT conserver la clé privée en lieu sûr, elle n'est pas récupérable. Jeter sa clé privée, c'est comme acheter une urne en verre incassable avec une serrure qui tient le coup et jeter le trousseau dans les égouts, en veillant à la fondre et la broyer avant. Vous pourrez ajouter des bulletins dans l'urne mais jamais la dépouiller.
Élection
Une fois l'élection créée, il n'est plus possible de la modifier, ce qui est normal. Il est possible de définir les horaires d'ouverture de l'élection (attention : les horaires sont sur le fuseau UTC !).
Pour voter, chaque participant doit présenter son code d'élection, voter pour chacune des questions, puis soumettre son bulletin en s'authentifiant via le moyen défini. Son bulletin est ensuite chiffré et enregistré. Il est possible de revoter autant de fois que nécessaire, chaque vote annulant le précédent.
Dépouillement
Après l'élection, et pas avant (ce qui n'est de toute façon pas possible), chaque autorité de déchiffrement doit soumettre sa clé privée pour ouvrir l'urne. Une fois cela fait, les résultats sont publics sur la page de l'élection.