Allow direct access to server only by Hostname not by IP

1. Caddy version (caddy version):

2.4.6 (Latest)

2. How I run Caddy:

I am running it inside a docker container. I just supply the Caddyfile and ssl folder to docker mount path. And then I start my container using below command.

a. System environment:

Docker on Ubuntu v20 on Azure VM.

b. Command:

docker-compose up

c. Service/unit/compose file:

version: "3.7"

services:
  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./caddy/caddy-data/Caddyfile:/etc/caddy/Caddyfile
      - ./caddy/ssl:/etc/caddy/ssl
      # - /data/caddy/data:/data # Optional
      # - /data/caddy/config:/config # Optional
    networks:
      - primary-net
networks:
  primary-net:

d. My complete Caddyfile or JSON config:

portainer.winoff.ml {
	tls /etc/caddy/ssl/winoffrg.ml/cert.pem /etc/caddy/ssl/winoffrg.ml/cert.key {
		client_auth {
			mode require_and_verify
			trusted_ca_cert_file /etc/caddy/ssl/origin-pull-ca.pem
	  	}
	}
	reverse_proxy portainer:9000
}

3. The problem I’m having:

  1. I want to block the access to site by direct IP Address. For example if somone enter’s by IP Address I just shouldn’t resolve the request and simply block it with error code 444. And allow only by hostname. I also don’t want if someone opens postman and manually set Host header and get’s the response.
  2. Currently, whenever I curl the IP Address of server the response in terminal is
* Trying 20.198.67.202:443...
* TCP_NODELAY set


* connect to 20.198.67.202 port 443 failed: Connection timed out
* Failed to connect to 20.198.67.202 port 443: Connection timed out
* Closing connection 0
curl: (28) Failed to connect to 20.198.67.202 port 443: Connection timed out

and in brower the response is ERR_SSL_PROTOCOL_ERROR;

4. Error messages and/or full log output:

{"level":"error","ts":1644679326.8757384,"logger":"http.log.error","msg":"dial tcp: lookup portainer on 127.0.0.11:53: server misbehaving","request":{"remote_addr":"172.70.189.146:28474","proto":"HTTP/2.0","method":"GET","host":"portainer.winoff.ml","uri":"/favicon.ico","headers":{"X-Forwarded-For":["2405:201:4022:30c1:7d32:c499:1dba:e7af"],"Sec-Ch-Ua":["\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"98\", \"Google Chrome\";v=\"98\""],"Sec-Fetch-Mode":["no-cors"],"Accept-Language":["en,en-US;q=0.9"],"Cookie":["cf_use_ob=0"],"Sec-Fetch-Dest":["image"],"Cdn-Loop":["cloudflare"],"Cf-Connecting-Ip":["2405:201:4022:30c1:7d32:c499:1dba:e7af"],"X-Forwarded-Proto":["https"],"Sec-Ch-Ua-Mobile":["?0"],"Accept":["image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"],"Sec-Fetch-Site":["same-origin"],"Accept-Encoding":["gzip"],"Cf-Ray":["6dc6cf8009856bf7-SIN"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Cf-Ipcountry":["IN"],"Cf-Visitor":["{\"scheme\":\"https\"}"],"Dnt":["1"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36"],"Referer":["https://portainer.winoff.ml/"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","proto_mutual":true,"server_name":"portainer.winoff.ml","client_common_name":"origin-pull.cloudflare.net","client_serial":"590972600064187615923117765527414994083281880359"}},"duration":0.009194602,"status":502,"err_id":"v0iqkc87m","err_trace":"reverseproxy.statusError (reverseproxy.go:886)"}

5. What I already tried:

6. Links to relevant resources:

  1. Filtering direct IP accesses (" - #4 by francislavoie
  2. https://github.com/caddyserver/caddy/issues/3815

7. Other Information:

  1. My DNS is managed by cloudflare where I have just added an A record for portainer.winoff.ml for 20.198.67.202.
  2. On portainer.winoff.ml I am getting the correct response. If Issue A is solved I believe B will be also solved automatically. I just don’t want anyone to access site from IP Address.
  3. On my Azure only port 443 is open.

I’m not sure where you got the idea of error code 444, that’s a non-standard HTTP status code, only used by nginx.

Caddy’s default behaviour when you configure it with a domain is to not match requests that don’t have that domain in the Host header.

It’s not possible to “block someone manually setting the Host header”, because that is what the browser does when you put the domain in the URL.

Browsers also set the TLS-SNI field in the TLS handshake so that the server can determine which certificate to use to complete the handshake (because the Host header is part of the request payload which is encrypted, otherwise the server would have no way to know which certificate to use for which request).

That’s normal, that means the TLS handshake failed because the browser either couldn’t trust the certificate it was sent from the server, or it didn’t match the hostname it sent in the request (if you made a request with an IP address but the server responds with a certificate for a particular domain and not that IP address, then it fails).

This means that Caddy wasn’t able to resolve the name portainer with Docker’s built-in DNS server to an IP address. I’m not sure why that would happen, but that’s not a problem with Caddy, it’s a problem with your Docker setup.

Thanks a lot for kind and quick response.

Ah Ok Got it! I just wanted to create the same environement as of nginx.

Understood, This now sounds obvious to me. Thanks a lot for kind explanation :smile:
So, Is there any way I can stop showing that error If someone open the site via IP Address. Like show the message “Direct Access via IP Address is not Allowed”. and hide that SSL Error.

Is this because my SSL Certificate is configured only for specific domian *.example.com and example.com, So because of that when accessed via IP it shows us that SSL Could not be found since the host didn’t matched.

No, there’s no way to respond with anything, because the browser won’t trust anything the server sends when the browser made a request over HTTPS, if the handshake failed.

Right. Caddy can either automate issuance of certificates for you (Automatic HTTPS) or use certificates you give it, and it’ll try to match the request’s hostname with one of the loaded certificates. If none match, then the handshake can’t complete, and the client will show an error.

1 Like

Understood! Thanks, So nothing can be done? Like custom error on anything. Does this happen with most sites? Like maybe anything we could show an error from our side, not the default SSL Error.
And leaving it as such, Could it anyhow harm the server? Or can someone exploit it?

Would be very thankfull if you can answer the above questions as well :smile:

Not really. Just don’t tell people to connect with the IP address. Don’t link to your server by IP address publically.

Yeah.

There’s no harm. This is the safest possible behaviour. A custom error would require data to be sent unencrypted which would defeat the purpose of HTTPS.

1 Like

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