Elekto is a DIY service, so in order to start using it, you’re going to have to install it:
And then regardless of how you installed it:
This the multi-page printable view of this section. Click here to print.
Elekto is a DIY service, so in order to start using it, you’re going to have to install it:
And then regardless of how you installed it:
This guide will help you create a development installation of Elekto on your laptop, for hacking and testing purposes.
The application is written in Python using Flask and SQLAlchemy. This repository ships a requirements.txt
and an environment.yml
for conda users.
# Installation with pip
pip install -r requirements.txt
# Installation with Conda
conda env create -f environment.yml && conda activate elekto
The repository has a .env.example
file which can be used as a template for .env
file, update the environment file after copying from .env.example
.
# create a new .env file from .env.example
cp .env.example .env
Set the basic information about the application in the upper section
APP_NAME=k8s.elections # set the name of the application
APP_ENV=development # development | production
APP_KEY= # random secret key (!! important !!)
APP_DEBUG=True # True | False (production)
APP_URL=http://localhost # Url where the application is hosted
APP_PORT=5000 # Default Running port for development
APP_HOST=localhost # Default Host for developmemt
Update the database credentials,
DB_CONNECTION=mysql # Mysql is only supported
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=name
DB_USERNAME=user
DB_PASSWORD=password
Update the meta repository info
META_REPO=https://github.com/elekto-io/elekto.meta.test.git
META_DEPLOYMENT=local
META_PATH=meta
META_BRANCH=main
META_SECRET=db5a951969c379e75d0bf15ad6ff8b4a36fbeb02 # same as webhook of the same meta repository
Update the Oauth info, create a GitHub Oauth app if already not created.
GITHUB_REDIRECT=/oauth/github/callback
GITHUB_CLIENT_ID=d79f002c1d2e3cf20521
GITHUB_CLIENT_SECRET=2f64fff6612c46f87314ad5bb81d05c8fd29c561
The console
script in the repository is used to perform all the table creations and syncing of the meta files.
# to migrate the database from the command line
python console --migrate
To sync the database with the meta files
# to the sync the database with the meta
python console --sync
The flask server will start on 5000
by default but can be changed using --port
option.
# to run the server on default configs
python console --run
# to change host and port
python console --port 8080 --host 0.0.0.0 --run
This guide walks you through installing Elekto by hand on a server or VM.
For Elekto to run in production, you need the following small application stack:
Elekto can be installed either as a native application or as a container. The instructions below cover installation as a native application. For installation in a container, see Kubernetes installation
Elekto is a Python/Flask/uWSGI application developed in Python 3. Building it from source requires the following prerequisites, which should be installed using your OS’s packaging system:
Having installed those prerequisites, you can now install the python requirements using Pip, which will build the python source:
# Installation with pip
pip install -r requirements.txt
# Installation with Conda
conda env create -f environment.yml && conda activate elekto
Please check the above build process carefully for error messages; the only acceptable errors are (a) warnings about out-of-date versions and (b) missing database libraries for the databases you’re not using.
It is possible that you could run Elekto using fastcgi instead of uWSGI, but at this time we have no documentation on how to do this.
Github stores ballots and some metadata in a designated SQL database, which is up to you to install and run. Currently, Elekto supports the following:
However, as Elekto uses SQLAlchemy, it can potentially support any SQL database that SQLAlchemy supports. Adding new databases will require a PR to Elekto.
Database requirements are very light, so it is completely feasible to run the database on the same server as the python application. The database server needs less than 2 CPUs, 1GB memory, and 25GB storage. You can also use a cloud database service; the included database support was chosen specifically because there are multiple cloud database services available. In that case, you are likely to use the smallest size of cloud database available.
The Elekto database user needs to have permission to create tables. The database must be configured with user/password login. Other forms of authentication are not yet supported.
Here’s an example of doing this on PostgreSQL:
postgres=# create role elekto with login password 'TEST';
CREATE ROLE
postgres=# create database elekto owner elekto;
CREATE DATABASE
The Elekto application will not run if the database is unavailable. The ballot data contained in the database is not stored anywhere else, and as such is unrecoverable if the database is lost. For this reason, it is up to the administrator to set up and manage backups and high availability. This is particularly a concern for SQLite, which is an embedded database; you will need to set up cron jobs on the server to back this up.
Elekto runs in the python uWSGI web application server. uWSGI is not very scalable, though, and does not handle SSL connections. As such, for anything other than developer mode, we recommend that you put a web server in front of it.
Nginx works well for this, whether as a standalone or as part of Kubernetes Ingress. See the sample Nginx configuration in the installation directory of the Elekto repository for an example setup. If running directly on a host with an Nginx proxy, you’ll want to run Eletko in “socket” connection mode. Other web servers would also work, but Nginx is the only sample configuration supplied.
On Kubernetes, you’ll want to access Elekto via Ingress. See installation/deployment
for an example of this. In a Kubernetes setup, you want to run Elekto in http
mode.
Elekto’s workflow is GitOps-based. This means that to use Elekto, you must have a GitHub (GitLab TBD) repository for Elekto to attach to. This must be a repository you own and have administration rights on, as you will be setting up a webhook and directories.
It does not need to be a repository that’s exclusively dedicated to Elekto. Most organizations using Elekto place its election metadata into a subdirectory of a repository that’s used for other community documents (e.g. knative/community/elections
). Given that Elekto will refresh for every webhook push, however, it’s probably better if it’s not a repository that gets multiple commits per hour.
See Configuration for how to set up this GitHub repository, and the Administration Guide for what files go into it.
Elekto does not maintain user authentication; by design, it relies on an outside Oauth source for user login. Currently the only Oauth source supported is GitHub. If you want to add other sources, contributions are very welcome.
See Configuration for how to configure this in GitHub.
Elekto does not require elevated privileges to run, so for security, we recommend running it under an elekto
, www
, or python
application account with restricted permissions.
Elekto caches a copy of the election respository on disk, and as such needs a file location to which it can write, with storage equal to the storage size of a git clone of that repository.
If you’ve completed everything in Installation, please proceed to Configuration.
Elekto’s configuration is divided into three parts:
This document covers the first two, which consists of the items that need to be configured before Elekto will run. The third part is covered in the [Administrator Guide].
Elekto relies on an external Oauth provider for user authentication, by design. This provider must be configured to accept authentication requests from Elekto. You may only have one Oauth provider per Elekto instance, as authentication happens before selecting an election.
You must also configure the repository that contains the election metadata (hereafter “the repo”) to push changes to Elekto.
Currently, the only Oauth provider offered is GitHub. Contributions of additional providers are extremely welcome.
You must set up an “Oauth Application” that Elekto can use. In GitHub, this is under Settings–>Developer Tools–>Oauth Applications. Note that Oauth Apps are belong to accounts rather than organizations, so you’ll want to set up an account with shared access in your infra team. We also recommend setting up a separate Oauth App for Elekto rather than re-using ones created for other purposes, and giving each Elekto instance its own secret key.
The Oauth App must have the following settings:
https://your.elekto.domain/oauth/github/callback
(note that this can be changed in ENV)Once you create the Oauth App, GitHub will create a ClientID for it, which you populate in GITHUB_CLIENT_ID in ENV. You then create a new Oauth secret under the app and copy the value for that, and that gets populated in GITHUB_CLIENT_SECRET.
To receive changes from the repo, you need to add a webhook that pushes changes whenever you merge. Webhooks are under “settings” for the individual repository (which also means you must be a repo owner).
The Webhook should have the following settings:
https://your.election.domain/v1/webhooks/meta/sync
This “secret” is an arbitrary string that authenticates the push to the Elekto server. It can be any string, such as one created by a password generator or a passphrase you like. This string then gets set in META_SECRET.
Elekto is designed to accept its runtime configuration as ENV variables in its shell environment. A sample .env.example
file can be found in the base directory of the Elekto source. These ENV configuration variables are not expected to change frequently, or at all, for any particular running Elekto instance. Changing them generally requires restarting Elekto.
All of these env variables need to be set before starting Elekto as a uWSGI application, or even in developer mode; without them, Elekto will error out and refuse to start. You can set this up however you please:
.bashrc
for the elekto application userSince we use ENV for Elekto configuration, this does mean that Elekto must be launched under a shell.
What follows are the ENV variables that must be set for Elekto to run.
APP_NAME
Required Name of the Elekto instance you are running. Displays in some parts of the UI. For user information only.
Example: k8s.elections
APP_ENV
Optional Status of this Elekto instance, for CI/CD purposes. Not used internally by Elekto.
Example: production
or development
APP_KEY
Optional Encryption seed for application cookies. Most users should leave blank, in which case it will be set automatically as a random 8-byte key by the startup code. Set manually to an 8-16 character key if you need to run multiple Elekto instances for the same organization.
Example: 2400229255
APP_DEBUG
Required Whether to run in Debug mode for troubleshooting purposes. In Debug mode, will output stack traces for errors and reload templates.
Example: True
or False
APP_URL
Optional URL of the Elekto instance. Used by some uWSGI and/or Nginx configurations. Not used internally by Elekto.
Example: http://elections.knative.dev
APP_PORT
Optional Used in some uWSGI start scripts, and when running Flask in standalone mode. Port on which to serve Elekto.
Example: 5000
APP_HOST
Optional Used in some webserver startup scripts, and by the Flask server in standalone mode. Name of the host served by this Elekto instance.
Example: localhost
APP_CONNECT
Optional Whether to serve uWSGI over HTTP or via a local Unix socket. Used by some startup scripts; see entrypoint.sh
for an example.
Example: http
or socket
MIN_PASSCODE_LENGTH
Optional: Integer. Minimum length for a voter-ballot passphrase in order to deter hacking. Set to 6 if left blank.
Example: 8
DB_CONNECTION
Required Which database connection type to use. Currently only PostgreSQL, MySQL, and SQLite backends are supported.
Example: postgresql
, mysql
, or sqlite
DB_HOST
Required The URL, IP, or hostname of the database server. Ignored if using SQLite (set to N/A
).
Example: pgdb-1a.prod.elekto.dev
DB_PORT
Required The network port for the database server. Ignored if using SQLite (set to 1001
).
Example: 3306
DB_PATH
Optional Used only for SQLite. Path to the database filesystem.
Example: /var/db/elekto-db
DB_USERNAME
Required Login user for the Elekto database. Not used by SQLite.
Example: elekto
DB_PASSWORD
Required Authentication password for the Elekto database. Not used by SQLite.
Example: 1a6e4b3f55dc
META_REPO
Required GIT clone URL for the repository which contains the election configurations. Should be the .git
link, not the .html
one.
Example: https://github.com/kalkayan/k8s.elections.meta.git
ELECTION_DIR
Required Directory, relative to the Git repository root, containing the set of election subdirectories. Must be a parent directory. Can be an arbitrarily deep path. Do not use a leading or trailing /
.
Example: elections
, gov/steering/elections
META_DEPLOYMENT
Optional Reserved for future advanced configuration use.
Example: local
, sidecar
META_PATH
Required Local file location at which to store a clone of the election data repository. This directory will be created by Elekto at sync time, so the application must have the ability to write to the parent directory. The directory may be absolute or relative; if it is relative, it will be under the Eletko source directory. Otherwise it defaults to meta
. For containers, a directory under /tmp
is recommended to make sure the location is writeable.
Example: meta
, /tmp/meta
META_BRANCH
Required Which git branch has the merged version of the election data. Defaults to main
.
Example: main
, master
, prod
META_SECRET
Required The shared secret string used in the Github webhook for updates of the election data repository. Can be set to anything you like; we recommend a random md5 string or generated password.
Example: 92a488d11c0d27bbfea0a97e3332e08a
At this time, there are only settings available for GitHub because other Oauth sources haven’t been implemented. When other sources get added to Elekto, each will get its own configuration variables.
GITHUB_REDIRECT
Required Flask path for the GitHub authentication callback. Defaults to /oauth/github/callback
, and there’s no good reason to change it.
Example: /oauth/github/callback
GITHUB_CLIENT_ID
Required The Client ID from the GitHub Oauth Application.
Example: 0b9138c0b2ffeefd9fc1
GITHUB_CLIENT_SECRET
Required The Client Secret from the GitHub Oauth Application.
Example: dd37278f8e9d57571590ad9288f0aae62228c2e8
This guide walks you through installing Elekto on a Kubernetes cluster.
TBD.
In most cases, you should be able to upgrade Elekto just by replacing the Python/Flask code or the container image and restarting the service. The cases where that is not true are documented below.
If you are running Elekto as Python code, you should also update the dependencies every time you upgrade, as we usually upgrade python library versions to current ones.
pip3 install -r requirements.txt
This Upgrade Requires Extra Steps
Version 0.6 involves several major changes to the database schema which require a multi-step database migration. At this time, this process is only automated for PostgreSQL.
The Elekto Project Recommends Backing Up Your Database Before Upgrading
For users on PostgreSQL, the process should be as simple as:
python console --migrate
If you are using the official container image, or others built like it, then the container image should automatically do the above when you delete and replace the container or pod.
This may fail on PostgreSQL, either because you have modified your database schema after creation by Elekto, or because the Elekto database user does not have permissions to modify tables. In either case, you may need to run the database migration manually. If so, you will need to run the following SQL statements as the database owner or superuser:
CREATE TABLE schema_version ( version INT PRIMARY KEY );
INSERT INTO schema_version VALUES ( 2 );
ALTER TABLE voter ADD COLUMN salt BYTEA, ADD COLUMN ballot_id BYTEA;
CREATE INDEX voter_election_id ON voter(election_id);
ALTER TABLE ballot DROP COLUMN created_at, DROP COLUMN updated_at;
ALTER TABLE ballot DROP CONSTRAINT ballot_pkey;
ALTER TABLE ballot ALTER COLUMN id TYPE CHAR(32) USING to_char(id , 'FM00000000000000000000000000000000');
ALTER TABLE ballot ALTER COLUMN id DROP DEFAULT;
ALTER TABLE ballot ADD CONSTRAINT ballot_pkey PRIMARY KEY ( id );
CREATE INDEX ballot_election_id ON ballot(election_id);
We do not yet have instructions for upgrading MySQL or SQLite. Those are in development. If you have an Elekto/MySQL instance, please contact us via Elekto slack channel on CNCF Slack, or by commenting on the issue; we would like to work with you on this.
If you do not care about preserving election history, your other option is
to simply delete the old database and create a new, empty one. In that case,
Elekto will generate a new, updated schema through console --migrate
.