Skip to content

Instantly share code, notes, and snippets.

@Fabrizz
Last active November 27, 2024 18:20
Show Gist options
  • Save Fabrizz/c147c101b131c3a055057285bb3b9935 to your computer and use it in GitHub Desktop.
Save Fabrizz/c147c101b131c3a055057285bb3b9935 to your computer and use it in GitHub Desktop.
Secure HomeAssistant with Cloudflared and client certificates

HA via Cloudflared with mTLS

This tutorial uses Cloudflare Tunnels to allow you to connect to your Home Assistant instance without opening ports to the intertet, it also guide you on adding client certificates (mTLS), allowing only you to access your instance, and fully compatible with the companinn app.

Important

Client certificates do not work properly on IOS. You still can use Cloudflared to allow remote access.
2016 bug HA Issue

Note

If you are not using (/dont want to use/) Cloudflare tunnels, or you want to use other services, HA offers ssl_peer_certificate to validate clients in a similar way.

Prequisites

  • Domain name
    You need a domain name (freenoms do not work), you can get one from any registrar1 of your liking, for example Cloudflare or Porkbun.

  • Home Assistant OS
    This guide uses the Cloudflared Addon, you can install Cloudflared on any machine and use other services that are not Home Assistant, but this guide only covers HAOS installs.2

  • Cloudflare Account
    You need an account, every Cloudflare service used in this guide is free. I also recommend turning on 2FA (two factor authentication) to have your account secure.3

Cloudlare ≠ Cloudflared ≠ Cloudflared Addon
Cloudflare is the service provider, Cloudflared is the service that Cloudflare provides to create a CF Tunnel, the Cloudflared Addon is a "wrapper" so Clouflared can be installed in HAOS.


Step 1: Preparing Cloudflare

Now that you own your domain name, you need to set Cloudflare as the DNS provider, if you bought your domain throught them, this is done automatically, if you have used another registrar, you just need to clic "Add a site" on your homepage.

After entering your domain, you will see instructions on how to change the nameservers (Detailed guide). Cloudflare can take up to an hour to notice your changes, its not instant, so dont panic!

Cloudflare offers a suite of services for your domain like security, chaching, custom routing, etc. There are lots of options and they are outside the scope of this guide. The default configuration is great, we will do only a couple of changes later so we only accept secure connections (HTTPS) and clients that have our certifice.

You can read more about each setting in the Cloudflare panel or in their really good documentation.

Step 2: Preparing Home Assistant

Installing the addon

You can add the addon by clicking here:

image

Tip

You can manually add it going to your addons page, then Addon store > Three dots > repositories > Add, and adding https://github.com/brenner-tobias/ha-addons to the list, after a couple of seconds and reloading the page, you can see the the Cloudflared addon in the store.

Now we are going to install the addon (not running it), after the installation is completed, we should see on the top bar two new tabs, "Configurations | Logs", inside the configuration tab we need just to set the external hostname of our HA instance. Example: subdomain.domain.tld > home.fabrizz.co. (Clic save to actually make the changes)

Configuring Home Assistant

After entering the hostname, we need to tell Home Assistant to allow connections from the cloudflared addon, as well as adding basic Fail2Ban to ban IPs that fail to authenticate.

You need to access the configuration.yaml file, you can access it using the File Editor or Studio Code Server addons (We will use the Studio Code Server addon later to create the certificates!).

Open your Home Assistant instance and show the dashboard of an add-on.

We will add the configuration to allow connections from Cloudflared:

http:
  trusted_proxies:
    - 172.30.33.0/24
  use_x_forwarded_for: true
  ip_ban_enabled: true
  login_attempts_threshold: 5

What does this do?

  • trusted_proxies: Makes HA trust the docker network were the addons live.
  • use_x_forwarded_for: Uses the X-Forwarded-For header from the proxy to know real client IP.
  • ip_ban_enabled: Enables the banning of IPs based on login attempts.
  • login_attempts_threshold: Allowed failed attempts util the IP is banned.

Now we need to run the addon!

After a couple of seconds you can go to the "Logs" tab and see to which Cloudflare servers you are connected. I personally do not have the watchdog/auto-updates, but if you want you can enable them. Cloudflare regularly updates the upstream Cludflared so keeping the addon updated is important.

You can now use the external domain to access your Home Assistant interface. The next step adds client certificates as well as recommend some Cloudflare settings to change, like forcing HTTPS. If you do not plan on using mTLS, you can go straight to Step 4: Cloudflare recommended settings.

Step 3: Configuring client certificates (mTLS)

What is mTLS? - We will configure mTLS matching as a WAF rule, but you can also configure it inside the Zero Trust dashboard.

Creating certificates and setting the WAF rule

You need to go to Your zone > SSL/TLS > Client Certificates.

A | Set the hosts (View image)
First we will set on which host we will enable mTLS, click Edit, add your hostname (example: home.fabrizz.co), and then click save.

B | Create the certificates (View image)
Then we need to generate or certifitate pair, click Create certificate, then click Generate private key and CSR with Cloudflare (RSA 2084 should be selected by default, you can change the validity of your cert if you want), then click Create.

Now we should see our key pair!, copy the certificate to a cert.pem file, and the private key to a key.pem file.
(You can use Notepad, Notepad++, VSCode, etc to edit/create this files)

Caution

  • You must keep the newline at the end of both cert files!
  • Once you click OK, the certificates are not shown again!
  • IOS WS connections do not work correctly! Explained at the top of the gist.

C | Create WAF rule (View image)
Now we will block every connection to the origin server if the client cert is not present. You should check before this change that everything works and that your instance is reachable using the tunnel. You should use HTTPS.

Now click Create mTLS rule, this will bring you to Security > WAF, with a preapplied template for mTLS.

D | Change the name (View image)
Set your WAF rule name to something descriptive, if you use your domain for multiple services or want to have multiple WAF rules, this is important.

E | Creating the rule (View image)
Now we will edit the template included, you can do it using the expression builder, but here we will click (E) Edit expression and change the default one to:

(http.host eq "home.fabrizz.co" and not cf.tls_client_auth.cert_verified)

Change "home.fabrizz.co" to your subdomain.domain.tld! (Also make sure that the action is Block and not Allow)

What does this do?
  • First it matches the host to your home subdomain. http.host eq "home.fabrizz.co"
  • Then if the client is NOT tls verified not cf.tls_client_auth.cert_verified
  • Blocks the request to the server

F | Deploying (View image)
Click deploy. Now if you go to your HA via the CF tunnel, you should see a Cloudflare page saying that you do not have access to this resource, great!


(Example log when an action is blocked)

Step 3A: Generating the pkcs12.p12 certificate

Warning

  • You can view detailed information on the original thread home-assistant/android#2650
  • We are going to use the OpenSSL CLI to generate the .p12 certificate, in the thread above there are some solutions using the bundled OpenSSL that come with git, but the easiest way is to use a terminal addon in HomeAssistant.
  • The instructions below are general, I personally use the Studio Code Server addon terminal/interface to easily upload/download the files, you can view the instructions below.

General instruccionesions

Test if the certificates work correctly:

curl --cert cert.pem --key key.pem https://hass.example.com

If you see the raw HTML response from HomeAssistant, you can continue. If you receive a Cloudflare error then you probably did not configure the host in step 3A, you should alse disable the WAF rule and check if the tunnel works.

Combine the keys:

cat cert.pem key.pem > pkcs12.pem

Create the client certificate:

openssl pkcs12 -in pkcs12.pem -export -out home_assistant_pkcs12.p12

Follow the on screen prompts, you should add a pin/password to your cert.

Importing the certificate Now you can import this certificate on your mobile phone, depending on your OS4 you need to do different things to add it to your device, on Android for example you need to select "User certificate for app/vpn".

After you have imported it, you can restart the HA app, it should ask you which installed certificate to use, if not, accessing first using the browser on your phone or waiting a bit may be necessary. If you do not provide a certificate / do something wrong, you need to clear the cache of the page or the HA app.

Test that you can access and use your HA instance correctly, then use another device to access the tunnel without certs and test that Cloudflare blocks it.

Creating rules to bypass this mTLS check to allow Google Assistant/Alexa connections is possible using the knwon IPs of the aws/gcp servers, but its outside of the scope of this guide,


Use Studio Code Server Addon

You should have the addon installed and the web interface open.

cf-vscode-example

  • A: Drag and drop your cert.pem and key.pem files to the config folder
  • B: Open a new terminal. You should see the Home Assistant welcome screen.

Now you can follow the general instructions on the Home Assistant terminal.

When you finish you can right click on the home_assistant_pkcs12.p12 file and download it to your account, then once you have tested that it works, you can delete this files from Home Assistant using Right click > Delete permanently.

Step 4: Cloudflare recommended settings

This part depends heavily on what are you going to use your domain to, if you want to setup custom mail, etc.

We will start at the DNS tab: I recommend doing all the Recommended steps to complete zone set-up. For email using set up restrictive SPF, DKIM, and DMARC records or Email Routing is recommended, you can view more on the email tab.

PS: If you have github pages you can add subdomains for them! (Or migrate them to CF pages)

Inside the SSL/TLS tab I recommend:

  • Enabling Always Use HTTPS
  • Enabling Automatic HTTPS Rewrites

Inside the security tab you can:

  • Block countries from accessing your instance (not important if you are using mTLS)
  • Allow certain IPs to access for example the HA API (Allow Google/amazon connections)

Inside the Speed tab:

  • Enable Brotli compression

There are tons of other options, you should take a look at them if you have the time!


With <3 by Fabrizio | fabriz.co

Original idea from home-assistant/android#2650
Using Zero Trust for more granular control: home-assistant/android#3593 (comment)
TODO: Find original comment about google/amazon IP ranges
More info: https://community.home-assistant.io/t/expose-home-assistant-for-google-ips-only-ipv4-only/184646

Fabrizz logo

Footnotes

  1. GoDaddy is not recommended, as they have gained a bad reputation, they spend more on ads than on service.
    You can always use a registrar from your country, or your country registrar (for example, nic.ar for .ar domains)

  2. You can use this guide to secure non HAOS installs (installing Cloudflared directly), installs using proxies, other services or even non CF Tunnels endpoints!

  3. Cloudflare is a by design a MITM, they need this kind of access as the security suite/model that they provide depends on the raw data. If you do not like this, I recommend just using a VPN or reverse proxy on a VPS.

  4. Depending what operating system you use you may need to change the type of cert generated. Guide tested with different Android devices (for now). Client certificates should also work on all desktop browsers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment