TLS Client Certificate Authentication for outgoing requests

Mutual TLS (mTLS) authentication is a common and very robust means of securing access to HTTPS resources.

Home Assistant already has support for requesting and validating client certificates on incoming connections, via ssl_peer_certificate (or a trusted reverse proxy). Likewise, the companion apps have good support for specifying and sending such client certificates when connecting to Home Assistant.

The final piece that is missing, however, is the ability for Home Assistant to send client certificates when making outgoing requests. (For example, when making a request to a RESTful Sensor or to a ZWave JS UI server.) This would allow Home Assistant to authenticate itself to other services and gain access to private data protected via mTLS.

There have been a few feature requests for this in the past, regarding specific integrations:

Solving this piecemeal for each integration, however, would be a lot of work and not a very good user experience. Instead, I would like to propose a core configuration option for specifying “global” client certificates, that could be used by all integrations automatically. Something along the lines of:

extra_tls_certificates:
  client:
    - cert: config/HomeAssistant.cert.pem
      key: config/HomeAssistant.key.pem
      password: !secret homeassistant_client_cert_pw

Many (most?) integrations that make outgoing HTTP requests do so using the aiohttp or httpx sessions created by Home Assistant core. These sessions, in turn, all share the default SSLContext instances.

By loading client certificates into these default contexts, all integrations that use them will be able to authenticate their outgoing requests via mTLS, with no additional per-integration work! (Integrations that currently do not use the default contexts can be updated over time to do so, or could otherwise load the certs themselves.)

I have implemented a barebones PoC of this idea as a custom component, and successfully tested it with a number of integrations.

There is one limitation in doing this from a custom component, however: the concurrent nature of component loading causes a race condition. Other components that initialize first, before the client certificates are loaded, may fail to authenticate. For this feature to be reliable, I think it will need to be implemented in the core and guaranteed to load very early.

I am happy to work on contributing an implementation of this feature, but wanted to start a discussion here first to gauge interest and the appetite for this approach. A few questions that will need to be ironed out:

  • Is the approach of loading client certificates into the default TLS contexts the right way to go?
  • Does it make sense to also support loading custom CAs? This is implemented in the PoC above, but could be separated. Custom CAs often go hand-in-hand with mTLS, and specifying them via the configuration seems a lot nicer and more flexible than using REQUESTS_CA_BUNDLE.
  • Would we want to support putting PEM-encoded certificates directly in the config, in addition to files in disk?
  • Should this be configurable via the GUI somehow, or only via YAML (at least initially)?

Here is a branch showing what this might look like integrated into HA core: vexofp/homeassistant-core:extra-tls-certificates