Let Home Assistant trust a personal certificate authority

No progress yet sadly.

Let me know if you find something, I’ll update this thread too if I get to a solution!

1 Like

Okay, I made some progress. This post got me over the hump, though I don’t understand how to make the change permanent:

The short version is that Home Assistant uses a Python package called Certifi to manage trusted certificate authorities. The trust store is stored separately from the Linux platform trust store. This explains why updating the Linux trust store has no effect. The private CA can be either appended to the Certifi trust store (it’s text only), or you can run the script in the above post. Then restart the HASS services. The problem is that the file is not persistent across boots.

The Certifi trust store is located here: /usr/local/lib/python3.7/site-packages/certifi/cacert.pem

I am no expert on HASS or containers, so I don’t follow how to make this script run on boot so that the change is effectively permanent. That’s the last piece of the puzzle. Right now, I have to log in to the container and run the script manually. Maybe you can advise?

Finally, there needs to be some mechanism for updating or adding to the trust store on Home Assistant without all the above gymnastics. I’ll be digging in to how to make a suggestion for a feature enhancement on this topic.

Ken.

3 Likes

Great lead!
Back then I never searched for the acronyms, didn’t find some relevant topics in another case because of that too…

You’re running hass.io, right?

I’m running HASS on a VM on VMWare ESXi where I simply imported the VMDK file. I don’t think that’s hass.io, but again, I’m a bit of a newb here…

Damn, it didn’t post my reply from about half a week ago, but at least it saved a lot of what I had written:

A short, hopefully helpful rundown of the naming because I was quite confusedtoo when I started a few months ago (take all with a grain of salt though, the understanding is built from the context of many topics I read here):
Hass is just short for HomeAssistant; hass.io is the old term for running hass in Docker, today you’d just call it HomeAssistant or HomeAssistant Supervised if you want to emphasize the dockerization; HassOS is a kind of minimal Linux distro with everything necessary to run Hass by itself.
When you mentioned containers before I thought you were running Hass supervised, but if I got you correctly you’re running HassOS like me.

I used the script from the topic you linked and appended my CA to certifi’s store, but I still can’t connect to the CCU while using the verify_ssl option. A reboot resets the store like you said; calling homematic.reconnect after appending didn’t help either. The verify_ssl option works for you after appending to certifi’s store?

To run the script automatically I would normally use systemd, but I think Alpine uses OpenRC. However, /etc/local.d doesn’t exist and I also can’t run rc-update - so right now I’m not sure how HassOS handles initialization.
A cron service isn’t active either, so I’m running out of options a bit.

I think I found a way to add self-signed certs. Here’s the code I used, the trick was to append a .PEM version of the cert to the certifi CA.

# prep certs, this cannot be done in the Dockerfile because
# we also need to copy them to a mounted volume
cp /usr/local/share/ca-certificates/cert.crt /config/ssl/cert.crt
cp /usr/local/share/ca-certificates/cert.key /config/ssl/cert.key

update-ca-certificates

# create base64 PEM file from cert
openssl x509 -in /config/ssl/cert.crt -out /config/ssl/cert.pem

# add the pem to the certifi Python package certificate authority file
# the Python requests library does not use the system default CA, but uses the CA from the certifi package
# adding the PEM lets HA not spam warnings about insecure connections in the logs
cat /usr/local/lib/python3.8/site-packages/certifi/cacert.pem /config/ssl/cert.pem > /usr/local/lib/python3.8/site-packages/certifi/cacert-updated.pem
mv /usr/local/lib/python3.8/site-packages/certifi/cacert-updated.pem /usr/local/lib/python3.8/site-packages/certifi/cacert.pem
1 Like

Thank you for the suggestion, but krich11 and I are both on HassOS, not Supervised.
For us, the certifi certificate store resets on every reboot and so far I haven’t found out how HassOS handles initialization so that I could run a script on every boot (You can find what I’ve tried in that matter in my last comment before this one).
We also were already appending in pem format to our certifi store, whereby krich11 seems to have got it running, with the problem that a reboot resets the store, and I have properly appended my certificate but wasn’t able to connect to HomeMatic with the verify_ssl option nonetheless.

I verified the success of my appendix with the following script,

import certifi
import requests

try:
        print('Checking connection to RaspberryMatic...')
        requests.get('URL_TO_CCU')
        print('SSL is fine.')
except requests.exceptions.SSLError as err:
        print('SSL Error.')

which printed SSL error before my appendix and SSL is fine afterwards as expected.

1 Like

Was there any further progress being made? I am facing the same issue and created my own Root CA which of course hass.io does not accept. Because of that I can no longer connect to my QNAP NAS, QVR Pro and the Unifi components for presence detection. All use the certificates created with that Root CA which I would need to add to HA.

Does anyone know if a feature request or something exists to maybe create an “official” way to add it permanently to HA?

Thanks!

1 Like

Sadly not, I’m still facing the same issue.
I’m not aware of a feature request and hadn’t thought of one before because I expected some mildly-hacky way to accomplish this, even though certifi states that adding certificates is not supported.

I’m not sure how likely it will be for the request to go through, since it seems to me that the certificate store would need to be changed and that very few people are using their own CAs like us, but I can create a request over the next few days!

I think in this case the official way is the best way to do it. I personally created certificates covering the internal domains within my LAN and the externally reachable ones. So while the external part is no biggie with given plugins like Let’s Encrypt, etc. the challenge is always the internal domains. Since I did not want to use things strictly over the internet when I’m at home, I did not want to go all external.
So perhaps with a feature request we can at least get more information on how it can be done manually if there won’t be any “officially” implemented solution.
Maybe you can post the link to the request here as well for upvoting. :wink:

1 Like

Just posted the feature request!

Let me know if I should edit something in that would be better to have on top than in a separate comment!

Ok, I think I’ve got a workaround for homeassistant OS, but this is more like a workaround than a perm solution given you will lose this every time you install a core upgrade.

  1. Get that USB drive ready with the authorized_keys file described here: https://developers.home-assistant.io/docs/operating-system/debugging/
  2. Login to the :22222 ssh, and type “login” to get the shell
  3. Warg into the home assistant container: docker exec -it homeassistant /bin/bash
  4. Add your certificates to /etc/ssl/certs/ and ultimately to /etc/ssl/certs/ca-certificates.crt

Adding certs follows the same process you get automated by update-ca-certificates many linux distros:

  • Copy the cert into the /etc/ssl/certs directory
  • Get a hash with openssl x509 -hash -noout -in yourcert.pem
  • Symlink the certificate to $hash.0 (for example: fe8a2cd8.0 -> SZAFIR_ROOT_CA2.pem)
  • Add the cert to the end of /etc/ssl/certs/ca-certificates.crt (it is likely that this alone is enough, the rest is future-proofing / past-proofing)
  • Test success with curl https://yoursite.lan

The process of adding this can be automated rather easily, however my homeassisntant based cert-injector automation needs to wait :smiley:

Hope that helps,
mb

3 Likes

Wow, that looks promising, thanks a lot! I’ll definitely give it a try as soon as I find the time!

Presumably it’s even more helpful to kurisutian, since he can’t simply turn off verify_ssl like I can as it seems :sweat_smile:
And luckily, a MITM attack via my RaspberryMatic in my home network is rather unlikely - at least I hope that it is… :smiley:

Cool!

For the automation piece what I have in mind is:

  • sensor to check if https site loads with curl -skIX
  • sensor to check of https site loads with curl -sIX

Should only the last one fail, we need to inject the certs with a shell_command.

And this ideally fixes sensors as well upon the next check. Exceptions would be integrations that kick when initialized and put on an error path when they fail. Worst case: needs a restart.

Yeah, needs some time but nothing significant I imagine.

1 Like

Hi!, i need help on how to copy the CA file into step 4 ? I’m completely loss on where I’m when seeing the homeassistant /bin/bash. I also can not paste/edit via terminal text editor like nano or vim in there. I’m sorry for my silly question. Thanks anyway.

Edit:
Problem solved, how to resolve:

  1. In the /etc/ssl/certs directory: Get .pem certificate file from web using wget. ($ wget https://ca.example.com/ca.cert.pem)
  2. Get hash, and create symbolic link ($ ln -sf pathtoca.cert.pem pathtosymlinkwithhash)
  3. Create backup of ca-certificates.crt: $ cp ca-certificates.crt ca-certificates.crt.backup
  4. Join the certificate: $ cat ca-certificates.crt.backup ca.cert.pem > ca-certificates.crt

Done

2 Likes

Manual updates for this are tedious if you try to stay up-to-date with HA Core. I have a thread with a feature request here.

Discuss and more importantly, vote!

I’ve got some nastyhack proto code here that automates this process.
With the included binary sensor used, it constantly checks for losing trust, and re-injects as appropriate; this basically “smooths” you through core updates.

The important bit is that you’ll need to run this inside Core (homeassistant docker container).

While I’m sure there must be easier ways around this, I’ve chosen to live with this until a proper solution emerges.
Let me know if this works for you.

mb

1 Like

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