caddyserver / caddy

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
https://caddyserver.com
Apache License 2.0
55.48k stars 3.91k forks source link

caddytls: fix permission requirement with AutomationPolicy #6328

Closed willnorris closed 1 month ago

willnorris commented 1 month ago

Certificate automation has permission modules that are designed to prevent inappropriate issuance of unbounded or wildcard certificates. When an explicit cert manager is used, no additional permission should be necessary. For example, this should be a valid caddyfile:

https:// {
  tls {
    get_certificate tailscale
  }
  respond OK
}

This is accomplished when provisioning an AutomationPolicy by tracking whether there were explicit managers configured directly on the policy (in the ManagersRaw field). Only when a number of potentially unsafe conditions are present AND no explicit cert managers are configured is an error returned.

The problem arises from the fact that ctx.LoadModule deletes the raw bytes after loading in order to save memory. The first time an AutomationPolicy is provisioned, the ManagersRaw field is populated, and everything is fine.

An AutomationPolicy with no subjects is treated as a special "catch-all" policy. App.createAutomationPolicies ensures that this catch-all policy has an ACME issuer, and then calls its Provision method again because it may have changed. This second time Provision is called, ManagesRaw is no longer populated, and the permission check fails because it appears as though the policy has no explicit managers.

Address this by storing a new boolean on AutomationPolicy recording whether it had explicit cert managers configured on it.

Also fix an inverted boolean check on this value when setting failClosed.

Updates #6060 Updates #6229 Updates #6327

francislavoie commented 1 month ago

Oh wow, that's super subtle and tricky. Nice find.

I wonder if we should be resolving the underlying problem here though, i.e. it being Provision'd twice. That really should only happen one time. I'm not sure if we have a good way of doing that though. Maybe the catch-all one would need to be a totally fresh instance instead of re-provisioning? Maybe too complex.

But this workaround is probably sufficient for now. Thanks!

willnorris commented 1 month ago

I wonder if we should be resolving the underlying problem here though, i.e. it being Provision'd twice.

Yeah, I had that thought as well. I wasn't sure if it's generally best practice to avoid provisioning a module multiple times. Though for what it's worth, this particular logic regarding re-provisioning the catch-all automation policy is from 2020, so it's pretty well established.

mholt commented 1 month ago

Ahh okay, so yeah... we do reprovision APs at times because we need to update the underlying CertMagic configs.

Let's give this a try... not really sure a cleaner/simpler way to do it right now, but would be interested with revisiting it if we find one more elegant :)