Home Assistant Add-on: Caddy 2

As I said I think I changed it because when I did a security check on my HA from a third party site it recommended that option.
Yes I have x-forwarded-for set. Setting x-frame-options to same origin should ALLOW iFrame connections not deny them.

Caddy1 had no issues with those options and worked correctly.

So as a follow-up. I was recommended to try a content-security-policy like this:

Content-Security-Policy "frame-ancestors domain:xxxxx *.domain:xxxxx"

In the header instead and that works with my iframes.

When using the X-Frame-Options with “same-origin”, it failed on my end.
But changing the X-Frame-Options to the following enabled my iFrame instantly:

X-Frame-Options "ALLOW-FROM https://my.domain.com"

The above allows your webpage to be inserted into iFrames, but only on that particular page.
See also: here

The content-security policy supersedes the x-frame-options. A poster on the Caddy forum linked me to a doc about it. When I do a security check on my HA even though I slated the x-frame-options it shows that requirement as satisfied now. I can give you some links tomorrow when on my PC if you are interested.

I still don’t know it is only caddy2 that didn’t like the sameorigin… caddy1 was fine with that…
From your link

SAMEORIGIN which allows you to frame your own site or ALLOW-FROM https://example.com/ which lets you specify sites that are permitted to frame your own site.

I don’t understand unless sameorigin doesn’t see a sub domain as same origin in Caddy2?

From how it looks and sounds, Caddy 2 seems to be stricter. Which I think could be explained as a secure default. Who knows. At least I don’t know.

Regarding the content-security policy, that’s a header I am struggling with to be honest. Either it is secure, or part of my web pages aren’t loaded, even if I allow any source. If you could share some good links, or even your setting for that header, that would be awesome!

Yeah for sure. We need all the help we can get sometimes.
This is my Caddyfile

{
        email [email protected]
}

(common) {
        tls {
                dns lego_deprecated namecheap
                on_demand
        }
        header {
                Strict-Transport-Security "max-age=31536000; includeSubdomains"
                X-XSS-Protection "1; mode=block"
                X-Content-Type-Options "nosniff"
                Referrer-Policy "same-origin"
				Content-Security-Policy "frame-ancestors my-domain.com:xxxxx *.my-domain.com:xxxxx"
                -Server
        }
}

my-domain.com:xxxxx {
        import common
        reverse_proxy localhost:8123 {
        }
}

cockpit.my-domain.com:xxxxx {
        import common
        reverse_proxy localhost:19090 {
        }
}

glances.my-domain.com:xxxxx {
        import common
        reverse_proxy localhost:61208 {
        }
}

logviewer.my-domain.com:xxxxx {
        import common
        reverse_proxy localhost:4277 {
        }
}

portainer.my-domain.com:xxxxx {
        import common
        reverse_proxy localhost:9000 {
        }
}

tasmoadmin.my-domain.com:xxxxx {
        import common
        reverse_proxy localhost:9541 {
        }
}

My Caddy2 config:

non_caddyfile_config:
  email: '!secret le_email'
  domain: '!secret ha_host'
  destination: localhost
  port: 8123
args:
  - '--watch'
env_vars:
  - name: NAMECHEAP_API_USER
    value: '!secret name_user_V2'
  - name: NAMECHEAP_API_KEY
    value: '!secret name_key_V2'
log_level: info
config_path: /share/caddy2/Caddyfile
custom_binary_path: /share/caddy2/caddy

See my caddyforum post here https://caddy.community/t/converting-caddyfile-from-v1-to-v2-xframe-issue/11462/4?u=davidfw1960 where he says preferred method now is to use content security policy instead of x-frame-options.
Then if I do a scan here https://securityheaders.com/
I get this result:

Note I had 2 entries for content security policy.
If I remove my-domain.com:xxxxx ingress frames won’t load
If I remove *.my-domain.com:xxxxx iframes won’t load

See also I defined a different path for Caddy2 as I had Caddy1 as well when I was playing and never switched back. I use a cudtom caddy that includes the lego_deprecated plugin for dns validation.

On my router I have only one port opened for HA - it is a high number non standard port represented as xxxxx above. NO ports are opened or required by Caddy to obtain ssl certificates from LetsEncrypt - the beauty of DNS validation instead of http validation.

All of this really is childs play to setup. It’s the tweaking that gets you but I am only tweaking header options now and checking I still have access correctly without leaving too many holes. I am going to play with the permissions-policy now but this is now becoming a movable target as I see a few new header options are coming soon as well…

I just added a Permissions policy

Permissions-Policy "geolocation=(self domain:xxxxx *.domain:xxxxx), microphone=()"

And now get A+ rating…
(Also for completeness, I am downloading the Caddy Binary from the official Caddy2 site. I build it for AMD64 with the lego-deprecated plugin and it goes into the Cadd2 folder as caddy. I think I had to make it executable as well. The addon log will then tell you you are using a custom binary.

Caddy2 is pretty cool. At least it makes sense in my mind how it works which I could never work out with Nginx (vaguaries of the header not withstanding)

Hi @DavidFW1960,
Many thanks for sharing your setup! It is very interesting to see how other people use their Caddy :slight_smile:
And guess what, not only my setup, but also my experience is close to exactly the same! Setting up all of this with Nginx would be a nightmare…

But coming to some of my findings:

  1. Header
    I am using nearly the same header. Mixing your and my header, I get the following result, which passes A+ at securityheaders.com:
(header) {
	header {
		Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
		X-XSS-Protection "1; mode=block"
		X-Content-Type-Options "nosniff"
		Referrer-Policy "same-origin"
		-Server
		Content-Security-Policy "frame-ancestors domain.com *.domain.com"
		Permissions-Policy "geolocation=(self domain.com *.domain.com)"
	}
}
  1. Your Caddy2 config
    You can skip the non_caddyfile_config, as you make use of a Caddyfile. When the add-on finds a Caddyfile, the non_caddyfile_config is ignored. Therefore:
non_caddyfile_config: {}
args:
  - '--watch'
env_vars:
  - name: NAMECHEAP_API_USER
    value: '!secret name_user_V2'
  - name: NAMECHEAP_API_KEY
    value: '!secret name_key_V2'
log_level: info
config_path: /share/caddy2/Caddyfile
custom_binary_path: /share/caddy2/caddy
  1. Upcoming header
    I am also watching the upcoming headers, always trying to keep my server as secure as possible. :slight_smile:

Last but not least, thanks for sharing your setup for the DNS cert method, avoiding HTTP and HTTPS ports altogether. I’ll stick to the regular method though, until my DNS provider is supported.

Whenever you find a suitable or security update for our setup, don’t hesitate to share it!

3 Likes

Ah I was wondering about the non Caddyfile config. I will remove it. Thanks.

Also using lego_deprecated works fine so if your dns provider is supported there I would use it in preference to opening ports (particularly 80 and 443!)

I also use a non-standard port as shown and additionally my domain only has an AAAA record so I am pretty well obscured from view as well. This is only a nuisance if I am on a mobile network that doesn’t support IPv6 but then I can just use ZeroTier or even just my VPN to the router and I’m good. Fortunately my mobile provider and ISP both use IPv6 dual stack. Caddy works perfectly with this setup.

Not going to lie, I’ve been following these post for quite a bit of time on the HA site describing the caddy v2 upgrade and it still has me scratching my head. I’ve got errors with misconfigured set ups such as 403 and 400 and now a bind issue that kicks me right out. could someone help to diagnose?

caddy2 log:

Open source web and proxy server with automatic HTTPS
-----------------------------------------------------------
 Add-on version: 0.3.0
 You are running the latest version of this add-on.
 System: Home Assistant OS 5.11  (amd64 / qemux86-64)
 Home Assistant Core: 2021.2.3
 Home Assistant Supervisor: 2021.02.9
-----------------------------------------------------------
 Please, share the above information when looking for help
 or support in, e.g., GitHub, forums or the Discord chat.
-----------------------------------------------------------
[cont-init.d] 00-banner.sh: exited 0.
[cont-init.d] 01-log-level.sh: executing... 
Log level is set to INFO
[cont-init.d] 01-log-level.sh: exited 0.
[cont-init.d] done.
[services.d] starting services
[services.d] done.
INFO: Starting Caddy...
INFO: Setting DUCKDNS_TOKEN to <token>
INFO: Found custom Caddy at /share/caddy/caddy
v2.3.0 h1:fnrqJLa3G5vfxcxmOH/+kJOcunPLhSBnjgIvjXV/QTA=
INFO: Caddyfile found at /share/caddy/Caddyfile
{"level":"info","ts":1613411303.8377643,"msg":"using provided configuration","config_file":"/share/caddy/Caddyfile","config_adapter":""}
{"level":"info","ts":1613411303.8417926,"logger":"admin","msg":"admin endpoint started","address":"tcp/localhost:2019","enforce_origin":false,"origins":["localhost:2019","[::1]:2019","127.0.0.1:2019"]}
{"level":"info","ts":1613411303.8432853,"logger":"http","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
run: loading initial config: loading new config: http app module: start: tcp: listening on :8123: listen tcp :8123: bind: address already in use
[cont-finish.d] executing container finish scripts...
[cont-finish.d] 99-message.sh: executing... 
[cont-finish.d] 99-message.sh: exited 0.
[cont-finish.d] done.
[s6-finish] waiting for services.
[s6-finish] sending all processes the TERM signal.

my caddy add on config looks like this:

non_caddyfile_config:
  email: <my-email>@email.com
  domain: <my-domain>.duckdns.org
  destination: localhost
  port: 8123
args: []
env_vars:
  - name: DUCKDNS_TOKEN
    value: <token>
log_level: info

My Caddyfile looks like this:

{   
	email <my-email>@email.com
}

<my-domain>.duckdns.org:8123 {
    tls {
        dns lego_deprecated duckdns
        on_demand
	}
    header {
		Strict-Transport-Security "max-age=31536000; includeSubdomains"
		X-XSS-Protection "1; mode=block"
		X-Content-Type-Options "nosniff"
		X-Frame-Options "SAMEORIGIN"
		Referrer-Policy "same-origin"
		-Server
	}
    reverse_proxy localhost:8123
}

Also my config.yaml http section:

http:
  # Uncomment this to add a password (recommended!)
  # api_password: !secret http_password
  # 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
  # Uncomment this if you are using SSL/TLS, running in Docker container, etc.
  base_url: https://<my-domain>.duckdns.org:8123

Any help would be appreciated.

First thing I notice is it’s not using a custom caddy so the standard caddy doesn’t include the Duckdns addon. Not 100% sure but I think there is a read duckdns addon so you shouldn’t need deprecated anyway. In any case you need to build a custom caddy and copy the binary to the caddy directory as caddy with no extension and also make sure it’s executable.

Oops… Missed you are using a custom one.

I think the issue is way simpler… You are proxying 8123 to 8123…
run: loading initial config: loading new config: http app module: start: tcp: listening on :8123: listen tcp :8123: bind: address already in use. Which port are you forwarding in your router?

Your baseurl is ok. I would just remove the 8123 from here:

<my-domain>.duckdns.org: {
    tls {
        dns lego_deprecated duckdns
        on_demand
	}

and just forward 443 to 443 in your router. Then any https connection to your domain will get proxied to 8123. Personally I prefer to use a different port than 8123 externally hence my config and I would then forward that port xxxxx to xxxxx in your router and change the baseurl to reflect that new port.

I am seeing browser console errors with this… you?

Permissions-Policy “geolocation=(self), microphone=()”
everything seems to work still

Hi David, I appreciate your pointers - I’ve made the adjustment to the Caddyfile, but now it seems to hang at these portions of the certificate issuing, and then the add-on just resets:

{"level":"info","ts":1613444519.7058322,"msg":"using provided configuration","config_file":"/share/caddy/Caddyfile","config_adapter":""}
{"level":"info","ts":1613444519.7090364,"logger":"admin","msg":"admin endpoint started","address":"tcp/localhost:2019","enforce_origin":false,"origins":["localhost:2019","[::1]:2019","127.0.0.1:2019"]}
{"level":"info","ts":1613444519.7096941,"logger":"http","msg":"server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https_port":443}
{"level":"info","ts":1613444519.7099156,"logger":"http","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
{"level":"info","ts":1613444519.7106404,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["<my-domain>.duckdns.org"]}
{"level":"info","ts":1613444519.71106,"msg":"autosaved config","file":"/data/caddy/autosave.json"}
{"level":"info","ts":1613444519.711295,"msg":"serving initial configuration"}
{"level":"info","ts":1613444519.7121103,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc0000eaaf0"}
{"level":"info","ts":1613444519.7130206,"logger":"tls","msg":"cleaned up storage units"}

What is the meaning of the “has no tls policies”?

Thank you in advance!

can you restart the addon then post the full log (with redactions of course) also caddyfile

Sure…

[s6-init] making user provided files available at /var/run/s6/etc...exited 0.
[s6-init] ensuring user provided files have correct perms...exited 0.
[fix-attrs.d] applying ownership & permissions fixes...
[fix-attrs.d] scripts: applying... 
[fix-attrs.d] scripts: exited 0.
[fix-attrs.d] done.
[cont-init.d] executing container initialization scripts...
[cont-init.d] 00-banner.sh: executing... 
-----------------------------------------------------------
 Add-on: Caddy 2
 Open source web and proxy server with automatic HTTPS
-----------------------------------------------------------
 Add-on version: 0.3.0
 You are running the latest version of this add-on.
 System: Home Assistant OS 5.11  (amd64 / qemux86-64)
 Home Assistant Core: 2021.2.3
 Home Assistant Supervisor: 2021.02.9
-----------------------------------------------------------
 Please, share the above information when looking for help
 or support in, e.g., GitHub, forums or the Discord chat.
-----------------------------------------------------------
[cont-init.d] 00-banner.sh: exited 0.
[cont-init.d] 01-log-level.sh: executing... 
Log level is set to INFO
[cont-init.d] 01-log-level.sh: exited 0.
[cont-init.d] done.
[services.d] starting services
[services.d] done.
INFO: Starting Caddy...
INFO: Setting DUCKDNS_TOKEN to <token>
INFO: Found custom Caddy at /share/caddy/caddy
v2.3.0 h1:fnrqJLa3G5vfxcxmOH/+kJOcunPLhSBnjgIvjXV/QTA=
INFO: Caddyfile found at /share/caddy/Caddyfile
{"level":"info","ts":1613445482.821329,"msg":"using provided configuration","config_file":"/share/caddy/Caddyfile","config_adapter":""}
{"level":"info","ts":1613445482.8286877,"logger":"admin","msg":"admin endpoint started","address":"tcp/localhost:2019","enforce_origin":false,"origins":["127.0.0.1:2019","localhost:2019","[::1]:2019"]}
{"level":"info","ts":1613445482.8294985,"logger":"http","msg":"server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https_port":443}
{"level":"info","ts":1613445482.829688,"logger":"http","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
{"level":"info","ts":1613445482.8307972,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["<my-domain>.duckdns.org"]}
{"level":"info","ts":1613445482.831407,"msg":"autosaved config","file":"/data/caddy/autosave.json"}
{"level":"info","ts":1613445482.8318534,"msg":"serving initial configuration"}
{"level":"info","ts":1613445482.8322437,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc0009741c0"}
{"level":"info","ts":1613445482.8330128,"logger":"tls","msg":"cleaned up storage units"}

Caddyfile:

{   
	email <my-email>@email.com
}

<my-domain>.duckdns.org: {
    tls {
        dns lego_deprecated duckdns
        on_demand
	}
    header {
		Strict-Transport-Security "max-age=31536000; includeSubdomains"
		X-XSS-Protection "1; mode=block"
		X-Content-Type-Options "nosniff"
		X-Frame-Options "SAMEORIGIN"
		Referrer-Policy "same-origin"
		-Server
	}
    reverse_proxy localhost:8123
}

Thank you, I did take quite literal your take out the port post but left the : in there, I also tried a version without that too - but without much change to end result. Also tried the tls in Caddyfile to be just dns duckdns and also took out the “on_demand”, but that then had an API missing error (although I included both duckdns and lego deprecated in my Caddy build, just for good measure).

So you don’t need the : there unless you are specifying a port.
Did you check the caddy server site to see if there is a native addon for duckdns? I swear on their forum the other day I saw someone building caddy with that addon instead of lego-deprecated so I assume you checked for that? You may also need to check that you are providing the duckdns token in the right format.
If you want to use http validation instead of dns, you will need to forward 80-80 in your router as well.
Maybe @berichta can shed more light on the issue as well…

Hi David, I used the native duckdns add-on and it results in an API token error - I’m sure my fault, is the “DUCKDNS_Token” supposed to be “API_Token” instead?

logs:

[s6-init] making user provided files available at /var/run/s6/etc...exited 0.
[s6-init] ensuring user provided files have correct perms...exited 0.
[fix-attrs.d] applying ownership & permissions fixes...
[fix-attrs.d] scripts: applying... 
[fix-attrs.d] scripts: exited 0.
[fix-attrs.d] done.
[cont-init.d] executing container initialization scripts...
[cont-init.d] 00-banner.sh: executing... 
-----------------------------------------------------------
 Add-on: Caddy 2
 Open source web and proxy server with automatic HTTPS
-----------------------------------------------------------
 Add-on version: 0.3.0
 You are running the latest version of this add-on.
 System: Home Assistant OS 5.11  (amd64 / qemux86-64)
 Home Assistant Core: 2021.2.3
 Home Assistant Supervisor: 2021.02.9
-----------------------------------------------------------
 Please, share the above information when looking for help
 or support in, e.g., GitHub, forums or the Discord chat.
-----------------------------------------------------------
[cont-init.d] 00-banner.sh: exited 0.
[cont-init.d] 01-log-level.sh: executing... 
Log level is set to INFO
[cont-init.d] 01-log-level.sh: exited 0.
[cont-init.d] done.
[services.d] starting services
[services.d] done.
INFO: Starting Caddy...
INFO: Setting DUCKDNS_TOKEN to <token>
INFO: Found custom Caddy at /share/caddy/caddy
v2.3.0 h1:fnrqJLa3G5vfxcxmOH/+kJOcunPLhSBnjgIvjXV/QTA=
INFO: Caddyfile found at /share/caddy/Caddyfile
{"level":"info","ts":1613447995.9385211,"msg":"using provided configuration","config_file":"/share/caddy/Caddyfile","config_adapter":""}
run: adapting config using caddyfile: parsing caddyfile tokens for 'tls': /share/caddy/Caddyfile:7 - Error during parsing: missing API token
[cont-finish.d] executing container finish scripts...
[cont-finish.d] 99-message.sh: executing... 
[cont-finish.d] 99-message.sh: exited 0.
[cont-finish.d] done.
[s6-finish] waiting for services.
[s6-finish] sending all processes the TERM signal.

Caddyfile:

{   
	email <my-email>@email.com
}

<my-domain>.duckdns.org {
    tls {
        dns duckdns
        on_demand
	}
    header {
		Strict-Transport-Security "max-age=31536000; includeSubdomains"
		X-XSS-Protection "1; mode=block"
		X-Content-Type-Options "nosniff"
		X-Frame-Options "SAMEORIGIN"
		Referrer-Policy "same-origin"
		-Server
	}
    reverse_proxy localhost:8123
}

Thanks in advance! Also, I don’t think that it matters, but I have on a separate server a Nextcloud instance that I’m gearing up to dns challenge and add the token to txt for configuring but haven’t attempted yet to update the domain - probably not an issue, but I’ve seen weirder config go south!

So there was an example of someone using the duckdns addon on the caddyserver.com forum so search for that and it will likely make clear what you need to use… or open the addon in a text editor… it shouldn’t be that hard to see what format it wants…

Hi David, Looked at the example on the add on and got the log file below - still not serving up https, but a lot more promising!

6-init] making user provided files available at /var/run/s6/etc...exited 0.
[s6-init] ensuring user provided files have correct perms...exited 0.
[fix-attrs.d] applying ownership & permissions fixes...
[fix-attrs.d] scripts: applying... 
[fix-attrs.d] scripts: exited 0.
[fix-attrs.d] done.
[cont-init.d] executing container initialization scripts...
[cont-init.d] 00-banner.sh: executing... 
-----------------------------------------------------------
 Add-on: Caddy 2
 Open source web and proxy server with automatic HTTPS
-----------------------------------------------------------
 Add-on version: 0.3.0
 You are running the latest version of this add-on.
 System: Home Assistant OS 5.11  (amd64 / qemux86-64)
 Home Assistant Core: 2021.2.3
 Home Assistant Supervisor: 2021.02.9
-----------------------------------------------------------
 Please, share the above information when looking for help
 or support in, e.g., GitHub, forums or the Discord chat.
-----------------------------------------------------------
[cont-init.d] 00-banner.sh: exited 0.
[cont-init.d] 01-log-level.sh: executing... 
Log level is set to INFO
[cont-init.d] 01-log-level.sh: exited 0.
[cont-init.d] done.
[services.d] starting services
[services.d] done.
INFO: Starting Caddy...
INFO: Found custom Caddy at /share/caddy/caddy
v2.3.0 h1:fnrqJLa3G5vfxcxmOH/+kJOcunPLhSBnjgIvjXV/QTA=
INFO: Caddyfile found at /share/caddy/Caddyfile
{"level":"info","ts":1613455749.4342697,"msg":"using provided configuration","config_file":"/share/caddy/Caddyfile","config_adapter":""}
{"level":"info","ts":1613455749.437459,"logger":"admin","msg":"admin endpoint started","address":"tcp/localhost:2019","enforce_origin":false,"origins":["localhost:2019","[::1]:2019","127.0.0.1:2019"]}
{"level":"info","ts":1613455749.4380872,"logger":"http","msg":"server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https_port":443}
{"level":"info","ts":1613455749.4382966,"logger":"http","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
{"level":"info","ts":1613455749.4392517,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["<my-domain>.duckdns.org"]}
{"level":"info","ts":1613455749.4399426,"msg":"autosaved config","file":"/data/caddy/autosave.json"}
{"level":"info","ts":1613455749.4403546,"msg":"serving initial configuration"}
{"level":"info","ts":1613455749.4408722,"logger":"tls.obtain","msg":"acquiring lock","identifier":"<my-domain>.duckdns.org"}
2021/02/15 22:09:09 [INFO][FileStorage:/ssl/caddy] Lock for 'issue_cert_<my-domain>.duckdns.org' is stale (created: 2021-02-15 09:05:10.358163477 -0800 PST, last update: 2021-02-15 09:43:26.46172407 -0800 PST); removing then retrying: /ssl/caddy/locks/issue_cert_<my-domain>.duckdns.org.lock
{"level":"info","ts":1613455749.44215,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc00049cbd0"}
{"level":"info","ts":1613455749.442851,"logger":"tls","msg":"cleaned up storage units"}
{"level":"info","ts":1613455749.4434402,"logger":"tls.obtain","msg":"lock acquired","identifier":”<my-domain>.duckdns.org"}
{"level":"info","ts":1613455749.4639788,"logger":"tls.issuance.acme","msg":"waiting on internal rate limiter","identifiers":["<my-domain>.duckdns.org"]}
{"level":"info","ts":1613455749.4643369,"logger":"tls.issuance.acme","msg":"done waiting on internal rate limiter","identifiers":["<my-domain>.duckdns.org"]}
{"level":"info","ts":1613455750.4677846,"logger":"tls.issuance.acme.acme_client","msg":"trying to solve challenge","identifier":"<my-domain>.duckdns.org","challenge_type":"dns-01","ca":"https://acme-v02.api.letsencrypt.org/directory"}
{"level":"info","ts":1613455754.0069392,"logger":"tls.issuance.acme.acme_client","msg":"validations succeeded; finalizing order","order":"https://acme-v02.api.letsencrypt.org/acme/order/112679045/7937849578"}
{"level":"info","ts":1613455754.926826,"logger":"tls.issuance.acme.acme_client","msg":"successfully downloaded available certificate chains","count":2,"first_url":"https://acme-v02.api.letsencrypt.org/acme/cert/043dfe191bf902d9d756a69d696993272700"}
{"level":"info","ts":1613455754.9281528,"logger":"tls.obtain","msg":"certificate obtained successfully","identifier":"<my-domain>.duckdns.org"}
{"level":"info","ts":1613455754.9284616,"logger":"tls.obtain","msg":"releasing lock","identifier":"<my-domain>.duckdns.org"}

I put the token behind the duckdns in Caddyfile and removed the on demand reference. I also never answered the question about ports: 443:443,80:80,443:8123. Thank you, Daniel

Well it got a certificate and downloaded it… Why are you forwarding 443 to 8123 and 443 to 443? (And 80 to 80)???
You should only have 443 to 443 so https gets to the HA machine. The reverse-proxy will take care of everything else!

Can you show your amended Caddyfile (redacted) and Addon config - it might help someone else.

I used the on-demand only because I have multiple sub domains and it wasn’t successfully getting certificates. I doubt that was causing any issue.

Also check my header settings above… you might have issues with iFrames with your settings. Also check that security check site that you are seeing A+ ratings.