Radicale
Author: | akeil |
---|---|
Date: | 2013-10-20 |
Version: | 1 |
radicale [1] is an open source server for calendars and contacts written in python.
Although radicale can run as a standalone solution, it is recommended to run it inside a HTTP server (especially if is made available through the internet).
This is a description of a radicale setup on Arch Linux using nginx [2] as a HTTP server and gunicorn [3] as a python application server.
When complete, we can run our own server to store calendar and contacts data and make them available via CalDAV and CardDAV. Access to the calendars and address books is protected by passwords and the calendar/contact data is backed up regularly.
Set up nginx
Install nginx
On Arch Linux nginx can be installed through the package manager:
# pacman -Sy nginx
To make it start on boot:
# systemctl enable nginx
More information can be found in the Arch Wiki for nginx [6].
Configure nginx
Configuration files are found in /etc/nginx/. The main configuration is /etc/nginx/nginx.conf.
To reload configuration changes while nginx is running:
# nginx -s reload
To use nginx as a proxy for gunicorn, use the following config:
http { upstream radicale_app { server 127.0.0.1:8000 fail_timeout=0; } server { listen 5232; } location @proxy_to_app { proxy_pass http://radicale_app; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
This assumes that gunicorn listens on its default port 8000. nginx will listen to requests on port 5232 and forward them to the gunicorn instance listening on localhost:8000.
Logging
The log files will be stored in /var/log/nginx which contains access.log and error.log:
/var/log/nginx/access.log /var/log/nginx/error.log
gunicorn and radicale
Install gunicorn and radicale
virtualenv [4] is used to keep the gunicorn and radicale installation separate from the system-wide python installation. If virtualenv is not installed already, it should be available through the package manager.
Now create a virtualenv and install gunicorn and radicale inside it.
# cd /opt/venvs # virtualenv radicale # source radicale/bin/activate # (radicale) pip install gunicorn radicale
This assumes that you would like to keep your virtualenv for radicale under /opt/venvs/radicale. Any other location will work as well.
Note
When installed with pip inside a virtualenv, the application is not updated through the package manager. To upgrade manually, type:
Create the WSGI-app for gunicorn:
# cd radicale # touch radicale_app.py
and edit as follows:
# radicale_app.py #----------------------------------------------------- #-*- coding: utf-8 -*- import radicale radicale.log.start() application = radicale.Application()
To manually start gunicorn with the radicale app type (inside the virtualenv):
# (radicale) bin/gunicorn -w 4 radicale_app
or (outside the virtualenv):
# /opt/venvs/radicale/bin/gunicorn -w 4 --chdir /opt/venvs/radicale radicale_app
This will start gunicorn listening on localhost:8000 and using 4 workers.
To test the setup type:
$ curl localhost:8000
You should see something like this:
<!DOCTYPE html> <title>Radicale</title>Radicale Works!
User and Group
In order to control permissions for the radicale-app, create a user and group for radicale:
# groupadd radicale # useradd -g=radicale --shell=/bin/false --home-dir / radicale
Service File
To control the radicale application with systemd create a .service file for it:
# /usr/lib/systemd/system/radicale.service #---------------------------------------------- [Unit] Description=radicale daemon [Service] User=radicale WorkingDirectory=/opt/venvs/radicale ExecStart=/opt/venvs/radicale/bin/gunicorn -w 2 radicale_app [Install] WantedBy=multi-user.target
Start the radicale service on boot:
# systemctl enable radicale
Configure radicale
Radicale will look for configuration files in /etc/radicale/. After installation, the configuration files does not exist and default settings are used. To adjust radicale's config, create the configuration file:
# mkdir /etc/radicale # touch /etc/radicale/config # nano /etc/radicale/config
Edit:
# /etc/radicale/config #----------------------------------------------------- [storage] type = filesystem filesystem_folder = /var/lib/radicale [logging] config = /etc/radicale/logging [rights] type = from_file file = /etc/radicale/rights
This will store calendars and contacts in the file system in the directory var/lib/radicale/. Logging behavior and user permissions are configured in a separate files.
To configure logging, create /etc/radicale/logging which might look like this:
# logging config for radicale # Keys for loggers, handlers and formatters -------------- [loggers] keys = root [handlers] keys = console, file [formatters] keys = simple, full # Loggers and Handlers------------------------------------ [logger_root] level = DEBUG handlers = console, file [handler_console] class = StreamHandler level = INFO args = (sys.stdout, ) formatter = simple [handler_file] class = FileHandler level = DEBUG args = ('/var/log/radicale.log', ) formatter = full # Formatters --------------------------------------------- [formatter_simple] format = %(message)s [formatter_full] format = %(asctime)s - %(levelname)s: %(message)s
Change the permissions on the files required by radicale:
# chown -R radicale:radicale /var/lib/radicale /var/log/radicale.log
See below for a authentication and permissions.
Backup
Assuming the following radicale configuration:
[storage] type = filesystem filesystem_folder = /var/lib/radicale
The backup is simply based on the file system using rdiff [5]. To install rdiff:
# pacman -Sy rdiff-backup
Create a simple cron-job to perform the backup:
#!/bin/sh # /etc/cron.daily/radicale-backup # ---------------------------------------------------- src="/var/lib/radicale" dst="/mnt/storage-1/backup/radicale" /usr/bin/rdiff-backup -v 0 $src $dst
Authentication
Although radicale supports its own authentication facility, it is recommended to use the web server (nginx) for this.
Using nginx basic auth [7] module, add the following to the nginx configuration:
# /etc/nginx/nginx.conf #------------------------------------------------------------------------- ... location @proxy_to_app { auth_basic radicale; auth_basic_user_file htpasswd; ... } ...
The location of the user file is relative to the config file itself. That means it is expected at /etc/nginx/htpasswd.
The nginx error-log will contain an entry if the user file cannot be found.
Create the userfile and make it writable for root only, but readable for nginx (assuming that nginx is run with the default http user):
# cd /etc/nginx # touch htpasswd # chown root:http htpasswd # chmod 640 htpasswd
The password file looks like this:
/etc/nginx/htpasswd ------------------------------------------------------ # comment username:encrypted-password
To encrypt passwords, use openssl passwd command:
$ openssl passwd "the password"
Will output the encrypted password.
Given a user name and a password, this will create the necessary entry in the passwd file:
usergen.sh ------------------------------------------------------ username=$1 passwd=`openssl passwd $2` echo $username:$passwd >> /etc/nginx/htpasswd
Use like this (supply the unencrypted plain-text password):
$ usergen.sh username password
SSL
To use an encrypted connection, a SSL-certificate is required. Normally, these are issued by (and bought from) a Certificate Authority which verifies the identity of the server. Since certificates are expensive, we will use a self-signed certificate. With a self-signed certificate the encryption itself is just as secure but there is no way for the client to verify the identity of the server. Still, good enough for non-professional use.
- The certificate consists of two parts
- the certificate that is sent to the client
- the key which is kept private on the server side.
openssl generates them like this:
# cd /etc/nginx # mkdir ssl # cd ssl # openssl req -days 365 -new -x509 -nodes -out server.cert -keyout server.key # chown http server.key # chmod 0600 server.key
openssl will ask some questions to populate the certificates fields and create two files in the current directory (/etc/nginx/ssl). Make sure that the keyfile is only readable by nginx.
The -days 365 parameter means that the certificate will be valid for 365 days.
Now tell nginx to use SSL with this certificate. Add the following to nginx.conf:
# /etc/nginx/nginx.conf #----------------------------------------------------- server{ listen 5232; ssl on; ssl_certificate ssl/server.cert; ssl_certificate_key ssl/server.key; ... }
In this configuration, the service is only available through an encrypted connection. This may of course cause difficulties for clients that do not accept the self-signed certificate.
Permissions
While authentication is handled by the web server, permissions are managed within the application. radicale allows to set read and write permissions based on the authenticated user (the owner). The owner is derived from a collection's URL. If the URL is http://example.com/joe/calendar.ics, the collection "calendar.ics" belongs to the user "joe".
Therefore, we will allow (via nginx) access to the radicale-URLs to all authenticated users and rely on radicale to control access to the various calendars and contacts.
In the radicale config the [rights] section points to a file which controls permissions:
# /etc/radicale/config #------------------------------------------------------ [rights] type = from_file file = /etc/radicale/rights
By default (that is: without creating the rights-file) read and write access is granted to the owner. Different permissions can be specified in the rights file.
For example:
# /etc/radicale/rights #------------------------------------------------------ [joe/calendar.ics] mary = r
Grants the user "mary" read-permission on "joe"s calendar.
Note
When access is denied for a specific user by radicale that user may receive an authentication prompt repeatedly, even if correct credentials are provided.
nginx will authenticate the user "OK" but radicale will deny access to the requested calendar and ask for authentication.
Public Calendars
Public calendars can be used to provide clients with public holidays or other shared events in a read-only way. You can do this with radicale if you have these calenders in CalDAV format.
To do this, we will create a radicale-user named everyone and copy the public calendars to this user:
# cd /var/lib/radicale # mkdir everyone # cp /your/public/calendars/*.ics everyone # chown -R radicale:radicale everyone
Remember to grant read/write permissions to the radicale user/group.
Then, grant read permissions to the calendars that belong to everyone in radicale-rights:
# /etc/radicale/rights #------------------------------------------------------ [everyone] user1 = r [everyone/holidays.ics] user1 = r
Note
In radicale version 0.8, permissions are granted on a per user basis. There is no way(?) to grant permissions to all users with one directive.
Clients
Evolution
Evolution [8] is a mail-client for Linux which supports CalDAV and CardDAV as a backend.
If a self-signed SSL certificate is used, the "use a secure connection" and the "ignore invalid SSL-certificate" options must be used.
Thunderbird
Thunderbird also works well with CalDAV and CardDAV.
Android
CalDAV-sync [9] and CardDAV-sync [10] are utility-apps for android that can sync calendars and contacts against a server.
[1] | http://radicale.org |
[2] | http://nginx.org/ |
[3] | http://gunicorn.org/ |
[4] | http://www.virtualenv.org/en/latest/index.html |
[5] | http://www.nongnu.org/rdiff-backup/ |
[6] | https://wiki.archlinux.org/index.php/Nginx |
[7] | http://nginx.org/en/docs/http/ngx_http_auth_basic_module.html |
[8] | https://help.gnome.org/users/evolution/3.1/ |
[9] | http://dmfs.org/caldav/ |
[10] | http://dmfs.org/carddav/ |