Radicale CalDAV und CardDAV Server installieren

Minimalistischer CalDAV und CardDAV Server mit Bcrypt und Nginx installieren. Radicale setzt dabei auf Python und ist dateibasierend. Das bedeutet, dass neben den Kalendern auch die Nutzerdaten im Klartext in einer entsprechenden Datei vorliegen. Es gibt verschiedene Möglichkeiten die Passwörter zu schützen, hier wird Bcrypt eingesetzt. Bcrypt gilt noch als eine sehr gute und sichere Lösung.

Vorbereitung

Sofern noch nicht vorhanden, muss zuerst python-dev installiert werden. Außerdem empfiehlt es sich alle verfügbaren Updates zu installieren. Zum Kopieren von Github wird außerdem das Paket "git" benötigt.

sudo apt-get update && sudo apt-get upgrade && sudo apt-get install python-dev git

Passlib herunterladen und installieren

Für Bcrypt wird die Passlib-Bibliothek benötigt. Dazu unter https://pypi.python.org/pypi/passlib die aktuelle Version mit wget herunterladen. Mein letzter Download war die Version 1.7.1.

wget <Link>
tar -xvf passlib-*.tar.gz
cd passlib-*
sudo python setup.py install
cd..

Bcrypt herunterladen und installieren

Nun kann Bcrypt heruntergeladen und installiert werden:

git clone https://github.com/pyca/bcrypt.git
cd bcrypt
sudo python setup.py install
cd..

Radicale herunterladen und installieren

Radicale kann auf der Webseite unter http://radicale.org/download/ heruntergeladen werden. Das Projekt gibt es auch auf Github, allerdings habe ich nach dem Kopieren von dort immer Probleme gehabt. Es empfiehlt sich daher das Herunterladen eines Archives.

wget http://pypi.python.org/packages/source/R/Radicale/Radicale-1.1.1.tar.gz
tar -xvf Radicale-*.tar.gz
sudo python setup.py install
cd..

Benutzer anlegen

Radicale wird später unter seinem eigenen Benutzer laufen, der wie folgt erstellt wird.

sudo adduser --disabled-login radicale

Radicale einrichten

Als Nächstes wird das Verzeichnis für die Konfigurationsdateien von Radicale und die Datei "config" angelegt.

sudo mkdir /etc/radicale
sudo nano /etc/radicale/config

In die Datei wird folgendes eingefügt:

[server]
# CalDAV server hostnames separated by a comma
# IPv4 syntax: address:port
# IPv6 syntax: [address]:port
# For example: 127.0.0.1:9999, [::]:9999
# IPv6 adresses are configured to only allow IPv6 connections
hosts = 0.0.0.0:5232
# Daemon flag
daemon = True
# File storing the PID in daemon mode
pid =
# SSL flag, enable HTTPS protocol
ssl = False
# SSL certificate path
certificate = /etc/radicale/ssl/server.crt
# SSL private key
key = /etc/radicale/ssl/server.key
# SSL Protocol used. See python's ssl module for available values
protocol = PROTOCOL_SSLv23
# Ciphers available. See python's ssl module for available ciphers
ciphers =
# Reverse DNS to resolve client address in logs
dns_lookup = True
# Root URL of Radicale (starting and ending with a slash)
base_prefix = /
# Possibility to allow URLs cleaned by a HTTP server, without the base_prefix
can_skip_base_prefix = False
# Message displayed in the client when a password is needed
realm = Radicale - Password Required


[encoding]
# Encoding for responding requests
request = utf-8
# Encoding for storing local collections
stock = utf-8


[auth]
# Authentication method
# Value: None | htpasswd | IMAP | LDAP | PAM | courier | http | remote_user | custom
type = htpasswd

# Usernames used for public collections, separated by a comma
public_users = public
# Usernames used for private collections, separated by a comma
private_users = private

# Custom authentication handler
custom_handler =

# Htpasswd filename
htpasswd_filename = /etc/radicale/users
# Htpasswd encryption method
# Value: plain | sha1 | crypt | bcrypt
htpasswd_encryption = bcrypt

# LDAP server URL, with protocol and port
ldap_url = ldap://localhost:389/
# LDAP base path
ldap_base = ou=users,dc=example,dc=com
# LDAP login attribute
ldap_attribute = uid
# LDAP filter string
# placed as X in a query of the form (&(...)X)
# example: (objectCategory=Person)(objectClass=User)(memberOf=cn=calenderusers,ou=users,dc=example,dc=org)
# leave empty if no additional filter is needed
ldap_filter =
# LDAP dn for initial login, used if LDAP server does not allow anonymous searches
# Leave empty if searches are anonymous
ldap_binddn =
# LDAP password for initial login, used with ldap_binddn
ldap_password =
# LDAP scope of the search
ldap_scope = OneLevel

# IMAP Configuration
imap_hostname = localhost
imap_port = 143
imap_ssl = False

# PAM group user should be member of
pam_group_membership =

# Path to the Courier Authdaemon socket
courier_socket =

# HTTP authentication request URL endpoint
http_url =
# POST parameter to use for username
http_user_parameter =
# POST parameter to use for password
http_password_parameter =


[git]
# Git default options
committer = Radicale <radicale@example.com>


[rights]
# Rights backend
# Value: None | authenticated | owner_only | owner_write | from_file | custom
type = owner_only

# Custom rights handler
custom_handler =

# File for rights management from_file
file = ~/.config/radicale/rights


[storage]
# Storage backend
# Value: filesystem | multifilesystem | database | custom
type = filesystem

# Custom storage handler
custom_handler =

# Folder for storing local collections, created if not present
filesystem_folder = ~/.config/radicale/collections

# Database URL for SQLAlchemy
# dialect+driver://user:password@host/dbname[?key=value..]
# For example: sqlite:///var/db/radicale.db, postgresql://user:password@localhost/radicale
# See http://docs.sqlalchemy.org/en/rel_0_8/core/engines.html#sqlalchemy.create_engine
database_url =


[logging]
# Logging configuration file
# If no config is given, simple information is printed on the standard output
# For more information about the syntax of the configuration file, see:
# http://docs.python.org/library/logging.config.html
config = /etc/radicale/logging
# Set the default logging level to debug
debug = False
# Store all environment variables (including those set in the shell)
full_environment = False


# Additional HTTP headers
#[headers]
#Access-Control-Allow-Origin = *

Radicale Logfiles einrichten

Damit Logfiles erstellt werden, wird die Konfigurationsdatei "logging" erstellt. Das darin angegebene Verzeichnis wird zuvor mit den nötigen Rechten angelegt.

sudo mkdir /var/log/radicale
sudo chown -R radicale:radicale /var/log/radicale

sudo nano /etc/radicale/logging

In die Konfigurationsdatei fügen wir die folgenden Zeilen ein:

# Loggers, handlers and formatters keys
[loggers]
# Loggers names, main configuration slots
keys = root

[handlers]
# Logging handlers, defining logging output methods
keys = console,file

[formatters]
# Logging formatters
keys = simple,full

# Loggers

[logger_root]
# Root logger
level = DEBUG
handlers = console,file


# Handlers

[handler_console]
# Console handler
class = StreamHandler
level = INFO
args = (sys.stdout,)
formatter = simple

[handler_file]
# File handler
class = FileHandler
args = ('/var/log/radicale/radicale.log',)
formatter = full


# Formatters

[formatter_simple]
# Simple output format
format = %(message)s

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

Logrotate einrichten

Mit Logrotate wird nun jeden Tag ein neues Logfile angelegt und nach 14 Tagen/Versionen gelöscht.

sudo nano /etc/logrotate.d/radicale

Dazu werden die nachfolgenden Zeilen benötigt:

/var/log/radicale/radicale.log {
        daily
        rotate 14
        compress
        delaycompress
        create 640 radicale radicale
        postrotate
                systemctl restart radicale.service > /dev/null
        endscript
        sharedscripts
        missingok
        notifempty
}

Installation testen

Zuerst wird Radicale im Vordergrund gestartet und es sollte dabei keine Fehlermeldungen erscheinen.

sudo su radicale
cd ..
radicale -f
<strg + C>

Danach wird sicherheitshalber noch das Logfile überprüft.

sudo tail -f /var/log/radicale/radicale.log

Dort sollten die folgenden Zeilen stehen:

2017-02-12 17:00:18,025 - INFO: Starting Radicale
2017-02-12 17:00:18,025 - DEBUG: Authentication type is htpasswd
2017-02-12 17:00:18,147 - DEBUG: registered 'bcrypt' handler: <class 'passlib.handlers.bcrypt.bcrypt'>
2017-02-12 17:00:18,175 - DEBUG: detected 'bcrypt' backend, version u'3.1.2'
2017-02-12 17:00:18,177 - DEBUG: 'bcrypt' backend lacks $2$ support, enabling workaround
2017-02-12 17:00:21,816 - DEBUG: Base URL prefix: /
2017-02-12 17:00:21,817 - DEBUG: Listening to localhost port 5232
2017-02-12 17:00:21,820 - DEBUG: Radicale server ready

Nginx als Reverse Proxy

Die Verschlüsselung der Datenübertragung übernimmt Nginx und leitet die Anfragen an Radicale weiter.

server {
    listen 80;
    listen 443 ssl;

    server_name radicale.sharky.koeln;

    ssl_certificate /path/to/*.crt;
    ssl_certificate_key /path/to/*.key;

################# Enforce HTTPS ###
    if ($scheme = http) {
        rewrite ^ https://$server_name$request_uri? permanent;
    }
#################

################# Reverse Proxy ###
    location / {
        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_pass http://127.0.0.1:5232;
    }
#################
}

Radicale Autostart unter systemd

In Debian Jessie kann der Autostart von Radicale mit der folgenden Datei unter /etc/systemd/system/radicale.service realisiert werden.

[Unit]
Description=Radicale CalDAV server
After=network.target
Requires=network.target

[Service]
Type=forking
User=radicale
Group=radicale
PIDFile=%h/pid.pid
ExecStart=/usr/local/bin/radicale -d

[Install]
WantedBy=multi-user.target

Dann wird Radicale in den Autostart aufgenommen, gestartet und der Status überprüft.

# Autostart
sudo systemctl enable radicale.service
# Start services
sudo systemctl start radicale.service
# Status
sudo systemctl status radicale.service

Benutzer anlegen

Das Anlegen von Benutzern wird mit einem Python-Skript realisiert.

sudo touch /etc/radicale/users
sudo nano /home/radicale/add-user.py

Das Skript hat folgenden Inhalt:

#!/usr/bin/env python
import bcrypt
from optparse import OptionParser

parser = OptionParser()
parser.add_option("-u", "--user", dest="username", help="Username")
parser.add_option("-p", "--password", dest="password",
                  help="Password which will be hashed using bcrypt")
parser.add_option("-w", "--workfactor", dest="workfactor", default="12",
                  help="Workfactor of bcrypt, the higher the more secure")

(options, args) = parser.parse_args()
if options.username == None or options.password == None:
        parser.print_help()
else:
        hashed = bcrypt.hashpw(options.password,
                               bcrypt.gensalt(int(options.workfactor)))
        with open("/etc/radicale/users", "a") as configfile:
                configfile.write(options.username + ":" + hashed + "\n")
        print "Radicale user created"

Ein Benutzter wird schließlich wie folgt angelegt und anschließend Radicale einmal neu gestartet.

sudo python /home/radicale/add-user.py -u <user> -p <password>
...
sudo systemctl restart radicale.service

Client verbinden

Wie die unterschiedlichen Clients mit dem Server verbunden werden, ist unter http://radicale.org/user_documentation/ beschrieben. Auf jeden Fall sollte das abschließende "/" beachtet werden!

radicale.sharky.koeln/<user>/calender.ics/

Quellen

Radicale

Jan Karres

Diverse andere :)