Home Assistant Add-on: Caddy 2

I haven’t had this issue for ages. It’s working perfectly now.

1 Like

I have my stuff working but wanted a double check if missed anything by using all help above to create this.

  1. Registry a dynamic DNS
    Example: https://www.duckdns.org/

  2. Add this repository to your Hass.io instance:
    GitHub - einschmidt/hassio-addons: Home Assistant addons
    Addon Configuration:

args:
  - '--watch'
env_vars:
  - name: EMAIL
    value: <email>
  - name: DOMAIN
    value: <domain>
  - name: PORT
    value: <home assistant port>'8123'
  - name: DUCKDNS_TOKEN
    value: <your_token_here>
log_level: info
non_caddyfile_config:
  destination: localhost
  domain: <domain>.duckdns.org
  email: <email>
  port: <home assistant port>8123
config_path: /share/caddy/Caddyfile
custom_binary_path: /share/caddy/caddy
caddy_fmt: true
  1. Save and Run Caddy - will fail more things needed.
  2. Some changes needed to Home assistant.
    Configuration.yaml
homeassistant:
    external_url: https://<domain_name>.duckdns.org/
    internal_url: http://<local_ip>:8123
http:
    # ssl_certificate: /ssl/fullchain.pem
    # ssl_key: /ssl/privkey.pem
    use_x_forwarded_for: true
    trusted_proxies:
      - 127.0.0.1
      - ::1
    ip_ban_enabled: true
    login_attempts_threshold: 5
  1. Changes to the Router
  • Need to open Port 443 to 443, see your router for that.
  • As for port 80 to 80 might be needed. Caddy will auto route to 443 for you if it is open.
  • No other ports needed.
  1. Download binary for your OS
    • Download Caddy
    • Example:
    • Platform “Linux amd64” Select Binary: Duckdns
    • Insert binary into folder matching Addon Configuration path above.
      • Create folder then place file in
        • /share/caddy/
      • Rename file to: caddy
      • Update the file permissions
        • Allow read/execute access
  2. Caddyfile:
    • Create file
      • /share/caddy/Caddyfile
{
    email {env.EMAIL}
}
(common) {
        tls {
                dns duckdns {env.DUCKDNS_TOKEN}
                on_demand
        }
        header {
		        Strict-Transport-Security "max-age=31536000; includeSubdomains; preload"
		        X-XSS-Protection "1; mode=block"
		        X-Content-Type-Options "nosniff"
		        Referrer-Policy "same-origin"
		        Content-Security-Policy "frame-ancestors {env.DOMAIN}.duckdns.org"
		        -Server
		Permissions-Policy "geolocation=(self), microphone=()"
	    }
}

{env.DOMAIN}.duckdns.org {
    import common
    reverse_proxy localhost:{env.PORT} {
    }
}
  1. Restart Caddy Addon after updating file.
    Check https://securityheaders.com/ to see if secure.
    Check if can access <domain_name>.duckdns.org

  2. Example of adding a subdomain add to the Caddyfile (Note access outside of network):

    • name of your choice.
    • Use either ip different from host or localhost if on the same machine.
  • Add to Add on config env_vars
env_vars
- name: SUB_DOMAIN_1
  value: <sub_domain_1>

  • Add to the Caddyfile
    Within selection header
    add wildcard to the Content-Security-Policy *.{env.DOMAIN}.duckdns.org
example after update
Adding  *.{env.DOMAIN}.duckdns.org
Content-Security-Policy "frame-ancestors {env.DOMAIN}.duckdns.org *.{env.DOMAIN}.duckdns.org"

Add to bottom

{env.SUB_DOMAIN_1}.{env.DOMAIN}.duckdns.org {
    import common
    reverse_proxy localhost :<port> {
    }
}
  1. Restart Caddy Addon after updating.
    Check if can access <sub_domain_1>.<domain_name>.duckdns.org
    Check https://securityheaders.com/ to see if secure.
1 Like

Thanks for this its practically a how-to but a lot of your configuration looks a little custom. Any chance somebody wants to take a crack at actually doing some instruction. I am still stuck on caddy 1 and I gave it a shot but so far I have gone back to a back up and caddy one. I am ready to give it a another shot. Do I have to compile my own binary?

I think I have a pretty common use case. I use duckdns and letsencrypt for certificates. I can access remotely a few subdomains I have set up without issue except from work where I can reach all of the subdomains except HA which gives an authorization error. Home assistant is running on Virtual machine and like I say I can access remotely everything from external networks except from my work network, my HA instance.
I will post all my config files when I get home but I am at work and like I say cant get into HA except from my phone that utilizes a private network.

edit wait is there documentation that I cant find about the addon that is referred to in this thread

My config in this thread is pretty similar to yours except I use Namecheap instead of duckdns. You will need to build your own caddy with the duckdns plugin for DNS challenge.

Listening to your advice for years. I’m parsing the whole thread sort of looking at the help EagleDTW received and your config to try and piece it together. Hopefully I can get it figured and then get to the bottom of what is causing the authorization error for only my HA instance when I try to connect from work.

I wish caddy got more love from HA but I think Frencks nginx add on steered allot of people that way. Maybe one day you will write a new article on caddy 2 but God knows we are all busy in these times

It’s on my to-do list. But I’m also considering ditching supervised for container and making my own caddy 2 with the addons built in and running it via compose.

I just don’t understand NGINX and I really do like to not just black-box all this and have some understanding of what I’m doing. I really like Caddy as it ticks the boxes.

2 Likes

Do I need non caddy config file at all if using custom binary and a caddy file?
EDIT: I really had to massage it but got it going. Thank you @firebass08 @DavidFW1960 your set up got me going but I had some confusion with firebass08’s environment variables and ended up just putting those important infos right into the caddy file.
I’m getting this warning in the log:

{"level":"warn","ts":1645821643.596266,"logger":"tls","msg":"YOUR SERVER MAY BE VULNERABLE TO ABUSE: on-demand TLS is enabled, but no protections are in place","docs":"https://caddyserver.com/docs/automatic-https#on-demand-tls"}

Thoughts?

Also updating binary or add on?

Still not sure what has to be in the addon config section of the addon if I am using the caddy file?

Sorry for the confusion. Wanting to make it easier than the 2 weeks it took me to get working.
My goal with the variables is to update within home assistant and just drop the file in the location with no edits.
in the top part where the email is need to add the following:

{
    email {env.EMAIL}
    admin off
    on_demand_tls {
        ask      http://localhost:5000/exist
        interval 2m
        burst    5
    }
}

More on Admin off - https://caddyserver.com/docs/caddyfile/options#admin

for that error on_demand_tls https://caddyserver.com/docs/automatic-https#on-demand-tls

I tried switching to cloudfare from duckdns. Had issues converting to it and removed Caddy and using the nabacasa currently.

1 Like

You don’t need to worry about that. It’s a warning when you enable the on-demand feature. I have that turned off now anyway as it’s not needed. Here is my config for the addon:

args:
  - '--watch'
env_vars:
  - name: NAMECHEAP_API_USER
    value: '!secret name_user_V2'
  - name: NAMECHEAP_API_KEY
    value: '!secret name_key_V2'
log_level: debug
non_caddyfile_config:
  destination: localhost
  domain: mydomain.com
  email: [email protected]
  port: 8123
config_path: /share/caddy2/Caddyfile
custom_binary_path: /share/caddy2/caddy
caddy_upgrade: false
caddy_fmt: false

Note I have the updrade and format false. I just have those as a place holder. If you set the upgrade to true it will build caddy using whatever plugins you are using automaticqally for you. But it will also rebuild the binary every time you start the addon.
My Caddyfile:

{
	email [email protected]
}

(common) {
	tls {
		dns lego_deprecated namecheap
	}
	header {
		Strict-Transport-Security "max-age=31536000; includeSubdomains"
		X-XSS-Protection "1; mode=block"
		X-Content-Type-Options "nosniff"
		Referrer-Policy "same-origin"
		Permissions-Policy "geolocation=(self) , microphone=()"
		Content-Security-Policy "frame-ancestors domain.com:xxxxx *.domain.com:xxxxx"
		-Server
	}
}
domain.com:xxxxx {
	import common
	reverse_proxy localhost:8123 {
	}
}
cockpit.domain.com:xxxxx {
	import common
	reverse_proxy localhost:19090 {
	}
}
glances.domain.com:xxxxx {
	import common
	reverse_proxy localhost:61208 {
	}
}
logviewer.domain.com:xxxxx {
	import common
	reverse_proxy localhost:4277 {
	}
}
portainer.domain.com:xxxxx {
	import common
	reverse_proxy localhost:9000 {
	}
}
tasmoadmin.domain.com:xxxxx {
	import common
	reverse_proxy localhost:9541 {
	}
}

Note the :xxxxx lets me use a non standard port. I use one in the 30000 range and that is the only port I have Opened/forwarded on my router. The username and password for DNS validation is included in the config for the addon.

I’m finding I am ending up typically with a zerossl certificate too. I don’t seem to be getting a LetsEncrypt one anymore. Doesn’t matter. Both do the same job

2 Likes

Sure seems like an enterprise feature where you are dealing with customers and there own domains. Any consequences to turning off that I would need to look for?

Thanks for that. So to update you simply make true and restart but you dont want an autoupdate situation on your “production” type setup.

Some information in my config file(non_caddyfile_config) seems to be repeated in my caddy file but probably not an issue. I think for the DUCKDNS plugin in caddy it requires email and token for letsencrypt. It seems like forever ago when letsencrypt and duckdns addons all had to configured and that was a pain. I believe I have the tls properly configured in the caddy file and I can close port 80 now but havent tried.

Yes I have seen. Security through obscurity and moving to the higher ports.
How does that impact your Content-Security-Policy? Do you have define each of the subdomains with the port number or am I off the mark… or could you do *.domain.com:*. Or maybe I am misunderstanding?

As aside is cockpit more like portainer or webmin or guacamole?

I will see if I can throw up my configs when I am not at work once i sanitize them to help others

No idea. I was originally advised to add that when I was getting timeout issues trying to get certificates with letsencrypt for 6+ sites at a time. It didn’t help with that so I removed it.

If you subscribe to the caddyserver list they will notify you when there is a new caddy version available so you can edit the addon config to make it build a new custom caddy with your addons and the new caddy version. You don’t want that everytime you start the addon, only when there is a new version available. Yes I do this on my production server.

Not an issue but you can remove it from the addon config as you don’t need it and it will be ignored anyway if you have a caddyfile.

If you use DNS challenge as I do then you can close port 80. Can and Should. You don’t need port 80 or any other port open except 443 of xxxxx as I have in my config.

See my policy above it also needs to include xxxxx. *.domain.com:xxxxx covers them all. In addition to this, I only have a ‘A’ record for my domain so it only has an IPv6 address. I can then only access HA via a device that supports IPv6. Even more obscure. Good luck stumbling on my domain and then scanning it’s IPv6 address on the open port. IPv6 opens a port to a device it’s not forwarding.

Cockpit is a web ssh project which you can use to manage debian via a web page instead of ssh. It also shows nice graphics and stats of your server but I mostly use ssh anyway.

So xxxxx is like a wildcard(I thought you were redacting info for security)? I thought I tried to use that in my config and got an error. I must have made an error. I will try again I have a few subdomains

Substitute whatever port you are using for xxxxx

Hi,
I’m having the issue that in the Add-On configuration the “Network” box is not being displayed. I would like to change the default port from “443” to something else and for that I would need it.

This section is missing:

I have re-installed the Add-On several times, but it doesn’t appear anymore.
Any idea how to fix this?

It is a confusing option not needed. You don’t want to use port443 for what exactly? If it’s for a reverse proxy all my examples use a port that isn’t 443. What are you trying to do?

I have issues with the portforwarding on my network. I can‘t access my HA anymore from outside/the internet.
This used to work in the past and I don‘t find the issue. All settings look good.

As one of the tests I wanted to switch the incoming port to see if this is causing the issue.

Anyways I am wondering why the network part is not displayed. On my friend‘s system it is showing.

It’s an old version of the addon. It wasn’t originally there then he added it but then removed it because it’s not needed and confusing. You can use any port you want and you specify it in the caddyfile as I said before ans as I show in all my caddyfile posts in this thread.

In the meanwhile I found the same description here: Port changes do not appear to be honoured · Issue #34 · einschmidt/addon-caddy-2 · GitHub

Turned out that Caddy is not part of my issue… somehow it’s my firewall blocking the incoming calls on port 443, although it is configured to be open.

Never mind… thanks for your help! Caddy is rock solid as expected! Great tool.

Anyone seen an issue where you can’t delete integrations when behind caddy?

I’ve had an issue for a long time where I can add integrations but not delete them. The button does nothing (Click Delete → Click OK → Dialog closes then nothing happens). I had been deleting them from the .storage folder, eww, but I saw reference in another nginx thread that someone had the same issue and had to add to their nginx config.

dav_methods PUT DELETE;

I tried bypassing caddy and suddenly I could delete integrations again. Wondering if such a thing for caddy exists? I’ve not had much luck with Dr Google.

I’ve never had that problem