Applications Azure

Belenios on Ubuntu 24.04 on Azure User Guide

| Product: Belenios on Ubuntu 24.04 LTS on Azure

Overview

Belenios is an open-source verifiable online voting system developed by INRIA. Voters cast encrypted ballots, anyone can check that their ballot was recorded on the public bulletin board, and the final tally can be independently verified against that board - so results are trustworthy without having to trust the server. Roles are cleanly separated between the administrator who runs the election, the credential authority who issues voting credentials, and the trustees who share the decryption key. The cloudimg image runs the Belenios server (built from the official release) behind nginx over HTTPS: the server is published on the loopback interface only, and nginx terminates TLS in front of it. Backed by 24/7 cloudimg support.

What is included:

  • Belenios 3.3.0 (built from the official INRIA release on top of the upstream glondu/beleniosbase toolchain), baked into the image and published on loopback 127.0.0.1:8001
  • nginx terminating TLS on :443 in front of Belenios, with :80 redirecting to HTTPS
  • A per-VM administrator account generated on first boot - no default or shared credential ships in the image (Belenios ships with no accounts at all; a single administrator is seeded uniquely for your VM)
  • A self-contained single-VM deployment - the election spool, accounts and password database live under /var/lib/belenios
  • docker.service, belenios.service and nginx.service as systemd units, enabled and active
  • 24/7 cloudimg support

Belenios is distributed under the GNU AGPL-3.0-or-later licence (with an OpenSSL linking exception).

Secure by default - a per-VM administrator credential

This image ships with no default or shared login. Belenios itself ships with no accounts, and no password database ships in the image. On first boot a one-shot service resolves your public URL, generates a per-VM TLS certificate, writes the public election URL into the server configuration, and seeds a single administrator account with a unique per-VM password that it writes to a root-only file. The Belenios server is deliberately held back until that has happened, so there is never a window in which a fresh VM is reachable with a well-known login. You retrieve the password over SSH and change it after your first login.

The Belenios login page served over HTTPS

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a comfortable starting point; scale up for very large electorates. NSG inbound: allow 22/tcp from your management network, and 80/tcp + 443/tcp from wherever your administrators and voters browse Belenios (:80 only redirects to :443).

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Belenios by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22), HTTP (80) and HTTPS (443). Then Review + create -> Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name belenios \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard

az vm open-port --resource-group <your-rg> --name belenios --port 80 --priority 1010
az vm open-port --resource-group <your-rg> --name belenios --port 443 --priority 1020

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

The message of the day shows your VM's Belenios admin URL and administrator username.

Step 4 - Confirm the services are running

Belenios runs as three systemd units - the Docker engine, the Belenios server and nginx:

systemctl is-active docker.service belenios.service nginx.service
active
active
active

The Belenios server is published only on the loopback interface; nginx is the only service bound to public ports (:80 and :443):

sudo ss -tln | grep -E ':(80|443|8001) '
LISTEN 0      4096       127.0.0.1:8001      0.0.0.0:*
LISTEN 0      511          0.0.0.0:443       0.0.0.0:*
LISTEN 0      511          0.0.0.0:80        0.0.0.0:*

The web UI is served over HTTPS, and plain HTTP redirects to it:

curl -sk -o /dev/null -w 'https:// -> %{http_code}\n' https://127.0.0.1/
curl -s  -o /dev/null -w 'http://  -> %{http_code}\n' http://127.0.0.1/
https:// -> 200
http://  -> 301

Belenios services, loopback + nginx binding, and HTTPS health on the VM

Step 5 - Read the per-VM administrator credential

On first boot the VM writes a root-only file with your Belenios URL, the administrator username and the unique password generated for this VM:

sudo grep -E '^BELENIOS_URL=|^BELENIOS_ADMIN_USER=|^BELENIOS_ADMIN_EMAIL=' /root/belenios-credentials.txt
BELENIOS_URL=https://<vm-public-ip>
BELENIOS_ADMIN_USER=admin
BELENIOS_ADMIN_EMAIL=admin@cloudimg.local

The BELENIOS_ADMIN_PASSWORD line in that file holds your unique password. Only the per-VM credential authenticates - a wrong password is rejected. The following prints the HTTP status of the server's programmatic login endpoint for the per-VM password and for a wrong one:

P=$(sudo grep '^BELENIOS_ADMIN_PASSWORD=' /root/belenios-credentials.txt | cut -d= -f2-)
curl -sk -o /dev/null -w 'per-VM password -> HTTP %{http_code}\n' -X POST https://127.0.0.1/api/login/local \
  -H 'Content-Type: application/json' --data "{\"username\":\"<belenios-admin-user>\",\"password\":\"<belenios-admin-password>\"}"
curl -sk -o /dev/null -w 'wrong password  -> HTTP %{http_code}\n' -X POST https://127.0.0.1/api/login/local \
  -H 'Content-Type: application/json' --data '{"username":"admin","password":"wrong-nope"}'
per-VM password -> HTTP 200
wrong password  -> HTTP 403

Secure by default: the per-VM info file and a live login round-trip

Step 6 - First login

Open Belenios in your browser (accept the self-signed certificate warning, or install a trusted certificate first - see below), then choose local login:

https://<vm-public-ip>/admin

Enter the administrator username (admin) and the BELENIOS_ADMIN_PASSWORD from Step 5, accept the terms of service on first login, and you reach the administration home. Change your password from your account menu after your first sign-in.

The Belenios administration home with your elections

Step 7 - Create a verifiable election

From the administration home choose Create a new election, give it a name, then work through the setup: define your question(s) and answers, add your voters (by e-mail), let the credential authority generate voting credentials, confirm the trustee setup, and open the election for voting. Each election gets its own page with a cryptographic fingerprint, the list of trustees whose keys are needed to decrypt the result, and a See accepted ballots link - this is the public audit and verification surface that makes the result independently checkable.

A Belenios election page with its verification data and audit link

Voters open the election link and vote in the booth: they enter their credential, answer the question, review and encrypt their ballot, authenticate, and confirm - the ballot is encrypted in the browser before it is ever sent to the server.

The Belenios voting booth showing an encrypted ballot

Step 8 - The Belenios server image

Belenios runs from a pinned image built at capture time from the official INRIA release, baked into the VM (no runtime pull), published on the loopback interface only:

sudo docker ps --format 'table {{.Image}}\t{{.Status}}\t{{.Ports}}'
IMAGE            STATUS         PORTS
belenios:3.3.0   Up 6 minutes   127.0.0.1:8001->8001/tcp

The pinned Belenios image and the loopback-published service

Voter e-mail (credentials and notifications)

Belenios sends voters their credentials and notifications by e-mail. By default this image queues outgoing mail to a log (/var/lib/belenios/var/log/outgoing-mail.log) so the server never blocks on delivery and you can inspect exactly what would be sent. For production, wire a real SMTP relay: drop an executable sendmail-compatible relay at /var/lib/belenios/relay-sendmail (for example an msmtp wrapper configured with your provider), and set your own domain in /var/lib/belenios/ocsigenserver.conf.in before restarting sudo systemctl restart belenios.

Security updates

The image is captured fully patched (including Ubuntu phased updates) and unattended-upgrades stays enabled, so security patches keep flowing on your VM. There should be no held-back packages:

apt-mark showhold

The OS security baseline and the running Belenios stack

Your data

Belenios stores its election spool, accounts and password database under /var/lib/belenios:

sudo du -sh /var/lib/belenios/var

The election spool holds cryptographic material for every election. Snapshot the VM's OS disk in Azure to back up your elections, or copy /var/lib/belenios/var to external storage on a schedule.

Enabling a trusted TLS certificate

The image ships a per-VM self-signed certificate so HTTPS works out of the box; browsers will warn until you install a trusted certificate. Because election URLs are cryptographically baked into election data, install a trusted certificate before you create real elections. Point a DNS A record at the VM's public IP, ensure 443/tcp is open in the NSG, then install certbot and let it manage the nginx certificate. Replace the placeholders with your own domain and email:

sudo apt-get update
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.com -m you@example.com --agree-tos

certbot configures the HTTPS server block and sets up automatic renewal. After it completes, set the <public-url prefix="https://your-domain.example.com"/> line in /var/lib/belenios/ocsigenserver.conf.in and run sudo systemctl restart belenios, and Belenios is available at your domain with a trusted certificate.

Upgrading Belenios

Belenios runs from a pinned image. cloudimg publishes updated images as new Belenios releases are certified; migrate by deploying the new image and restoring /var/lib/belenios/var. Always snapshot the OS disk first. cloudimg support can assist with planning and performing upgrades.

Support

This image is backed by 24/7 cloudimg support covering deployment, upgrades, SMTP integration, TLS termination and election administration. Contact us by email and chat.

Belenios is a trademark of its respective owner. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.