caddy icon indicating copy to clipboard operation
caddy copied to clipboard

Idea: configuration assertions

Open simon04 opened this issue 3 years ago • 3 comments

Very often the desired functionality of a webserver configuration is clear. When a user accesses the URL 1, she should get the file X from /var/foo. The URL 2 is proxies to the backend URL 3 and headers Y and Z are added.

Correctly configuring the webserver is more challenging. As a non-expert in caddy, one has to try multiple configurations and debug the proxy backend requests.

Can we add a configuration directive assert request_matcher response_matcher (using matchers)? Those assertions would be checked after parsing/loading the configuration. Some vague examples:

example.com {
  root /var/foo
  reverse_proxy /api/* localhost:9005 {
    header_up Foo "Bar"
  }
  assert https://example.com/cat.jpg file:/var/foo/cat.jpg
  assert https://example.com/does-not-exist { status_code 404 }
  assert https://example.com/api/ping { reverse_proxy http://localhost:9005/ping; header Foo "Bar" }
}

What you you think?
Thanks!

simon04 avatar Jan 19 '22 18:01 simon04

Interesting idea.

In theory this would probably run during the Validate() phase (see the lifecycle here: https://caddyserver.com/docs/architecture#module-lifecycle). But the trouble is that some of those assertions are only possible once the app is actually Start()ed because the HTTP app only binds to ports when starting, not when provisioning/validating.

So for example the caddy validate command could not run these assertions, they would need to happen right at the end of the HTTP app's Start() and maybe return an error if any of them fail.

Another thing is that asserting an https:// request would probably almost always fail on first run, because Caddy fetches certificates from ACME issuers asynchronously, so it would likely not have the certificate yet to be able to make an HTTPS connection to itself.

Also I think for this to be robust, we would need to be able to craft any kind of request, because request matching isn't simply by URL, it also matters what HTTP method is used, headers, sometimes body, etc. So the syntax might need to be something like this:

assert {
	request {
		method <method>
		uri <uri>
		header <field> <value>
		body <body>
	}
	response {
		status <status>
		header <field> <value>
	}
}

To me, this seems like it would be pretty complex overall, not sure we want the maintenance burden. This could be implemented as a plugin, but admittedly it would be less useful if it's not included in the standard distribution of Caddy, since users would have to go out of their way to add it to a custom build.

francislavoie avatar Jan 19 '22 21:01 francislavoie

Nice idea, I had a similar idea while writing Caddy 2 a couple years ago but decided to shelve it until the project was more mature and I had more time (like that'll happen)... OR until a customer needs it and pays for it. 😁

I think a more wholistic solution is to have tests that Caddy runs during config reloads. Any time the config is loaded, it runs tests defined ... somewhere/somehow ... and if the tests fail with the new config, it rolls back to the previous config.

I don't think defining the tests in the config file is going to be flexible enough. I envision a separate assertion file and API endpoints for this.

mholt avatar Jan 20 '22 23:01 mholt

Second guessing the notion that a separate API or assertions file is needed. Maybe it should all go within one big config document. Hmmmm. Will need to evaluate pros and cons.

mholt avatar Apr 20 '22 04:04 mholt