Support SSL Mutual Authentication in caldav component

My calendar is internet exposed and the only way to access it is via SSL mutual authentication which users have their own SSL certificate to authenticate to the server. But, the caldav component in home assistant today doesn’t support that. I’ve created a patch to support this. Would you like to accept it?

First I add a new config attribute for ssl client certs. Underlying python modules (caldav and requests) expect this to be a file or a tuplet of files. I’m using a single file for the cert.

root@homeassistant:/srv/homeassistant/lib64/python3.9/site-packages/homeassistant# diff -u const.py.20220909 const.py
--- const.py.20220909   2022-09-09 19:06:52.601048585 +0000
+++ const.py    2022-09-09 12:20:43.612253233 +0000
@@ -231,6 +231,7 @@
 CONF_SLAVE: Final = "slave"
 CONF_SOURCE: Final = "source"
 CONF_SSL: Final = "ssl"
+CONF_SSL_CLIENT_CERT: Final = "ssl_client_cert"
 CONF_STATE: Final = "state"
 CONF_STATE_TEMPLATE: Final = "state_template"
 CONF_STOP: Final = "stop"

Next I add the attribute to caldav and pass the cert to the python caldav module:

root@homeassistant:/srv/homeassistant/lib64/python3.9/site-packages/homeassistant/components/caldav# diff -u calendar.py.20220909 calendar.py
--- calendar.py.20220909        2022-09-09 12:22:15.359201502 +0000
+++ calendar.py 2022-09-09 12:22:30.231030029 +0000
@@ -19,6 +19,7 @@
 from homeassistant.const import (
     CONF_NAME,
     CONF_PASSWORD,
+    CONF_SSL_CLIENT_CERT,
     CONF_URL,
     CONF_USERNAME,
     CONF_VERIFY_SSL,
@@ -59,6 +60,7 @@
                 )
             ],
         ),
+        vol.Optional(CONF_SSL_CLIENT_CERT, default=None): cv.string,
         vol.Optional(CONF_VERIFY_SSL, default=True): cv.boolean,
         vol.Optional(CONF_DAYS, default=1): cv.positive_int,
     }
@@ -80,7 +82,8 @@
     days = config[CONF_DAYS]

     client = caldav.DAVClient(
-        url, None, username, password, ssl_verify_cert=config[CONF_VERIFY_SSL]
+        url, None, username, password, ssl_verify_cert=config[CONF_VERIFY_SSL],
+        ssl_cert=config[CONF_SSL_CLIENT_CERT]
     )

     calendars = client.principal().calendars()

And it works!

Notes:

  • The cert is a local file on the filesystem which works for me with HA core on my raspberry pi. How this can work with containers or in the cloud, I don’t know.
  • I didn’t update documentation. But if you want help with that let me know. I would ask that in the documentation you include security considerations for certificates in your doc
  • Security consideration: I suggest leaving the certificate for client auth outside of the HA working directory. There may be issues in HA UI such that a bad actor can access the certificate. Certificates used for client auth are an identity and need to be secured even tighter than passwords. Further make sure certs are only readable by the homeassistant user.