caddy: Reject request with IP address as hostname (unless defined in Caddyfile)

1. What version of Caddy are you using (caddy -version)?

0.10.11

2. What are you trying to do?

Serve a HTTPS host that only answers to a specific domain name, while rejecting all other requests and not revealing the very existence of that domain name to anyone who isn’t explicitly asking for it

3. What is your entire Caddyfile?

domain.example.com {
  root /var/www
  gzip
  tls email@example.com
}

4. How did you run Caddy (give the full command and describe the execution environment)?

/usr/local/bin/caddy -log stdout -agree=true -conf=/etc/caddy/Caddyfile -root=/var/tmp

5. Please paste any relevant HTTP request(s) here.

curl https://1.2.3.4 # literal IP address of domain.example.com

6. What did you expect to see?

Caddy rejecting the request during TLS server hello, since there is no host defined for the literal IP address, and not revealing the domain name of any host it is serving

Preferably, there should be an option to reject any client request for a literal IP address (note that SNI spec does not allow literal IPv4 or IPv6 address to be used in SNI), reject any client request for a domain name but without SNI, and reject any client request for a domain name with SNI but not matching any of the domain names explicitly defined in the Caddyfile

7. What did you see instead (give full error messages and/or log)?

Caddy accepting the request, finishing the TLS server hello and then sending the TLS certificate of domain.example.com to the client, thus revealing that it is serving a host at that domain name

In other words, Caddy is leaking the domain name (which cannot be acquired through reverse DNS from the IP address) to anyone who is scanning the HTTPS port of the server

8. How can someone who is starting from scratch reproduce the bug as minimally as possible?

See above

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 15 (3 by maintainers)

Most upvoted comments

While I personally don’t believe this kind of secrecy is important or effective against malicious port scans or crawlers, you could prevent “leakage” of valid domains served at an IP address via certificate names by specifying a site label in Caddy for that IP address as a host and using a self-signed certificate.

https://1.2.3.4 {
  tls self_signed
  status 403 /
}

A HTTPS request to this site should return something like:

curl -kv https://[snip]/
*   Trying [snip]...
[...]
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES256-GCM-SHA384
* ALPN, server accepted to use h2
* Server certificate:
*  subject: O=Caddy Self-Signed
*  start date: Mar 15 07:11:49 2018 GMT
*  expire date: Mar 22 07:11:49 2018 GMT
*  issuer: O=Caddy Self-Signed
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fed8d808400)
> GET / HTTP/2
> Host: [snip]
> User-Agent: curl/7.54.0
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
< HTTP/2 403
< content-type: text/plain; charset=utf-8
< server: Caddy
< x-content-type-options: nosniff
< content-length: 14
< date: Thu, 15 Mar 2018 07:13:18 GMT
<
403 Forbidden

This is alongside another site with a current valid LetsEncrypt certificate.

Haven’t tested this one, but I think this would work the same?

https:// {
  tls self_signed
  status 403 /
}

Should catch every single HTTPS host that isn’t otherwise defined in your Caddyfile.