Setup of an Etesync Server on Fedora 37

Etesync is a server-client system that allows syncing contact and calendar data in an end-to-end-encrypted (E2EE) fashion. This way you don't need to trust the server administrator and his ability to protect your data from himself, hackers, or other entities. Note that data is mostly securely transmitted via TLS/HTTPS between client and server nowadays. But with only client-server encryption you would still need to trust certain entities, which is not 100 percent security. E2EE gives you that 100 percent.

This concludes that with E2EE all you need is an open source client. You then can choose any service provider, and don't need to select carefully, or don't need to set up your own server. As explained, by using an open source E2EE client you don't need to trust the server (apart for some meta data for synchronisation that needs to stay unencrypted). But if you choose to set up your own server anyways, here is the step-by-step manual.

Wording in this tutorial

string

meaning

foo.net

domain

xxxxxxxxx

any string

xx.xx.xxx.xx

IPv4 address

xxxx:xxx:xxxx:xxxx::1

IPv6 address

Etebase

# Create user
sudo useradd etebase
sudo passwd etebase

# Dependencies
sudo dnf install python3-devel

# Tools
sudo dnf install argon2

# Download
mkdir srv
cd srv
git clone https://github.com/etesync/server.git etebase

# Virtual Environment
sudo su etebase
mkdir ~/pyenv
python -m venv ~/pyenv/etebase
source ~/pyenv/etebase/bin/activate
pip install pip --upgrade

# Patch requirements.txt
# Only for Fedora 37/Python 3.11. Reason: Older versions of those packages
# throw compile errors during PIP installation.
sed -i 's/^uvloop==0.16.0$/uvloop==0.17.0/g' ~/srv/etebase/requirements.txt
sed -i 's/^httptools==0.4.0$/httptools==0.5.0/g' ~/srv/etebase/requirements.txt
sed -i 's/^cffi==1.15.0$/cffi==1.15.1/g' ~/srv/etebase/requirements.txt
# Would cause an error when starting server.
sed -i 's/^pydantic==1.9.0$/pydantic==1.10.2/g' ~/srv/etebase/requirements.txt

# Install
cd ~/srv/etebase
pip install -r requirements.txt

# Configure
cp etebase-server.ini.example etebase-server.ini
# (Exit etebase user and go back to normal user.)
exit
sudo mkdir -p /var/www/etebase/
sudo chown etebase /var/www/etebase/
sudo chgrp etebase /var/www/etebase/
sudo su etebase
mkdir /var/www/etebase/static
mkdir /var/www/etebase/media
sed -i 's/^static_root = .*$/static_root = \/var\/www\/etebase\/static/g' etebase-server.ini
sed -i 's/^media_root = .*$/media_root = \/var\/www\/etebase\/media/g' etebase-server.ini
date +%s | argon2 udita-%a3ra -id -r > secret.txt
sed -i s/^allowed_host1 = .*$/allowed_host1 = pim.foo.net/g etebase-server.ini

Firewall

# NOTE: This port is just temporary until HTTP is configured.
sudo ufw allow 8000/tcp

Start it up:

sudo su etebase
cd ~/srv/etebase/
source ~/pyenv/etebase/bin/activate
uvicorn etebase_server.asgi:application --port 8000 --host 0.0.0.0

Visit http://xx.xx.xxx.xx:8000/. It should say "It works!".

Nginx

[https://github.com/etesync/server/wiki/Production-setup-using-Nginx]

# Install
sudo dnf install nginx

# To have the server start at each boot:
sudo systemctl enable nginx.service

# To start the web server now:
sudo systemctl start nginx.service

# Prepare Django instance:
./manage.py collectstatic

Create sudo nvim /etc/nginx/conf.d/pim.foo.net.conf (don't omit the .conf ending!), and edit:

  • server_name

  • client_max_body_size

  • alias of location /static/

=> Full sudo nvim /etc/nginx/conf.d/pim.foo.net.conf:

# the upstream component that nginx needs to connect to
upstream etebase {
    # server unix:///var/sockets/etebase_server.sock; # for a file socket
    server 127.0.0.1:8001;  # for a web port socket (we'll use this first)
}

# configuration of the server
server {
    # the port your site will be served on
    listen      8000;
    # the domain name it will serve for
    server_name 65.21.187.48 pim.foo.net;    # substitute your machine's IP address or domain name
    charset     utf-8;

    # max upload size
    client_max_body_size 200M;               # adjust to taste

    location /static {                       # /static not /static/
        alias /var/www/etebase/static;       # Project's static files
    }

    location / {
       proxy_pass http://etebase;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $server_name;
    }
}
# Test
sudo nginx -t

# Restart
sudo systemctl restart nginx

# Start Gateway to Django instance
sudo su etebase
cd ~/srv/etebase/
source ~/pyenv/etebase/bin/activate
uvicorn etebase_server.asgi:application --port 8001 --host 0.0.0.0

# (Test both IP address and domain in web browser.)

# Change configuration to file socket

Nginx via file socket

Prepare directory for socket files (standart is /tmp, which cannot be read by nginx user, if created by etebase user! [https://stackoverflow.com/a/22277617])

mkdir /var/sockets
chmod 777 /var/sockets

Change sudo nvim /etc/nginx/conf.d/pim.foo.net.conf:

# the upstream component that nginx needs to connect to
upstream etebase {
    server unix:///var/sockets/etebase_server.sock; # for a file socket
    # server 127.0.0.1:8001; # for a web port socket (we'll use this first)
}
# Test, restart
sudo nginx -t
sudo systemctl restart nginx

# Just as above, start gateway to Django instance
sudo su etebase
cd ~/srv/etebase/
source ~/pyenv/etebase/bin/activate
uvicorn etebase_server.asgi:application --uds /var/sockets/etebase_server.sock

Run Uvicorn at boot

Test manually step in [Service] (becoming sudo su etebase, switching dir, starting up)! Here may be errors difficult to find later.

sudo nvim /etc/systemd/system/etebase_server.service:

[Unit]
Description=Execute the etebase server.

[Service]
User=etebase
WorkingDirectory=/home/etebase/srv/etebase/
ExecStart=/home/etebase/pyenv/etebase/bin/uvicorn etebase_server.asgi:application --uds /var/sockets/etebase_server.sock

[Install]
WantedBy=multi-user.target
sudo systemctl start etebase_server
sudo systemctl enable etebase_server
# After editing, a reload is necessary (restart not sufficient):
sudo systemctl daemon-reload

Check on http://pim.foo.net:8000/, it should show the "It works" message.

HTTPS

[https://github.com/etesync/server/wiki/Setup-HTTPS-for-Etebase]

# Install certificates
sudo dnf install certbot
sudo dnf install python3-certbot-nginx
sudo ufw allow 80/tcp
sudo certbot --nginx
sudo ufw delete allow 80/tcp
sudo ufw reload
sudo ufw status

# Firewall
sudo ufw allow 443/tcp
sudo ufw reload
# Port 8000 not needed anymore
sudo ufw delete allow 8000/tcp
sudo ufw reload
sudo ufw status

Go to https://pim.foo.net/ and check if "It works!" is shown.

Administration

Create a superuser with ./manage.py createsuperuser.

Login at https://pim.foo.net/admin

Create a user, e.g. xxxxxxxxx.

Go to the mobile phone app, choose "login", enter xxxxxxxxx as username, the password, and https://pim.foo.net/ as the server. You should be logged in now.