Configuring caddy to refuse connections from unconfigured domains

1. Caddy version (caddy version):

caddy docker (tag:latest)

2. How I run Caddy:

I run caddy as a reverse proxy in a test environment

a. System environment:

Docker

d. My complete Caddyfile or JSON config:

tv.<domain>.com.au {
    reverse_proxy 10.20.10.52:8096
}

3. The problem I’m having:

Everything is working as expected at the moment, the issue is I can’t seem to find documentation on what I want to do.

The problem I have is that I am testing caddy as a replacement for traefik, due to our company complaining that traefik returns a self signed certificate on invalid domains and subdomains. It’s actually a crazily dumb reason to look at alternatives, but I’m already preferring caddy’s simplicity.

What I would like is to have any domain or subdomain that isn’t actively configured (including directly accessing the server via the IP address) to be refused or timed out. Preferably I don’t want the reverse proxy to respond at all, as if it was just dropping the request. Or failing that, just giving a refused connection response. What I am trying to avoid is getting the SSL_INTERNAL_ERROR_ALERT that is currently given on an unconfigured attempt at accessing the server.

I’m not sure that’s possible with Caddy as-is. The domain is figured out during the TLS handshake using SNI. If Caddy can’t satisfy the handshake due to not managing that domain, it fails the handshake and that’s the error the browser sees.

It may be possible with Project Conncept (which is currently only available to sponsors) by inspecting the request at the TCP level before starting the TLS handshake. @matt could clarify here, I might be wrong. If this is something that’s interesting to you, please consider sponsoring:

1 Like

I’m not sure what you’re asking for is possible. No server can know which host is being accessed until the connection is established. You can refuse connections based on remote IPs but that’s not what you’re looking for.

So the connection must be accepted at least. And some bytes need to be read. Caddy can, as-is, reject connections during the TLS handshake if looking for a particular host (server name) you don’t support (or yes, Conncept can do this too), but once a TLS handahake has started if we close the connection clients will raise a TLS error because that’s what it is. Or you could allow the connection and Encrypt it and then block the http host, and then close the connection. Caddy can do this too.

But I’m not aware of any software that can magically predict what the host will be before the connection is even established…

1 Like

Thanks, to be honest that’s what I expected. I didn’t know if the domain information was transmitted before or during the handshake. Obviously if it’s during, there’s nothing to be done.

What’s unfortunate is trying to explain to the checkbox tickers of our auditors how a reverse proxy works is like trying to explain it to a brick wall. They’ll just stare blankly at you for a while, and then reiterate that their scanner is showing a warning.

I’ll go ahead and implement caddy, and hopefully a TLS error does not get picked up on a scan like a self signed cert does. Honestly, the amount of man-hours spent implementing traefik, compared to the amount of man-minutes spent implementing caddy, made the switch a done deal anyway.

4 Likes

Best of luck then :slight_smile: Glad it can (hopefully) save you some time!

I had another thought, is there a way to set one of the domains to be the “default”, even if the certificate doesn’t match the domain request?

So the idea is that I set up a basic static html page saying “these are not the subdomains you are looking for”, with a valid SSL for “uhoh.domain.com.au”. Is there a way to configure all unconfigured requests to default to that site? The idea being that it will obviously throw an error (the certificate doesn’t match the domain request, but it’s still a valid certificate), but that is most likely what the auditors will expect and won’t chuck a hissy over it.

Yep, sort-of. There’s the default_sni global option that would do this if the client doesn’t specify the domain at all. But that doesn’t solve your issue, because if Caddy can’t complete the TLS handshake, the client will still throw TLS errors.

1 Like

I am working under the assumption that the auditor’s scanners are expecting a static website. IE: “blah.domain.com.au” is the only ssl certificate that will ever be provided, and as long as it is valid, they don’t care even if they are making an invalid request.

If the default_sni meets those parameters, I think that would be enough to dodge the checkmark police.

If you’re only worried about subdomains you actually control, you can make a server block like *.domain.com.au with either On-Demand TLS (so Caddy can fetch a valid cert for that domain on the fly) or with a wildcard cert (requires using the DNS challenge), and serve an error page in that block. Caddy will sort domains that aren’t wildcards first, so it will be used as a fallback for unconfigured domains, but you still need to own that domain.

Don’t forget to set reasonable limits if using the on-demand approach because you could open yourself up to a denial-of-service attack if someone tries to make requests against a ton of valid subdomains.

1 Like

Thanks, but I don’t think I have to go that far. My assumption is that they are testing ports directly on the IP address. IE: they go to [our IP range]:443 and see what happens. In that situation, a static site will also fail because the certificate they provide won’t match the domain. But, at least it would provide some sort of valid SSL. I suspect that’s enough to satisfy the scanner.

2 Likes

Oh boy, I hope your employers haven’t hired an auditor that expects an IP address to have publicly verifiable HTTPS. But from the way you’ve described the folk operating the scanners…

I’m crossing my fingers for you! :crossed_fingers:

2 Likes