291 lines
11 KiB
Markdown
291 lines
11 KiB
Markdown
# 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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
sudo apt install --no-install-recommends ocsigenserver
|
|
```
|
|
|
|
### Compilation de Belenios
|
|
|
|
On commence par cloner le dépôt dans un endroit adapté :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```xml
|
|
<!-- -*- 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 :
|
|
|
|
```xml
|
|
<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 :
|
|
|
|
```python
|
|
"\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.
|