Multiple subdomains wildcard cert

1. The problem I’m having:

I have in the past just gotten a cert for every subdomain of a single fqdn.

like so which runs fine right now

(r53) {
  log {
  output stdout
  format console
  }
  tls adomain.net@gmail.com {
    dns route53 {
      max_retries 10
    }
  }
}
(errors) {
handle_errors {
  	rewrite * /{http.error.status_code}.html
  	file_server
  }
}

# Git Server
https://git.645.adomain.net {
    import r53
    reverse_proxy http://nadal.adomain.net:3000
    }

# Home Assistant Server
https://ha.adomain.net, https://ha.645.adomain.net {
    import r53
    reverse_proxy /* hassos.645.adomain.net:8123
    }

https://gateway.adomain.net, https://gateway.645.adomain.net {
    import r53
    reverse_proxy /* router.645.adomain.net:8080
  }

https://docker.adomain.net, https://docker.645.adomain.net {
   import r53
   reverse_proxy /* http://nadal.adomain.net:9016
 }

But if I can use a single wildcard cert for multiple subdomains of my fqdn all the better. One cert to rule them all.

trying to expand on this simple example

# {
#     acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
# }

*.adomain.net {

  tls adomain.net@gmail.com {
    dns route53 {
      max_retries 10
    }
  }

  @docker host docker.645.adomain.net, docker.adomain.net, admin.adomain.net, admin.645.adomain.net
    handle @docker {
      reverse_proxy nadal.adomain.net:9016
      }

  @ha host ha.adomain.net, ha.645.adomain.net 
    handle @ha {
    reverse_proxy hassos.645.adomain.net:8123   
    }

}   

but either I am not getting the syntax correct or this is not possible.

see at the very bottom where I used a simple version like the example and it works. So wildcard cert itself is not the issue but rather this “muliple/more” subdomans.

2. Error messages and/or full log output:

There is no error in the logs

{"level":"info","ts":1706809672.0089629,"logger":"tls.obtain","msg":"certificate obtained successfully","identifier":"*.adomain.net"}
nadal-caddy  | {"level":"info","ts":1706809672.0094056,"logger":"tls.obtain","msg":"releasing lock","identifier":"*.adomain.net"}

but when I visit some the single subdomain urls (i.e. docker.adomain.net) I get a blank page, no errors in network inspector, the favicon is passed but that’s about it.

and the two deep subdomains I get

This site can’t provide a secure connectiondocker.645.subdomain.net sent an invalid response.
ERR_SSL_PROTOCOL_ERROR

3. Caddy version:

v2.7.6 h1:w0NymbG2m9PcvKWsrXO6EEkY9Ru4FJK8uQbYcev1p3A=

4. How I installed and ran Caddy:

I run caddy in a docker container with custom image I build.
The image grabs the latest release rather then load via alpine packages.

a. System environment:

latest alpine 3.19

b. Command:

/opt/caddy/bin/caddy run --config test.conf --adapter caddyfile

services:
  caddy:
    container_name: ${NAME:-caddy}
    image: ${IMAGE:-caddy}
    # if no $CONF is given then Caddyfile in ${PWD}/conf:/opt/caddy/conf will be used
    command: caddy run ${CONF}
    hostname: ${NAME:-caddy}
    env_file:
      - $CREDENTIALS
    volumes:
      - data:/opt/caddy/data
      - settings:/opt/caddy/settings
      - conf:/opt/caddy/conf
      # - files:/opt/caddy/files
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443
      - 2019:2019
# binding data and settings are not required
# But if there volumes are deleted caddy will need to redo all the certs
volumes:
  data:
  # driver_opts:
  #   type: none
  #   device: ${PWD}/data
  #   o: bind
  settings:
    # driver_opts:
    #   type: none
    #   device: ${PWD}/config
    #   o: bind
  # files:
  #   driver_opts:
  #     type: none
  #     device: /data/Hacking/webfiles
  #     o: bind
  conf:
    driver_opts:
      type: none
      device: ${PWD}/conf
      o: bind

d. My complete Caddy config:

see above

just so I tried a simple version like the example and that works.

# {
#     acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
# }

*.adomain.net {

  tls adomain.net@gmail.com {
    dns route53 {
      max_retries 10
    }
  }

  @docker host docker.adomain.net
    handle @docker {
      reverse_proxy nadal.adomain.net:9016
      }

  @ha host ha.adomain.net 
    handle @ha {
    reverse_proxy hassos.645.adomain.net:8123   
    }

}   

maybe part of the problem is a wildcard cert can only go one subdomain deep

*.adomain.net, *.645.adomain.net {

if I ask for another wildcard cert at the next level then the ?.645.adomain.net urls work but now the ?.adomain.net urls give the can’t provide a secure connection error.

So maybe you can’t combine them into one reverse proxy like I did before. :(. I suppose I could have one for each subdomain level, not very DRY but at least I would only have two certs instead of dozens.

Commas are not valid syntax in the host matcher.

Please run caddy fmt -w on your Caddyfile, your indentation is very messy so it’s hard to read and follow your config.

not sure why you find it hard to read as running fmt didn’t change it.

anyway commas no good. Are you only allow a single host or can you have multiple but different delimiter?

way below is a solution but involves lots of repeating of the same reverse proxy over and over (basically one for EVERY alternative host). If that is the only way then ok. The old way is better but involves dozens certs the new (wildcard) way is not DRY but only requires two certs. What is the better/suggested approach to my use case?

# {
#     acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
# }

*.kebler.net, *.645.kebler.net {
	tls kebler.net@gmail.com {
		dns route53 {
			max_retries 10
		}
	}

	@docker host docker.kebler.net, admin.kebler.net, admin.645.kebler.net, docker.645.kebler.net
	handle @docker {
		reverse_proxy nadal.kebler.net:9016
	}

	@ha host ha.kebler.net, ha.645.kebler.net
	handle @ha {
		reverse_proxy hassos.645.kebler.net:8123
	}
}

the only way I can get it to work is keep it separate and only one sudomain per host

# {
#     acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
# }

*.645.kebler.net {
	tls kebler.net@gmail.com {
		dns route53 {
			max_retries 10
		}
	}

	@docker645 host docker.645.kebler.net
	handle @docker645 {
		reverse_proxy nadal.kebler.net:9016
	}

	@ha645 host ha.645.kebler.net
	handle @ha645 {
		reverse_proxy hassos.645.kebler.net:8123
	}

	@power645 host power.645.kebler.net
	handle @power645 {
		reverse_proxy 192.168.0.10:8081
	}
}

*.kebler.net {
	tls kebler.net@gmail.com {
		dns route53 {
			max_retries 10
		}
	}

	@docker host docker.kebler.net
	handle @docker {
		reverse_proxy nadal.kebler.net:9016
	}

	@ha host ha.kebler.net
	handle @ha {
		reverse_proxy hassos.645.kebler.net:8123
	}
}

It did, actually. Looks much better now. The whitespace is consistent, and { } braces are aligned. Thanks.

Spaces as delimiters. Commas are only valid for the site addresses, nowhere else in the config. If it were me, we’d drop comma support entirely for consistency, but it was included for it to feel natural for that.

pfff… all it was were the commas. :slight_smile:

so does the root level wildcard work for any number of sub/sub domains or I have to ask for a cert for a subdomain if I want to use a sub/sub domain?

i.e. is *.645.adomain.net required.

*.adomain.net, *.645.adomain.net {
	tls adomain.net@gmail.com {
		dns route53 {
			max_retries 10
		}
	}

	@docker host docker.adomain.net admin.adomain.net admin.645.adomain.net docker.645.adomain.net
	handle @docker {
		reverse_proxy nadal.adomain.net:9016
	}

	@ha host ha.adomain.net ha.645.adomain.net
	handle @ha {
		reverse_proxy hassos.645.adomain.net:8123
	}
}

Only a single * is allowed, and it must be the left-most label. That’s how DNS works.

So yes, you need both.

1 Like

Ok Thx,

maybe a little more “involved” example in the docs would have been helpful. (like mine as is)

I just assumed comma was needed because there was no example of two+ hosts and that was what I used with the “old” way. Plus making it clear that wildcard is need for each successive subdomain host. your docs are great BTW, but even better docs = less dumb questions :).

The assumption generally is that you’ve read through Caddyfile Concepts — Caddy Documentation

But noted, I’ll look to add examples that cover that.