CalDav on DreamHost Shared Hosting | Feb 2022

Dreamhost natively supports WebDav for file synchronization but does not support CalDav unless you're on dedicated servers or their "dreamcompute" servers (which require you to configure everything).

This is just regarding Apache's mod_dav module. You CAN get CalDav working through a regular webserver, using Sabre Dav, or probably other software too, if you don't want PHP.

Setup is pretty easy once you know what's going on.

Useful Info & Links

  • Sabre Dav: The PHP software that makes this work. SabreDav Github
  • Sabre Authentication page for changing away from the default username/password of admin/admin
  • Sabre CalDav Setup Page for info about database access & which caldav urls to use
  • from SSH on your dreamhost server, cat ~/logs/dav.yourdomain.com/https/error.log to see errors
  • There's a lot of useful links and documentation on the sabre dav site ... check those out if you need more help.
  • sqlite3 data/db.sqlite should let you access the local database to add/remove users, when you're done using the default admin user.

Known Issues

  • This tutorial uses the default user admin and default password admin that comes pre-setup with Sabre.
  • DH's mod_security config blocks the text/calendar content type, among other parts of the request for CalDav. You must disable Extra Web Security to fix this, which may be a security risk.
  • Pushing changes to the server may delete any calendar events and users you have saved, so don't add much til you're done setting up everything.
  • the password hashing uses md5() instead of password hash functions. See this issue discussing it

Configure DreamHost

  1. Create a new domain (or subdomain) with a new user, and enable https
  2. Disable 'Extra Web Security'
  3. Use php 7.4 (at time of writing)

Install SabreDav

See official install instructions for more info.

  1. On your development machine, make a new folder like dav.mywebsite.com, then cd into it.
  2. composer require sabre/dav ~3.2.0
  3. composer update sabre/dav
  4. cp vendor/sabre/dav/examples/groupwareserver.php server.php to copy the sample server file that includes CalDav and CardDav support.
  5. edit server.php: uncomment the $baseUri line (line 27), and add $authBackend->setRealm('SabreDav'); to line 69. (realm is used for password hashing)
  6. mkdir data/
  7. cat vendor/sabre/dav/examples/sql/sqlite.* | sqlite3 data/db.sqlite
  8. chmod -Rv ug+rw data/, to ensure read/write permissions for the data dir & sqlite db file for user & group.

Test Server

  1. php -S localhost:3000 server.php to run a php test server
  2. Go to http://localhost:3000 in your browser. use admin and admin for username and password
  3. You should see "Nodes" and "Properties" headers.
  4. Click calendars -> admin -> scroll down to Create New Calendar form.
  5. Put taeluf as the uri, and Taeluf's Calendar as the Display Name (except use YOUR name!)
  6. Click on the newly created calender under the nodes. Now copy this url, something like http://localhost:3000/calendars/admin/taeluf/
  7. In your email client or calendar program, use http://localhost:3000/calendars/admin/taeluf/ as the url.
    • It may be http://localhost:3000/principals/admin/ or just http://localhost:3000 depending on your client. see Sabre's CalDav page
  8. Add a new event, and save it, to make sure it works! For extra testing, delete and re-add the server (to see if your added events re-download)

Dreamhost Shared Hosting Server

Now that the Php stuff is all working, let's set up an .htaccess for DreamHost's apache server.

Create a .htaccess file & put this in it:

DirectoryIndex /server.php

RewriteEngine on

RewriteCond %{REQUEST_URI} !^/server.php$
RewriteRule .* server.php [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]

Now, ensure "Extra Web Security" is disabled on DreamHost for your dav domain.

Then, upload your files to your DH server!

Repeat the setup steps from Test Server, but use dav.yourdomain.com instead of localhost:3000

Finishing Setup | Users

Delete the admin user & create a new user. See the "Authentication" link above and inspect the sqlite database for more info.

Delete the admin user:

  1. sqlite3 data/db.sqlite
  2. delete * from users; delete * from principals;

Add a new user:

The simplest way is to use this php script I wrote. It modifies the users table and principals table

  1. Copy it into bin/add-user.php
  2. chmod ug+x bin/add-user.php
  3. bin/add-user.php, and answer the prompts.
  4. Navigate to your dav domain in the browser & add a new calendar

Afterword, your CalDav url will be like: https://dav.yourdomain.com/calendars/user-name/calendar-name/

#!/usr/bin/env php
<?php
/**
 * First, chmod ug+x bin/add-user.php
 * @usage `bin/add-user.php`, then answer the prompts
 */

$db = __DIR__.'/../data/db.sqlite';
$pdo = new \PDO('sqlite:'.$db);

echo "\n# Add user\n\n";

$un = readline("Username: ");

$display_name = readline("Display Name: ");

// See https://www.php.net/manual/en/function.readline.php
// for a more robust readline solution that will not display your password as you type it
$pw = readline("Password: ");
readline_clear_history();

$email = readline("Email: ");

$pass_hash = md5($un.':SabreDav:'.$pw);

$sql = <<<SQL
    INSERT INTO users (username, digesta1) 
    VALUES ('$un', '$pass_hash')
SQL;

// echo $sql;
$pdo->exec($sql);

$sql = <<<SQL
    INSERT INTO principals (uri, email, displayname)
    VALUES ('principals/$un','$email', '$display_name');

    INSERT INTO principals (uri)
    VALUES ('principals/$un/calendar-proxy-read'),
        ('principals/$un/calendar-proxy-write');
SQL;

$pdo->exec($sql);

Conclusion

CalDav is used to synchronize calendars between different devices and different people. DreamHost does not natively support CalDav with their Apache server, but they do allow you to run pretty much whatever software you want, even on shared hosting.

There is a free and open source PHP package called SabreDav which lets you run a CalDav server from a single PHP script (and of course all the dependencies that install through composer). SabreDav seems to do a LOT more than CalDav, but that's outside the scope of this post.

The best long-term solution would be for DreamHost to just add CalDav support. They already have WebDav, so I don't understand why they don't just add it. Tweet @DreamHost, contact them on their site, or otherwise nag them to add official CalDav support to their hosting plan.

I personally prefer to use self-hosted software over that which Big Tech offers. It just ... feels good. Also, it seems really silly that CalDav isn't more wide-spread. This stuff should NOT be so difficult.

It should be easy for an average computer user to setup their own CalDav server for extremely cheap, but this does not seem to be the case. Then again, I didn't shop around.

Errors you might have encountered

ModSecurity doesn't like the content type text/calendar

[Mon Feb 28 15:57:56.532405 2022] [:error] [pid 363916:tid 3573857371904] [client 111.222.333.44:58000] [client 111.222.333.44] ModSecurity: Warning. Match of "within %{tx.allowed_request_content_type}" against "TX:content_type" required. [file "/dh/apache2/template/etc/mod_sec3_CRS/REQUEST-920-PROTOCOL-ENFORCEMENT.conf"] [line "956"] [id "920420"] [msg "Request content type is not allowed by policy"] [data "|text/calendar|"] [severity "CRITICAL"] [ver "OWASP_CRS/3.3.2"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-protocol"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "capec/1000/255/153"] [tag "PCI/12.1"] [hostname "dav.yourwebsite.com"] [uri "/calendars/admin/taeluf/d10b67106646bb65ff458b0e74d70565d561ae42.ics"] [unique_id "0oD*5eO@2fK_5mO(5lG-0hX-6y"]

ModSecurity doesn't like highly escaped newlines

[Mon Feb 28 15:57:56.533750 2022] [:error] [pid 363916:tid 3573857371904] [client 111.222.333.44:58000] [client 111.222.333.44] ModSecurity: Warning. Pattern match "[\\n\\r]" at ARGS_NAMES:BEGIN:VCALENDAR\r\nCALSCALE:GREGORIAN\r\nPRODID:-//Ximian//NONSGML Evolution Calendar//EN\r\nVERSION:2.0\r\nBEGIN:VTIMEZONE\r\nTZID:America/Chicago\r\nX-LIC-LOCATION:America/Chicago\r\nBEGIN:DAYLIGHT\r\nTZNAME:CDT\r\nTZOFFSETFROM:-0600\r\nTZOFFSETTO:-0500\r\nDTSTART:20070311T020000\r\nRRULE:FREQ. [file "/dh/apache2/template/etc/mod_sec3_CRS/REQUEST-921-PROTOCOL-ATTACK.conf"] [line "171"] [id "921150"] [msg "HTTP Header Injection Attack via payload (CR/LF detected)"] [data "Matched Data: \x0d found within ARGS_NAMES:BEGIN:VCALENDAR\x5cr\x5cnCALSCALE:GREGORIAN\x5cr\x5cnPRODID:-//Ximian//NONSGML Evolution Calendar//EN\x5cr\x5cnVERSION:2.0\x5cr\x5cnBEGIN:VTIMEZONE\x5cr\x5cnTZID:America/Chicago\x5cr\x5cnX-LIC-LOCATION:America/Chicago\x5cr\x5cnBEGIN:DAYLIGHT\x5cr\x5cnTZNAME:CDT\x5cr\x5cnTZOFFSETFROM:-0600\x5cr\x5cnTZOFFSETTO:-0500\x5cr\x5cnDTSTART:20070311T020000\x5cr\x5cnRRULE:FREQ: BEGIN:VCALENDAR\x0d\x0aCALSCALE:GREGORIAN\x0d\x0aPRODID:-//Ximian//NONSGML Evolution [hostname "dav.yourwebsite.com"] [uri "/calendars/admin/taeluf/d10b67106646bb65ff458b0e74d70565d561ae42.ics"] [unique_id "0oD*5eO@2fK_5mO(5lG-0hX-6y"]

ModSecurity doesn't like a high anomaly score

[Mon Feb 28 15:57:56.538177 2022] [:error] [pid 363916:tid 3573857371904] [client 111.222.333.44:58000] [client 111.222.333.44] ModSecurity: Access denied with code 418 (phase 2). Operator GE matched 7 at TX:anomaly_score. [file "/dh/apache2/template/etc/mod_sec3_CRS/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "93"] [id "949110"] [msg "Inbound Anomaly Score Exceeded (Total Score: 10)"] [severity "CRITICAL"] [ver "OWASP_CRS/3.3.2"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [hostname "dav.yourwebsite.com"] [uri "/calendars/admin/taeluf/d10b67106646bb65ff458b0e74d70565d561ae42.ics"] [unique_id "0oD*5eO@2fK_5mO(5lG-0hX-6y"]