Handling requests to IP and unused subdomains

1. Caddy version (caddy version):

v2.1.1 h1:X9k1+ehZPYYrSqBvf/ocUgdLSRIuiNiMo7CvyGUQKeA=

2. How I run Caddy:

docker

a. System environment:

  • OS: Ubuntu 20.04 LTS
  • docker version: 19.03.12

b. Command:

docker-compose up -d --force-recreate caddy

c. Service/unit/compose file:

caddy:
    image: caddy:2
    container_name: caddy
    volumes:
      - /mnt/zfsMirror/caddy/sites:/usr/share/caddy:ro
      - /mnt/zfsMirror/minecraft/BTL/plugins/dynmap/web:/var/www/dynmap:ro
      - /mnt/zfsMirror/caddy/data:/data
      - /mnt/zfsMirror/caddy/config:/config
      - /mnt/zfsMirror/caddy/logs:/var/log
      - /mnt/zfsMirror/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
      - /mnt/storage/DOWNLOADS:/downloads:ro
    ports:
      - 443:443
      - 80:80
    depends_on:
      - "php-fpm"
    networks:
      - web
    restart: always

d. My complete Caddyfile or JSON config:

(logging) {
    log {
        output file /var/log/{args.0}.log {
            roll_size 1gb
            roll_keep 5
            roll_keep_for 720h
        }
    }
}

(defaults) {
    import logging main

    root * {args.0}
    templates
    file_server
}

(redirect) {
    @used {
        expression {http.request.host.labels.2} in ['joeisthebest', 'blamethelatency']
        expression {http.request.host.labels.3} in ['map', 'blog', '']
	}

	redir @used https://{host}{uri} 308
}

# redirect to https
http:// {
    import logging main

    import redirect

    redir https://joeisthebest.mooo.com{uri} 308
}

#https:// {
#    import logging main
#    
#    tls internal
#    
#    import redirect
#    
#    redir https://joeisthebest.mooo.com{uri} 308
#}

joeisthebest.mooo.com {  
    import defaults /usr/share/caddy/joeisthebest
    
    redir /downloads /downloads/
    handle /downloads/* {
        root * /

        basicauth {
            user <passwordhash>
        }

        file_server browse
    }
}

blog.joeisthebest.mooo.com {
    import logging main
    
    reverse_proxy ghost:2368

    @notAllowed {
        path /ghost/*
        not {
            remote_ip 192.168.10.0/24
        }
    }

    respond @notAllowed 403 {
        close
    }
}

blamethelatency.mooo.com {
    import defaults /usr/share/caddy/blamethelatency
}

map.blamethelatency.mooo.com {
    root * /var/www/dynmap
    
    import logging dynmap

    php_fastcgi php-fpm:9000
    file_server
}

3. The problem I’m having:

I would like to redirect requests made to IP or subdomains I am not using. The current behavior is described in list below.

  • If a request is made to https://notused.joeisthebest.mooo.com, error http: TLS handshake error from 192.168.10.1:7353: no certificate available for 'notused.joeisthebest.mooo.com' is printed to stdout.
  • If a request is made to https://50.34.48.190 the error is http: TLS handshake error from 192.168.10.1:7359: no certificate available for '172.19.0.2'
  • if a request is made to http://50.34.48.190:443/, error is http 400 Client sent an HTTP request to an HTTPS server.

Ideally the expected behavior would be as the list below (since it has to establish TLS before redirecting it should use an internal cert or one of the existing ones from letsencrypt. It’s okay if the browser does not trust the cert for the IP/hostname prior to redirect, since the redirect should take it to a hostname/cert that work):

4. Error messages and/or full log output:

{"level":"info","ts":1595965605.9427545,"msg":"using provided configuration","config_file":"/etc/caddy/Caddyfile","config_adapter":"caddyfile"}
{"level":"info","ts":1595965605.9495392,"logger":"admin","msg":"admin endpoint started","address":"tcp/localhost:2019","enforce_origin":false,"origins":["localhost:2019","[::1]:2019","127.0.0.1:2019"]}
{"level":"info","ts":1595965605.9502964,"logger":"http","msg":"server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https_port":443}
{"level":"info","ts":1595965605.9503555,"logger":"http","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
2020/07/28 19:46:45 [INFO][cache:0xc00020e6c0] Started certificate maintenance routine
{"level":"info","ts":1595965605.9574983,"logger":"http","msg":"server is listening only on the HTTP port, so no automatic HTTPS will be applied to this server","server_name":"srv1","http_port":80}
{"level":"warn","ts":1595965605.9575257,"logger":"http","msg":"user server is listening on same interface as automatic HTTP->HTTPS redirects; user-configured routes might override these redirects","server_name":"srv1","interface":"tcp/:80"}
{"level":"info","ts":1595965605.9616077,"logger":"tls","msg":"cleaned up storage units"}
{"level":"info","ts":1595965605.9617136,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["map.blamethelatency.mooo.com","blog.joeisthebest.mooo.com","blamethelatency.mooo.com","joeisthebest.mooo.com"]}
{"level":"info","ts":1595965605.9756696,"msg":"autosaved config","file":"/config/caddy/autosave.json"}
{"level":"info","ts":1595965605.9756875,"msg":"serving initial configuration"}
2020/07/28 20:04:34 http: TLS handshake error from 104.236.247.64:54849: no certificate available for '172.19.0.2'
2020/07/28 20:09:00 http: TLS handshake error from 192.168.10.1:7352: no certificate available for 'notused.joeisthebest.mooo.com'
2020/07/28 20:09:00 http: TLS handshake error from 192.168.10.1:7353: no certificate available for 'notused.joeisthebest.mooo.com'
2020/07/28 20:11:25 http: TLS handshake error from 192.168.10.1:7358: no certificate available for '172.19.0.2'
2020/07/28 20:11:25 http: TLS handshake error from 192.168.10.1:7359: no certificate available for '172.19.0.2'
2020/07/28 20:20:52 http: TLS handshake error from 192.168.10.1:7383: no certificate available for '172.19.0.2'
2020/07/28 20:20:52 http: TLS handshake error from 192.168.10.1:7384: no certificate available for '172.19.0.2'

5. What I already tried:

My attempts to handle this are commented out in the caddyfile, if used it works to a point, but will overwrite my other site blocks, which is not what I want. If I use https://50.34.48.190 as the site block it does not seem to be used for requests to https://50.34.48.190/.

You can see my site block for http:// which handles requests made to http://50.34.48.190, and http://notused.joeisthebest.mooo.com for example.

I think the issue here is that https:// always superscedes the more specific site blocks. Any ideas how to prevent this, or achieve my goal in a different way?

Note, I have A records for *.joeisthebest.mooo.com and *.blamethelatency.mooo.com from freedns, which Caddy does not have a module for currently. As such requests to get a cert for *.joeisthebest.mooo.com, *.blamethelatency.mooo.com would fail. There is a certbot script for freedns that I found, at some point I may get around translating it to work for caddy.

anybody have any suggestions?

Have you tried the obvious and simple solution?

notused.joeisthebest.mooo.com {
   redir https://joeisthebest.mooo.com{uri}
}

(etc.)

You have 4 redirects to set up, that should do the trick.

Note that you can’t get a valid certificate for an IP address from a public CA (usually) – you’ll have to be OK with internally-trusted certs only for the IP addresses.

I use http for IP address redirect:

http://<ip address> {
    redir https://....
}

I don’t think we can get cert for IPs.

@matt

Have you tried the obvious and simple solution?

My DNS entry is for *.joeisthebest.mooo.com, so I would have to put a redirected for much more that just notused. I want to block or redirect ANY subdomain that isn’t explicitly set up.

If I’m understanding what you want correctly, On-Demand TLS is your best option here:

*.joeisthebest.mooo.com {
	tls {
		on_demand
	}
}

Make sure to also configure limits via the ask global option to avoid abuse

1 Like

I would prefer not to request certs for unused subdomains. I tried the below, which is similar to what you suggested, and it unfortunately it supersedes other more specific site blocks such as map.blamethelatency.com

*.blamethelatency.mooo.com {
    tls internal
}

If a TLS handshake can’t be completed, then Caddy can’t handle and respond to the request. That means that if Caddy can’t serve a certificate that the client can trust, then you’ll only see errors. The client and server need to agree on a secret to encrypt communications, and if they can’t do that, no communication can be done.

Agreed, then the TLS internal should work (assuming I accept the untrusted cert), except it causes all my other subdomains to also use the internal cert.

There should be a way to either (ideally both should be options, but unfortunately neither are)

  1. drop (ERR_EMPTY_RESPONSE) and log the requests
  2. send the internal (or arbitrary) cert for subdomains I don’t want to request certs for (without superseding more specific blocks).

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