Self-Hosted Calendar and Addressbook services on OpenBSD

       920 words, 5 minutes

Once you have self-hosted email up and running, you may want to add the Calendar and Addressbook features to your service bag. Nowadays, the standard protocols regarding those subjects are CalDAV and CardDAV.

I decided to go with Baikal , the dedicated CalDAV+CardDAV server based on the sabre/dav framework ; the same framework used in Nextcloud DAV services AFAIK.

It relies on PHP and is available as a package on OpenBSD.

Quoting Baikal’s homepage:

Baïkal is a lightweight CalDAV+CardDAV server. It offers an extensive web interface with easy management of users, address books and calendars. It is fast and simple to install and only needs a basic php capable server. The data can be stored in a MySQL or a SQLite database.

Setup the environment

Fast, simple and using SQLite were the reasons I went for it. On OpenBSD, the installation is really straightforward:

# pkg_add baikal php-pdo_sqlite%8.0

Configure PHP to run as a stand-alone chrooted daemon:

# mkdir /var/www/etc
# cp -p /etc/{hosts,resolv.conf} /var/www/etc/

# cd /etc/php-8.0
# ln -s ../php-8.0.sample/*.ini .

# vi /etc/php-8.0.ini
# vi /etc/php-fpm.conf
(...)
error_log = syslog

# rcctl enable php80_fpm
# rcctl start php80_fpm

I like to have php-fpm logging via syslog. Because this is easier to manage, for me. But this is not a must-have. Also I didn’t have to tweak the php ini file because defaults suits my needs. Your mileage may vary.

Configure httpd(8) to expose the service. Here, Baikal is using a dedicated HTTP Path. There are also well-known URIs that help DAV clients to configure themselves automagically.

# vi /etc/httpd.conf
(...)
  location "/.well-known/ca*dav" {
    block return 301 "https://$HTTP_HOST/baikal/dav.php"
  }
  location "/baikal/Core*" {
    block drop
  }
  location "/baikal/Specific*" {
    block drop
  }
  location "/baikal/config*" {
    block drop
  }
  location "/baikal/*.php*" {
    root "/baikal/html"
    request strip 1
    fastcgi socket "/run/php-fpm.sock"
    directory index index.php
    pass
   }
   location "/baikal/*" {
     root "/baikal/html"
     request strip 1
     directory index index.php
     pass
   }
# rcctl reload httpd

The block directives on (Core|Specific|config) are not strictly required as only files in the baikal/html can be accessed. But those directives simply drop the HTTP session rather than sending 404 errors.

In my particular case, and for some reasons, I don’t have httpd(8) listening on a public IP but use relayd(8). There’s nothing special here expect that you must forward the proper HTTP headers to httpd(8) to have a fully encrypted experience.

# vi /etc/relayd.conf
(...)
match request header append "X-Forwarded-Proto" value "https"
match request header append "X-Forwarded-For"   value "$REMOTE_ADDR"
match request header append "X-Forwarded-By"    value "$SERVER_ADDR:$SERVER_PORT"

Configure Baikal

Browse to the configured URL such as https://<hostname>/baikal/ and follow the configuration instructions.

Baïkal is now installed, and its database properly configured. For security reasons, this installation wizard is now disabled.

Clicking the [ Start using Baikal ] button will direct the Web browser to https://<hostname>/baikal/admin/ where you can create the users, set the passwords and check the calendar and addressbook objects.

Note for backup administrator: in my case, all user data is stored in /var/www/baikal/Specific/db/db.sqlite.

Configure DAV clients

Using that configuration, iOS Calendar and Contacts apps will autodiscover the proper service paths. Simply add a new CalDAV and/or CardDAV account specifying the domain name and credentials.

Thunderbird with TbSync and DAV-4-TbSync extensions is also able to access both calendars and addressbooks. Add a new CalDAV & CardDAV account specifying the domain name and credentials.

Migrate from Nextcloud

As I said earlier, I was using Nextcloud to provide (Cal|Card)DAV services. So I have to copy/move my data to Baikal.

I couldn’t find a way to do it using the iOS apps. And as the URI changes between NC and Baikal, you can’t just switch the service on the server side and expect the apps to behave properly.

Also, the few tests I’ve done using Thunderbird led to various issues. The Contacts lists were not transferred. And selecting all events from all calendars was … unsolved. Maybe I’m just dumb.

I already use vdirsyncer for other purposes. Quoting its homepage

Vdirsyncer is a command-line tool for synchronizing calendars and addressbooks between a variety of servers and the local filesystem.

What I did was simply synchronize two remote servers ; the Nextcloud instance as source, the Baikal instance as destination.

# pkg_add vdirsyncer

% vim ~/.vdirsyncer/config
[general]
status_path = "~/.vdirsyncer/status/"

# CARDDAV
[pair contacts]
a = "contacts_nextcloud"
b = "contacts_baikal"
collections = ["from a", "from b"]
metadata = ["displayname"]

[storage contacts_nextcloud]
type = "carddav"
url = "https://nextcloud/"
username = "changeme"
password.fetch = ["command", "secret-tool", "lookup", "Title", "Nextcloud"]

[storage contacts_baikal]
type = "carddav"
url = "https://fqdn/baikal/dav.php"
username = "changeme"
password.fetch = ["command", "secret-tool", "lookup", "Title", "Baikal"]

# CALDAV
[pair calendriers]
a = "cal_nextcloud"
b = "cal_baikal"
collections = ["from a", "from b"]
metadata = ["displayname", "color"]

[storage cal_nextcloud]
type = "caldav"
url = "https://nextcloud/"
username = "changeme"
password.fetch = ["command", "secret-tool", "lookup", "Title", "Nextcloud"]

[storage cal_baikal]
type = "caldav"
url = "https://fqdn/baikal/dav.php"
username = "changeme"
password.fetch = ["command", "secret-tool", "lookup", "Title", "Baikal"]

% vdirsyncer discover
% vdirsyncer metasync
% vdirsyncer sync

Select whatever password storage you like. As I did the migration from my workstation, I could use KeePassXC as a password management system.

From there, DAV clients will synchronize with Baikal, get the historical data and start updating new. Been using this for a couple of weeks now and, so far, everything works perfectly ok. Same user experience than using Nextcloud.

Like HAL said: Affirmative, DAVe. I read you.