Let Home Assistant trust a personal certificate authority

Here my workaround:

Copy your ca files in your favorite directory. For me it is ~/config/my_ssl. Here I can upload my files through the file editor addon.

Connect to the HA core via SSH Addon, copy the files via “docker cp” and update the system ca files via “docker exec”

for f in $(ls ~/config/my_ssl); do docker cp ~/config/my_ssl/$f homeassistant:/usr/local/share/ca-certificates/; done
docker exec -it homeassistant sh -c "update-ca-certificates"

Yes, you have to do this after every core container update :frowning: .

my_ssl contains the ca certificates:

➜  ~ ll ~/config/my_ssl
total 24K
-rw-r--r--    1 root     root        3.5K Feb  5 14:04 Client.CA.crt
-rw-r--r--    1 root     root        3.5K Feb  5 14:04 DMZ.CA.crt
-rw-r--r--    1 root     root        3.5K Feb  5 14:04 Hausautomation.CA.crt
-rw-r--r--    1 root     root        3.5K Feb  5 14:04 Intra.CA.crt
-rw-r--r--    1 root     root        3.5K Feb  5 14:04 Mgmt.CA.crt
-rw-r--r--    1 root     root        3.5K Feb  5 14:03 Root.CA.crt

This this I can use the checkbox “Check TLS connection” in the homematic integration.

1 Like

Here is a “solution” that allow you to survive home assistant upgrade, and to avoid any ssh as long as you already have hacs installed.

I made this today to test if this would solve issue with the fullykiosk integration using https.
It didn’t …

Create a python script available as a service

Install pyscript

https://hacs-pyscript.readthedocs.io/en/latest/installation.html

Enable allow_all_imports

In config/integrations, click on the configure button on the pyscript integration and check Allow All Imports

Import the script

Using what ever method you use to add or change file on your home assistant instance, add the following script in your config folder under pyscript/add_custom_ca.py

# ==================================================================================================
#  python_scripts/add_custom_ca.py
# ==================================================================================================

# --------------------------------------------------------------------------------------------------
# Add the .pem from the provided path to certifi catalogue
# --------------------------------------------------------------------------------------------------
# from https://community.home-assistant.io/t/let-home-assistant-trust-a-personal-certificate-authority/184917/22?u=vaarlion

import certifi
from os import path as os_path


@service
def add_custom_ca(pem_path):
    """yaml
    name: Add Custom ca
    description: Add the .pem from the provided path to certifi catalogue.
    fields:
      pem_path:
        name: Pem file(s) path
        description: a path or a list of path to .pem file
        exemple: ["/ssl/custome_ca.pem", "/config/www/ssl/extra_ca.pem"]
        selector:
          text:
    """

    inputPath = pem_path
    listPath = []
    if inputPath is None:
        log.warning("===== pem_path is required if you want to add something.")
    else:
        if (
            isinstance(inputPath, str)
            and inputPath
            and task.executor(os_path.isfile, inputPath)
        ):
            listPath.append(inputPath)

        elif isinstance(inputPath, list) and inputPath:
            for path in inputPath:
                if isinstance(path, str) and task.executor(os_path.isfile, path):
                    listPath.append(path)
                else:
                    log.info(
                        "===== ignoring '{}' as it's not a path to an existing file".format(
                            path
                        )
                    )
        else:
            log.warning(
                "===== pem_path is required to be a path or a list of path to existing files"
            )

        cafile = certifi.where()

        for pem in listPath:
            __append_fileA_to_fileB(pem, cafile)


@pyscript_executor
def __append_fileA_to_fileB(fileA, fileB):
    with open(fileA, "rb") as infile:
        customca = infile.read()
    with open(fileB, "r") as outfile:
        cachain = outfile.read()
    if customca.decode("utf-8") not in cachain:
        with open(fileB, "ab") as outfile:
            outfile.write(customca)

Then call the service pyscript.reload

Upload your .pem file

Using the same way you’ve uploaded the script, upload your CA certificate in a .pem format where ever you want. I recommend /ssl/. You can add as many as you want.

Automate it

Testing the service

Try to run the service and make sure there is no error in the log.
in developer-tools/service, run something like

service: pyscript.add_custom_ca
data:
  pem_path: /ssl/mycert.pem

or

service: pyscript.add_custom_ca
data:
  pem_path:
    - /ssl/mycert.pem
    - /config/some_other_cert.pem

With your own file obviously.
Now check to see if this solve your issue. It may not as not every automation use that certificate catalogue, and other have some hard-coded port or protocol.

If it does, then we need to have it survive an upgrade.

Create an automation

Now juste make an automation who run that services as soon as home assistant start :slight_smile:

alias: "Add custom cert on boot"
description: ""
trigger:
  - platform: homeassistant
    event: start
condition: []
action:
  - service: pyscript.add_custom_ca
    data:
      pem_path: /ssl/MainCA.pem
mode: single

No worry, the python script make sure the cert isn’t already present before adding it, so you won’t append it 500 time before the next release.

2 Likes

I don’t know if there is another way to do it, but this one works flawlessly. Thank you!!

My only concern is whether the automation will run before or after it tries to load the integration that needs to verify the certificate.
I will have to wait for the next update to check. In any case, the failure would only occur on the first boot after updating.

1 Like

Hello,
I’ve coded a custom integration to add custom private CA (Certificate Authority) to Home Assistant.
The main purpose is to avoid adding again manually CAs even if Docker container si recreated.
Give it a try : GitHub - Athozs/hass-additional-ca: Add custom CA (Certificate Authority) to Home Assistant.

2 Likes

Howdy @frenck, hope you are well.
This is the 6th solution for something HASS was believed to not need. Any chance we can revisit support for self-signed CA support for HA?

:beers:

4 Likes

I have an fairly easy way of doing this and will survive upgrades/restarts, using Docker. The Home Assistant Docker image already has the ca-certificates package installed so it’s just-a-matter of getting your CA cert into /usr/local/share/ca-certificates/ and running update-ca-certificates. To automate this, I simply mount the following script into /etc/cont-init.d:

#!/bin/sh -e
cp /config/ssl/ca.crt /usr/local/share/ca-certificates/
update-ca-certificates

NOTE: this assumes your CA cert is mounted at /config/ssl/ca.crt. Adjust according to your environment.

My setup looks something like this:
docker-compose.yaml:

services:
  homeassistant:
    # this section redacted
    volumes:
      - /opt/hassio/data/config:/config
      - /opt/hassio/data/ssl:/config/ssl
      - /opt/hassio/data/init:/etc/cont-init.d

My ca.crt (private root CA) is located in /opt/hassio/data/ssl/ca.crt.

My S6 init script:
/opt/hassio/data/init/01_install_ca:

#!/bin/sh -e
cp /config/ssl/ca.crt /usr/local/share/ca-certificates/
update-ca-certificates

Now when you startup the container, you should see the following in you log output:

[cont-init.d] executing container initialization scripts...
[cont-init.d] 01_install_ca: executing... 
WARNING: ca-certificates.crt does not contain exactly one certificate or CRL: skipping
[cont-init.d] 01_install_ca: exited 0.
[cont-init.d] done.
1 Like

It’s unfortunate this add-on is necessary, but thanks so much for writing it! I’m attempting to use it with smtp notify, but it doesn’t seem to use my CA certs, even though they are being installed in ca-certificates.crt by the add-on. openssl s_client successfully verifies my signed cert from within the homeassistant docker container. I think it’s not working because homeassistant.util.ssl client_context() depends on REQUESTS_CA_BUNDLE. Where does this environment variable need to be set when running haos?

Hey,
I could not find on the internet a reliable way to set permanently an environment variable in Home Assistant OS (some kind of hack may be possible I guess though).

Yes, I’ve asked on here and in a few different channels on the ha discord and no one seems to know how to set environment variables when using haos. I’m starting to think it might not be possible, and that my only option would be to redeploy and run on docker containers w/o supervisor or haos. This would be more overhead to manage, and kind of a pain to be honest, but I would then be able to pass environment variables via docker compose. Sure wish there was a better way. Thanks again.

Hello,
I just coded a new integration to add environment variables to HAOS: GitHub - Athozs/hass-environment-variable: Add Environment Variable to Home Assistant.
It’s not available in HACS yet, you’ll have to install it as a custom HACS repo, or manually without HACS.
Hope it will help.

Hello, I tried out your new addon but unfortunately I was not able to get it to work. I set REQUESTS_CA_BUNDLE as in your example but it doesn’t seem to be available to the smtp notify component. When this didn’t work, I made another custom component to test environment_variable. It simply iterates through the os.environ dict and prints out all the key/value pairs to the log. After several restarts of home assistant it appears to be random as to whether my custom environment variable will be present in the logs or not. I think this has to do with the order in which the components are loaded. I think if environment_variable is loaded before my evtest module I see my variable, otherwise I don’t. My current theory is that since smtp notify is a built-in component, maybe it’s loaded before all the custom components? If this is true, that might explain why smtp notify still doesn’t work, even after many restarts of ha. If it’s loaded before custom components, then maybe it doesn’t have the chance to learn from the environment_variable addon?

Hey,
Sorry for the late reply,
You’re right, it depends on the startup order of integrations.
You may need to reload your integration after Home Assistant has completely started to take environment variables into account.

@Vaarlion , I just wanted to jump on here and thank you for posting this solution, as it worked wonders for me. In my case, I have homeassistant connecting to my nextcloud instance to import calendars, but since it was backed by my custom CA, HA wouldn’t talk to it. HA most certainly needs an ability to import custom CA’s so this kind of work isn’t necessary, but in the meantime, this’ll do! +1 internet points to you :slight_smile:

1 Like

Hi,

I created yesterday a new version 0.2.0 of integration Additional CA for Home Assistant with full support for HAOS and Docker.

Add a private CA, the easy way :

  • Install HACS
  • Install Additional CA integration via HACS or manually without HACS, full docs in link above
  • Copy private CA to config folder
mkdir -p config/additional_ca
cp my_ca.crt config/additional_ca/
# configuration.yaml
---
default_config:
additional_ca:
  my_ca_1: my_ca.crt
  my_ca_2: ca_foo.pem
# ...
  • Export environment variable for Docker
# compose.yml
version: '3'
services:
  homeassistant:
    # ...
    environment:
      - REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
  • All done !
2 Likes

I followed the instructions to install through HACS (running in HAOS), but it didn’t seem to solve the issues I’m having.

For example, the CalDav and Jellyfin integrations still throw insecure cert warnings, even though I can see that your addon created the cert in /usr/local/share/ca-certificates in the homeassistant container. I can also curl my services from within the homeassistant container without any issue.

For addons such as Advanced SSH, I understand why curl from there doesn’t work, seeing that it’s a container of it’s own, which didn’t get the CA file.

But what doesn’t make sense, is why the core integrations aren’t using the updated CA store from the homeassistant container, which includes my CA.

 Logger: py.warnings
Source: components/jellyfin/client_wrapper.py:67
First occurred: 10:21:51 AM (2 occurrences)
Last logged: 10:21:51 AM
/usr/local/lib/python3.11/site-packages/urllib3/connectionpool.py:1061: InsecureRequestWarning: Unverified HTTPS request is being made to host 'jellyfin.[redacted]'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings warnings.warn( 

---

 Logger: py.warnings
Source: components/caldav/api.py:16
First occurred: 10:15:46 AM (6 occurrences)
Last logged: 10:15:46 AM
/usr/local/lib/python3.11/site-packages/urllib3/connectionpool.py:1061: InsecureRequestWarning: Unverified HTTPS request is being made to host 'cal.[redacted]'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings warnings.warn( 

It seems that Jellyfin integration uses a Python package named jellyfin-apiclient-python under the hood, this Python package does not seem to enable certificate verification.

The error message you got gives you a link and it says “This happens when a request is made to an HTTPS URL without certificate verification enabled. Follow the certificate verification guide to resolve this warning.”

If I’m correct, developpers should enable certificate verification on Jellyfin integration or jellyfin-apiclient-python package. Must be double checked :slight_smile:

I didn’t check for caldav, may be the same.

1 Like

Yeah, we may need to patch “certifi” in all containers that rely on that.
I made an attempt in HACI back a while ago at it, it seems to be quite stable.

It seems CalDav uses the Requests lib: https://github.com/home-assistant/core/blob/d45227adbb6ad9b3dff72059ae9a7a74d6b93e1f/homeassistant/components/caldav/__init__.py

I just exported the variable in the homeassistant container, and it doesn’t seem to be working, probably requires a restart, but that will clear the variable… Looking at your docs, it seems I’m a bit out of luck…

📝 Note: At time of writing, I could not find on the internet a reliable way to set permanently an environment variable in Home Assistant OS.

God, can’t believe we’re searching for workarounds to implement something so basic. Home Assistant is supposed to be all about local control, yet we’re expected to use certificates from an EXTERNAL certificate authority. (Even though letsencrypt is free, it is still an external service…)

@TurfFiber
Because you’re using HAOS, could you confirm that your private certificate is added to /usr/local/lib/python3.11/site-packages/certifi/cacert.pem ?

Interesting, it is not in the certifi cacert.pem, but I see it in /usr/local/share/ca-certificates/

I’m not seeing anything in the logs either. Any ideas how to debug this?