Radicale on FreeBSD

by Tykling


05. dec 2017 11:25 UTC


I've been working to replace my OwnCloud installation with something else. I use the Calendar part of OwnCloud a lot, and this post is about replacing the CalDAV bits of OwnCloud with Radicale, a Python based CalDAV and CardDAV server.

I use nginx and supervisord so I will start out by showing the config for those before getting deeper into Radicale.

Nginx and Authentication

I run all my webstuff through a reverse proxy which takes care of terminating TLS and IPv6 and passes the request to the backend jail running the service in question. This means that the configuration of the backend webserver in the service jail can be kept very simple:

        location / {
             # regular proxy stuff
             proxy_pass http://127.0.0.1:5232;
             proxy_http_version 1.1;
             proxy_set_header Host $http_host;
             proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

             # radicale http_x_remote_user config
             proxy_set_header X-Remote-User $remote_user;
             auth_basic "Radicale - Password Required";
             auth_basic_user_file "/usr/local/etc/nginx/radicale-htpasswd";
         }

I read in the Radicale reverse proxy documentation about the auth method called http_x_remote_user which sounds perfect since I can let nginx handle the authentication with regular HTTP Basic Auth. This was actually the reason I had to switch to the 2.x version of Radicale since 1.x doesn't support http_x_remote_user.

Supervisord

I am becoming a big fan of running stuff with Supervisord. Although software should be stable it is nice to have "something" keeping an eye on things, restarting as needed. But that is not all Supervisord does, it also handles logging very well. The following block (Ansible generated of course) is what I use to run Radicale and redirect console logs to syslog:

[program:radicale]
command=/usr/local/virtualenv/bin/python /usr/local/virtualenv/bin/radicale -C /usr/local/etc/radicale/config
user=radicale
stdout_logfile=syslog
stderr_logfile=syslog
startsecs=5
numprocs=1

To make Radicale not daemonize I had to add daemon = False to the [server] section of the config. Also note that I am calling the virtualenv Python directly and that the Radicale executable is also inside the virtualenv.

Radicale version

I started out (as one does) with Radicale from the FreeBSD Ports tree, which at the time of writing (December 2017) was at version 1.1.2. I was forced to switch to installing Radicale from pip instead to get the 2.x version. I will change that back to installing from FreeBSD Ports when the new Radicale arrives in Ports.

Radicale Config

All this boils down to the following config file:

[server]
daemon = False

[auth]
type = http_x_remote_user

[rights]
type = from_file
file = /usr/local/etc/radicale/rights

[storage]
filesystem_folder = /usr/local/share/radicale/collections

[logging]
config = /usr/local/etc/radicale/logging

Logging

Since Supervisord is configured to redirect both stdout and stderr to syslog I don't need Radicale to log to files or syslog by itself, it should just log to console and let Supervisord handle the rest. The following logging config accomplishes that:

[loggers]
keys = root

[handlers]
keys = console

[formatters]
keys = full

[logger_root]
level = DEBUG
handlers = console

[handler_console]
class = StreamHandler
# change this to DEBUG for more verbosity
level = INFO
args = (sys.stdout,)
formatter = full

[formatter_full]
format = %(asctime)s - %(levelname)s: %(message)s

Radicale used Pythons default logging framework and actually manages to distribute it in a fairly understandable and flexible way, well done. Documentation here.

Rights and Calendar Sharing

Radicale has a pretty flexible rights system. It can be configured in a couple of different ways, I went with from_file with the following rights file:

# Authenticated users can read and write their own collections.
[owner-write]
user = .+
collection = %(login)s(/.*)?
permission = rw

# Everyone can read the root collection
[read]
user = .*
collection =
permission = r

This just means that each user can read and write their own collections. To configure sharing add a section like this:

[tykling-read-someones-calendars]
user = tykling
collection = someone(/.*)?
permission = r

This would allow the user tykling to access all collections under the username someone. Read more about configuring rights in their rights documentation.

Radicale Storage

The Radicale directory structure is pretty simple and it saves it's data as .ics files. Here is the structure with an example calender with two events created:

$ sudo find /usr/local/share/radicale/collections
/usr/local/share/radicale/collections
/usr/local/share/radicale/collections/.Radicale.lock
/usr/local/share/radicale/collections/collection-root
/usr/local/share/radicale/collections/collection-root/tykling
/usr/local/share/radicale/collections/collection-root/tykling/5405a11d-52e8-0f00-e540-feba9927044d
/usr/local/share/radicale/collections/collection-root/tykling/5405a11d-52e8-0f00-e540-feba9927044d/29d56410-4b59-4cc3-95ea-cf167bfb24b2.ics
/usr/local/share/radicale/collections/collection-root/tykling/5405a11d-52e8-0f00-e540-feba9927044d/.Radicale.cache
/usr/local/share/radicale/collections/collection-root/tykling/5405a11d-52e8-0f00-e540-feba9927044d/.Radicale.cache/history
/usr/local/share/radicale/collections/collection-root/tykling/5405a11d-52e8-0f00-e540-feba9927044d/.Radicale.cache/history/29d56410-4b59-4cc3-95ea-cf167bfb24b2.ics
/usr/local/share/radicale/collections/collection-root/tykling/5405a11d-52e8-0f00-e540-feba9927044d/.Radicale.cache/history/8a6f1c57-aff6-4645-b483-10c99f61f866.ics
/usr/local/share/radicale/collections/collection-root/tykling/5405a11d-52e8-0f00-e540-feba9927044d/.Radicale.cache/item
/usr/local/share/radicale/collections/collection-root/tykling/5405a11d-52e8-0f00-e540-feba9927044d/.Radicale.cache/item/29d56410-4b59-4cc3-95ea-cf167bfb24b2.ics
/usr/local/share/radicale/collections/collection-root/tykling/5405a11d-52e8-0f00-e540-feba9927044d/.Radicale.cache/item/8a6f1c57-aff6-4645-b483-10c99f61f866.ics
/usr/local/share/radicale/collections/collection-root/tykling/5405a11d-52e8-0f00-e540-feba9927044d/.Radicale.cache/sync-token
/usr/local/share/radicale/collections/collection-root/tykling/5405a11d-52e8-0f00-e540-feba9927044d/.Radicale.cache/sync-token/d41d8cd98f00b204e9800998ecf8427e
/usr/local/share/radicale/collections/collection-root/tykling/5405a11d-52e8-0f00-e540-feba9927044d/.Radicale.props
/usr/local/share/radicale/collections/collection-root/tykling/5405a11d-52e8-0f00-e540-feba9927044d/8a6f1c57-aff6-4645-b483-10c99f61f866.ics

Creating Users and Calendars

For a while I didn't realise that Radicale has a webinterface to create users and collections in the filesystem. Since I authenticate users in nginx I first have to add the username and password in the htpasswd file there of course, but then I go to the Radicale web interface with a webbrowser and log on, and it shows me a simple view where I can choose to Create new addressbook or calendar. After creating a calendar I can copy the full URL including UUID for that collection and use that in when configuring Lightning or other clients.

Importing Existing Calendars

When searching for ways to import an existing calender (an ICS file exported from OwnCloud in my case) I found that the advice was just to drop the ICS file into the folder with the rest of the ICS files and it would kindof just work. But it didn't:

Dec  5 21:24:38 radicale1 supervisord: radicale_00 2017-12-05 21:24:38,470 - ERROR: An exception occurred during PROPFIND request on '/tykling/a5196bd5-f2fe-82dc-8e6a-81cefa92b4b6/': Failed to load item 'export.ics' in 'tykling/a5196bd5-f2fe-82dc-8e6a-81cefa92b4b6': Muliple VEVENT components with different UIDs in
 object: '2be379d9-3ef0-4749-a14e-bd11028b5675', '08fd2497-c73d-46f5-b25d-7888d1ed8f27'  

As you can see further up Radicale creates one (1) .ics file per event. The ICS format obviously supports multiple events in a single file, but support for this appears to have been removed from the Radicale 2.x series.

So I used this alternative method to import my ICS:

cat export.ics | curl -u 'tykling:password' -X PUT https://cal.tyk.nu/tykling/a5196bd5-f2fe-82dc-8e6a-81cefa92b4b6/ --data-binary @-

This took around 10 seconds for a ~100kb ICS file, and resulted in Radicale creating a whole bunch of ICS files. Perfect.

Clients

Thunderbird comes with Lightning built-in these days, and it works perfectly with Radicale. Just add a network calendar, paste in the URL to the collection, enter credentials, done. Thunderbird does not appear to support the calendar coloring thing where you can configure the color in Radicale and the CalDAV clients should show the calendar in that color. But I'll live :)

I run Copperhead OS on my phone so I only have access to the F-Droid app-store so I was happy to see that I still had some options wrt. CalDAV clients. I am currently using the "built-in" Etar calendar app to show/use the calendar, and Davdroid to synchhronise with the CalDAV server. This works very well, and it was even able to show a list of the calendars created under my user so I didn't have to enter the full URL. Fancy.

Search this blog

Tags for this blogpost