Default certificate when TLS on_demand returns non HTTP 200 code

1. The problem I’m having:

I want to specify a default certificate which is used when the service asked by TLS on_demand feature returns a non HTTP 200 code (=says issuing certificates for this domain is prohibited).

2. Caddy version:

v2.7.4 h1:J8nisjdOxnYHXlorUKXY75Gr6iBfudfoGhrJ8t7/flI=

4. How I installed and ran Caddy:

I installed it manually and run it via systemd unit.

a. System environment:

Debian bookworm, amd64

b. Command:

/usr/local/bin/caddy run --environ --config /etc/caddy/Caddyfile

c. My complete Caddy config:

{
	# Don't redirect everything to TLS to keep the number of issued
	# certificates low
	auto_https disable_redirects

	on_demand_tls {
		ask http://localhost:5555/
	}
}

(default-redirector) {
	log {
		output file /var/log/caddy/redirector.log {
			roll_size 1gb
			roll_local_time
			roll_keep 0
			roll_keep_for 168h
		}
	}

	@default_redirect {
		method GET
	}

	route {
		handle @default_redirect {
			redir https://www.example.org/?utm_source={host}&utm_medium=referral&utm_campaign=redirector-service 302
		}

		# Every request hitting the following redirect is not doing
		# a normal GET request. Therefore we are forcing
		# HTTP status 303 to prevent redirect target from receiving
		# unwated POST calls for example.
		redir https://www.example.org/?utm_source={host}&utm_medium=referral&utm_campaign=redirector-service 303
	}
}

:80 {
	# Bind to specific IP because this host has multiple IP addresses
	# and not every address should be used by caddy.
	bind 203.0.113.2

	import default-redirector
}

:443 {
	# Bind to specific IP because this host has multiple IP addresses
	# and not every address should be used by caddy.
	bind 203.0.113.2

	tls {
		on_demand
	}

	import default-redirector
}

We have a big site example.org and thousands of alias domains pointing to this site. A typical alias site’s DNS zone will look like

example.de.           300     IN      A       203.0.113.2
*.example.de.         300     IN      CNAME   redirect.to.example.org.

The service running at http://localhost:5555 will verify that the domain specified is really pointing at us.

Due to wildcard usage the service is also blocking certain unwanted subdomains and ensures that we don’t allow undefined levels of subdomains. But these details shouldn’t matter.

Anyway, so whenever the checking service forbids Caddy to issue a certificate, Caddy is currently aborting the connection.

However, I want that Caddy will use a default certificate in that case and still do the redirect (yes I know that most clients will probably display a certificate error but that’s fine for me. It’s still giving users a hint and in case someone ignores the error, I want them to reach our main site). Or in other words the default behavior when I would use an Apache/nginx server, where I would set a default SSL vhost which would get used in this case.

Is that possible with Caddy in combination with TLS on_demand?

Second question I have: Can I get Caddy to query check service also for HTTP requests? Like we already have implemented a bunch of checks/logic into the check service to not redirect everything. It would be cool if we could re-use this logic also for HTTP calls.

Hey Igor,

Welcome back! I’d like to answer your questions and understand your use case a bit better.

I don’t think so, if I understand you correctly – not yet. But if we are to implement that I definitely want to chat first.

Do you have a moment? You can book a time at https://matt.chat – I won’t charge for this call :slight_smile: We’ll discuss your deployment and make sure I understand and then I’ll let you know what options there are for making this work for you. It’ll likely involve a sponsorship to get this done in a reasonable time (so we can prioritize it).

I’m not quite sure I follow – what is the “check service”? Can you explain more what you want to do? (Or we can also talk about this on a call.)

Hi Matt,

by “check service” I mean the HTTP endpoint that receives ?domain=example.com as a parameter in on_demand_tls directive. For example, in my config

	on_demand_tls {
		ask http://localhost:5555/
	}

http://localhost:5555 is a custom web service which I call “check service” (because it checks if Caddy is authorized to obtain a certificate for queried name).

Like said, it is a custom service. It is currently checking if it is our domain, checks DNS delegation but because we use DNS wildcard records to point to our redirector service it will also check for “unwanted” names. For example we see HTTP requests for ns1.example.de, mail.example.de or phpmyadmin.example.de (usually bots or application probing something) which we don’t want to answer.

Now you maybe wonder why we use DNS wildcard records: Because this service is primarily intended for people. We want to catch typos people do when trying to access our site. For example imagine you type ww.example.de, wwe.example.de or wwww.example.de in your address bar instead of www.example.de. Instead of getting an error “Cannot open…”, the redirector service will catch the wrong name and bring you to us like you wanted (especially handy for mobile user where it is often a pain to type the correct address or spot an error). Using DNS wildcard records make our life much more easier (a typical alias/typo domain will only require two records). It allows us to capture all kinds of variations, even those we can’t even think of at the moment.

If you use our config as shown above at the moment, a call to https://mail.example.de/would be aborted (because our check service will not allow Caddy to obtain a certificate for mail subdomain) but a call to http://mail.example.de/ would be possible.

It would be nice if HTTP and HTTPS requests for the same domain would result in same results. Because we already have created the check service which is required for Caddy’s on_demand_tls feature, I thought it would be great if we could re-use this solution also for HTTP. Otherwise we would end up with two solutions and would need to keep logic in sync between them.

Sure, I can book a call if you still have some questions. But I doubt that I’ll get permission for a sponsorship, especially when this feature would have a limited use: Like said, falling back to the “default SSL vhost” will always result in an SSL error for users. While I believe this would be technically the better solution instead of aborting the connection (according to logs it looks like most browsers will automatically retry multiple times instead of giving up), it is hard to ask your employer for money for something if it is just “the more correct way to fail” :slight_smile:

I thought that using fallback_sni or something like that could do the trick out of the box…

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.