This document is a WORK IN PROGRESS.
This is just a quick personal cheat sheet: treat its contents with caution!
Radicale¶
Radicale is a small but powerful CalDAV (calendars, todo lists) and CardDAV (contacts) server.
Reference(s)
Prerequisite(s)
Table of contents¶
Install¶
Install Radicale with Python (v3
!) in /root/.local/bin/radicale
:
Create a folder, wherever you want, to store your CalDAV and CardDAV collections:
Check the installation by running the Radicale server, then you can access it by searching
localhost:5232
in your web browser:
$ sudo python -m radicale --storage-filesystem-folder=/path/to/radicale/collections
$ firefox localhost:5232 # e.g. with firefox
Config¶
Radicale config file¶
Create the Radicale configuration file with a basic config:
$ sudo mkdir /etc/radicale
$ sudo vi /etc/radicale/config
+ > [server]
+ > hosts = 0.0.0.0:5232, [::]:5232
+ >
+ > [storage]
+ > filesystem_folder = /path/to/radicale/collections
Authentication¶
Create the following script, it will allow you to define a user (and associated password) for your Radicale server:
$ cd /path/to/radicale/
> #!/bin/sh
>
> echo "Info - This script is a 'htpasswd' compatile alternative for this command:"
> echo " 'htpasswd -c /path/to/file username'"
> echo " by running './passwd.sh /path/to/file' instead."
> echo ""
>
> if [ $# -ne 1 ]
> then
> echo "Error - Illegal number of parameter(s), one paramater must be provided: "
> echo " the path to fhe file where you want to append the result of this script."
> exit 1
> fi
>
> if [ ! -f $1 ]
> then
> echo "Error - The path to fhe file where you want to append the result of this script"
> echo " does not exist!"
> exit 2
> fi
>
> if [ ! -w $1 ]
> then
> echo "Error - The path to fhe file where you want to append the result of this script"
> echo " is not writable by the current user!"
> echo " (you might want to run this script with 'sudo')"
> exit 3
> fi
>
> username=""
> password=""
>
> echo "[passwd.sh] enter the name of the user you want to create:"
> read username
>
> echo "[passwd.sh] enter the password of the user you want to create:"
> get_pwd () {
> # Safe and POSIX compliant way of asking password:
> stty -echo
> read password
> stty echo
> printf "\n"
> }
> get_pwd
>
> echo "$username":$(openssl passwd -apr1 "$password") >> $1
$ chmod +x passwd.sh
Create a user and it's password:
You can create second user/password just by running the same command again.
Then update the Radicale config file:
$ sudo vi /etc/radicale/config
> [server]
> hosts = 0.0.0.0:5232, [::]:5232
+ >
+ > [auth]
+ > type = htpasswd
+ > htpasswd_filename = /path/to/radicale/users
+ > # encryption method used in the htpasswd file:
+ > htpasswd_encryption = md5
>
> [storage]
> filesystem_folder = /path/to/radicale/collections
Domain name and router configuration¶
Getting a domain name is optional, but recommended if you don't want to enter your server's public IP address every time you want to access your Radicale server, or if you don't have a fixed public IP address.
Configuring your router in order to forward your port 80 and port 443 is optional, but recommended if you want to access your Radicale server outside your local network.
You can refer to DuckDNS for a free and open source service which will
point a DNS (sub domains of duckdns.org) to an IP of your choice, but you can choose the domain
name provider of your choice. In any cases, this cheat sheet will assume that your domain name is
yourhostname.tld
You might want to check if your router does support NAT loopback (see https://en.wikipedia.org/wiki/Network_address_translation#NAT_hairpinning and see https://en.wikipedia.org/wiki/Hairpinning). If it doesn't, you will have to look for a work around in order to access your server by it's domain name from your local network.
Nginx server¶
-
For reference my default
/etc/nginx/nginx.conf
can be found here, you just have to modify it like so: -
And create the following configuration file:
Check the Nginx server¶
-
Test your configuration:
-
(re)start
nginx
:
-
(re)start Radicale:
-
Now open a web browser and navigate to
yourhostname.tld
, you should see the login menu of Radicale.
Basic SSL certs config¶
-
Create basic self signed SSL certificate and key:
-
Add the SSL cert and key to the Nginx Nextcloud config:
$ sudo vi /etc/nginx/conf.d/radicale.conf > server { > listen 80; > listen [::]:80; > server_name yourhostname.tld; > + > # enforce https ~ > return 301 https://$server_name:443$request_uri; > } + > + > server { + > listen 443 ssl http2; + > listen [::]:443 ssl http2; + > server_name yourhostname.tld; + > + > ## Basic ssl config + > ################### + > ssl_certificate /etc/ssl/radicale/yourhostname.tld.crt; + > ssl_certificate_key /etc/ssl/radicale/yourhostname.tld.key; + > ################### + > + > return 301 https://$server_name:5232/; + > }
-
Then update the Radicale config file:
$ sudo vi /etc/radicale/config > [server] > hosts = 0.0.0.0:5232, [::]:5232 + > ssl = True + > certificate = /etc/ssl/radicale/yourhostname.tld.crt + > key = /etc/ssl/radicale/yourhostname.tld.key > > [auth] > type = htpasswd > htpasswd_filename = /path/to/radicale/users > # encryption method used in the htpasswd file: > htpasswd_encryption = md5 > > [storage] > filesystem_folder = /path/to/radicale/collections
Advanced SSL certs config¶
-
Install
acme-tiny
: -
Create a folder for the
acme-challenge
and another one forletsencrypt
: -
Create the account and domain private keys and the domain certificate:
-
Edit your
radicale.conf
file like below:$ sudo vi /etc/nginx/conf.d/radicale.conf # add acm-challenge location > server { > listen 80; > listen [::]:80; > server_name yourhostname.tld; > + > # Match best non-regular expression for "/.well-known/acme-challenge/" + > location ^~ /.well-known/acme-challenge/ { + > #alias /var/www/stephane-radicale.duckdns.org/acme-challenge/; + > alias /media/raid/radicale/acme-challenge/; + > try_files $uri =404; + > } + > + > # Match everything else to enforce https + > location / { ~ > return 301 https://$server_name:443$request_uri; + > } > } > > server { > listen 443 ssl http2; > listen [::]:443 ssl http2; > server_name yourhostname.tld; > > ## Basic ssl config > ################### > ssl_certificate /etc/ssl/radicale/yourhostname.tld.crt; > ssl_certificate_key /etc/ssl/radicale/yourhostname.tld.key; > ################### > > return 301 https://$server_name:5232; > }
-
Get the certificate from Let’s Encrypt:
$ cd /path/to/radicale/letsencrypt $ sudo acme-tiny --account-key account.key --csr domain.csr --acme-dir /path/to/radicale/acme-challenge/ | sudo tee signed_chain.crt $ sudo cp signed_chain.crt signed_chain.crt.back # backup signed_chain.crt just in case
Note:
signed_chain.crt
is the root CA certificate, and it is self signed. -
Generate your Diffie-Hellman parameter file (this will take a long time):
-
Download Let’s Encrypt intermediate cert and create a
fullchain
file with it and the signed cert:$ sudo wget -O - https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem | sudo tee chain.pem $ sudo cat signed_chain.crt chain.pem | sudo tee fullchain.pem
Note:
chain.pem
is the intermediate certificate. -
Just to make sure, you can verify the cert with this command:
-
Then update the Radicale config file:
$ sudo vi /etc/radicale/config > [server] > hosts = 0.0.0.0:5232, [::]:5232 > ssl = True ~ > #certificate = /etc/ssl/radicale/yourhostname.tld.crt ~ > #key = /etc/ssl/radicale/yourhostname.tld.key + > certificate = /path/to/radicale/letsencrypt/signed_chain.crt + > key = /path/to/radicale/letsencrypt/domain.key > > [auth] > type = htpasswd > htpasswd_filename = /path/to/radicale/users > # encryption method used in the htpasswd file: > htpasswd_encryption = md5 > > [storage] > filesystem_folder = /path/to/radicale/collections
-
Now your
radicale.conf
file should look like this:$ sudo vi /etc/nginx/conf.d/radicale.conf > server { > listen 80; > listen [::]:80; > server_name yourhostname.tld; > > # Match best non-regular expression for "/.well-known/acme-challenge/" > location ^~ /.well-known/acme-challenge/ { > #alias /var/www/stephane-radicale.duckdns.org/acme-challenge/; > alias /media/raid/radicale/acme-challenge/; > try_files $uri =404; > } > > # Match everything else to enforce https > location / { > return 301 https://$server_name:443$request_uri; > } > } > > server { > listen 443 ssl http2; > listen [::]:443 ssl http2; > server_name yourhostname.tld; > > ## Basic ssl config > ################### ~ > #ssl_certificate /etc/ssl/radicale/yourhostname.tld.crt; ~ > #ssl_certificate_key /etc/ssl/radicale/yourhostname.tld.key; > ################### + > + > ## Advanced ssl config + > ###################### + > ssl_certificate /path/to/radicale/letsencrypt/signed_chain.crt; + > ssl_certificate_key /path/to/radicale/letsencrypt/domain.key; + > + > ssl_dhparam /path/to/radicale/letsencrypt/dhparam4096.pem; + > + > ssl_stapling on; + > ssl_stapling_verify on; + > + > ssl_trusted_certificate /path/to/radicale/letsencrypt/fullchain.pem; + > + > #add_header Strict-Transport-Security "max-age=63072000" always; + > + > ssl_session_timeout 1d; + > ssl_session_cache shared:MozSSL:10m; # about 40000 sessions + > ssl_session_tickets off; + > + > ssl_protocols TLSv1.2 TLSv1.3; + > ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; + > ssl_prefer_server_ciphers off; + > ###################### > > return 301 https://$server_name:5232/; > }
Note: see https://ssl-config.mozilla.org for a secure SSL config.
Now you can test your config at this addresses:
With the previous configurations, you should get A+ grades.
Create a Radicale service¶
With OpenRC¶
- See https://github.com/Kozea/Radicale/issues/737 ?
- See http://varun.rajamane.me/2019/2-mar-2019-radicale-installation-alpine-linux/ ?
An OpenRC service can be created in order to start the Radicale server at startup, before login and starting the graphical server:
# sudo vi /etc/init.d/radicale # create an openrc service
> #!/sbin/openrc-run
>
> # See <https://github.com/Kozea/Radicale/issues/737>
>
> command="python"
> command_args="-m radicale"
> #command_user="root"
> command_background="yes"
> pidfile="/var/run/radicale.pid"
> description="A Free and Open-Source CalDAV and CardDAV Server"
>
> depend() {
> need localmount
> need net
> need nginx
> }
$ sudo chmod +x /etc/init.d/radicale
$ rc-update add radicale default
Auto renew certificates Cron job config¶
Reference(s)
$ sudo vi /opt/renew_cert_for_yourhostname.tld.sh
> #!/bin/sh
>
> rm -f /path/to/letsencrypt/signed_chain.crt.tmp
>
> acme-tiny --disable-check --account-key /path/to/radicale/letsencrypt/account.key --csr /path/to/radicale/letsencrypt/domain.csr --acme-dir /path/to/radicale/acme-challenge/ > /path/to/radicale/letsencrypt/signed_chain.crt.tmp || exit
>
> mv -f /path/to/letsencrypt/signed_chain.crt.tmp /path/to/letsencrypt/signed_chain.crt
>
> rc-service nginx reload
$ sudo chmod 700 /opt/renew_cert_for_yourhostname.tld.sh
Note that this script won't work if the current certificate is already expired!
$ sudo EDITOR=vi crontab -e # edit the crontab of the root user
> ...
> 0 0 1 * * /opt/renew_cert_for_yourhostname.tld.sh 2>> /var/log/acme-tiny-yourhostname.tld.log # run once per month
Use¶
Import an existing address book¶
- export
- e.g. in android -> Go into your android "Contacts" app -> menu -> settings -> export -> share
all contacts -> send the
.vcf
file to your PC -> from your PC: openyourhostname.tld
in a web browser and login -> upload address book or calendar -> select the.vcf
file
- e.g. in android -> Go into your android "Contacts" app -> menu -> settings -> export -> share
all contacts -> send the
Import an existing ics calendar¶
-
⚠️ This below command will overwrite the entire calendar when importing it ⚠️ :
See https://www.reddit.com/r/selfhosted/comments/jbnu1l/how_would_i_push_an_ics_to_a_caldav_server/
DAVx¶
"+" add account -> login with URL and user name -> base URL: yourhostname.tld
; username:
your-user-name
; password: your-password
-> login -> account name: your-davx-account-name
->
create account
Go into your android "Contacts" app -> menu -> settings -> accounts -> add account -> DAVx address
book -> your-davx-account-name
Go back into your android "Contacts" app -> menu -> settings -> default account for new contacts -> select the new DAVx address book
Troubleshooting¶
Can't upload an address book: Error: 400 Bad Request
¶
When trying to upload an address book, you might get the following error message: Error: 400 Bad
Request
. Server side you might get a log like this one:
[2021-01-09 17:42:22 +0100] [30265/Thread-35] [WARNING] Bad PUT request on '/username/9b900a16-b9ff-99f2-3608-64b2a94ab306/': At line 394: Failed to parse line: =38=68=20=C3=A0=20=32=30=68
You can resolve this error by editing the .vcf
file you are trying to upload. Search for the
NOTE
fields, and make sure every NOTE
field is on one line (with line wrapping), not on several
lines.
E.g. from this:
NOTE;ENCODING=QUOTED-PRINTABLE:=41=64=72=65=73=73=65=20=3A=20=31=33=20=72=75=65=20=52=
=69=63=68=61=72=64=20=4C=65=6E=6F=69=72=
=0A=43=6F=64=65=20=3A=20=37=39=33=31
To this:
NOTE;ENCODING=QUOTED-PRINTABLE:=41=64=72=65=73=73=65=20=3A=20=31=33=20=72=75=65=20=52=69=63=68=61=72=64=20=4C=65=6E=6F=69=72=0A=43=6F=64=65=20=3A=20=37=39=33=31
Note that the first
=
character of each line is deleted before merging them.
If this cheat sheet has been useful to you, then please consider leaving a star here.