Certificates Being Issued with On-Demand Certificate Generation Even When Wildcard Server Block is Present

1. The problem I’m having:

I am currently migrating our OpenResty setup to Caddy and have encountered a problem with the on-demand certificate issuance that I hope to get some help with.

Configuration Background:

We manage two primary wildcard domains: *.mydomain.app and *.mydomain.com. Our setup in Caddy is designed with distinct configurations: one specifically for efficiently handling requests from subdomains such as abc.mydomain.com and abc.mydomain.app, and another separate configuration for on-demand TLS certificate issuance. This on-demand setup is activated for requests originating from domains not managed by us, like abc.example.com.

Here’s a simplified snippet of our Caddyfile configuration:

# This block will server all custom domains not owned by us
:443 {
    reverse_proxy {$UPSTREAMS}

    tls {
            on_demand
            issuer zerossl {$ZEROSSL_API_KEY}
            # fallback on letsencrypt if zerossl fails or we hit rate limits
            issuer acme "https://acme-v02.api.letsencrypt.org/directory"
    }
}

*.mydomain.app:443, *.mydomain.com:443 {
    reverse_proxy {$UPSTREAMS}

    # Caddy defaults to using Let's Encrypt for on-demand wildcard certificates
    tls {
            dns googleclouddns {
                    gcp_project {$GCP_PROJECT}
            }
    }
}

Issue:

During the migration, Caddy began requesting on-demand certificates for every subdomain under *.mydomain.app and *.mydomain.com, which was not the intended behavior. We were expecting it to use the certificates generated by the wildcard domain server block.

Upon reviewing the converted JSON configuration from the Caddyfile, it appears that the on_demand issuer was not restricted to specific domains, leading to unexpected certificate requests:

...
"apps": {
  ...
  "tls": {
    "automation": {
      "on_demand": {
        "permission": {
          "endpoint": "https://mock.httpstatus.io/200",
          "module": "http"
        }
      },
      "policies": [
        {
          "subjects": [
            "*.mydomain.app",
            "*.mydomain.com"
          ],
          ...
        },
        {
          "on_demand": true,
          ...
        }
      ]
    }
  },
...

How can I configure Caddy to reuse the wildcard certificate for all requests targeting our subdomains, while issuing certificates on-demand only for requests to other domain not covered by our wildcard certificate?

3. Caddy version:

v2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=

4. How I installed and ran Caddy:

Running Caddy in Kubernetes environment with caddy run --config /etc/caddy/Caddyfile.

a. System environment:

Kubernetes, Linux, Docker image

b. Command:

caddy run --config /etc/caddy/Caddyfile

d. My complete Caddy config:

{
        log default {
                level debug
        }

        on_demand_tls {
                ask https://mock.httpstatus.io/200
        }

        servers {
                metrics
                strict_sni_host on
        }

        admin :2020
}

:443 {
        reverse_proxy {$UPSTREAMS}

        tls {
                on_demand
                issuer zerossl {$ZEROSSL_API_KEY}
                # fallback on letsencrypt if zerossl fails or we hit rate limits
                issuer acme "https://acme-v02.api.letsencrypt.org/directory"
        }
}

*.mydomain.app:443, *.mydomain.com:443 {
        reverse_proxy {$UPSTREAMS}

        # Caddy defaults to using Let's Encrypt for on-demand wildcard certificates
        tls {
                dns googleclouddns {
                        gcp_project {$GCP_PROJECT}
                }
        }
}

:8080 {
	respond /health 200
}

5. Links to relevant resources:

Perhaps this is the feature i’m looking for?

Are you sure they’re being issued? What’s your evidence? Show your logs. Check your storage, do you actually have certs issued for those individual domains?

1 Like

i’ll do the experiment again and post the logs but i did see certificates for those individual certs in the data directory along with the wildcard certificates for *.mydomain.app, *.mydomain.com (see the directory for let’s encrypt)

/data/caddy/certificates # ls

acme-v02.api.letsencrypt.org-directory  zerossl

/data/caddy/certificates # cd acme-v02.api.letsencrypt.org-directory/

/data/caddy/certificates/acme-v02.api.letsencrypt.org-directory # ls
wildcard_.mydomain.app                                     www.coffee112.mydomain.app                               www.hellosir.mydomain.app                                  www.smileplease.mydomain.com
wildcard_.mydomain.com                                     www.call.mydomain.com                                       www.hellotomama.mydomain.app                               www.smokeylime.mydomain.com

/data/caddy/certificates/acme-v02.api.letsencrypt.org-directory # cd ../zerossl
/data/caddy/certificates/zerossl # ls
www.408.mydomain.app                                       www.mountainmarketing.mydomain.com                           www.ibs-proof-systems.mydomain.app

Please note that when I access a domain that falls under *.mydomain.app through a browser, it correctly serves the wildcard certificate. However, it still prompts ZeroSSL to issue a certificate for the subdomain regardless.

Here are the logs:

WARNING 2024-09-15T23:24:57.137576488Z [resource.labels.containerName: caddy] {"error":"no information found to solve challenge for identifier: www.results-matter.mydomain.app", "host":"www.results-matter.mydomain.app", "level":"warn", "logger":"http", "msg":"looking up info for HTTP challenge", "remote_addr":"10.142.0.131:20386", "ts":1.726442697137304E9, "user_agent":"Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"}
DEBUG 2024-09-15T23:24:57.612696280Z [resource.labels.containerName: caddy] {"data":{…}, "id":"d4467e90-e87f-4d33-8e3c-cf45772b2ce3", "level":"debug", "logger":"events", "msg":"event", "name":"tls_get_certificate", "origin":"tls", "ts":1.7264426976124551E9}
DEBUG 2024-09-15T23:24:57.612736930Z [resource.labels.containerName: caddy] {"identifier":"www.results-matter.mydomain.app", "level":"debug", "logger":"tls.handshake", "msg":"no matching certificates and no custom selection logic", "ts":1.7264426976125083E9}
DEBUG 2024-09-15T23:24:57.612805020Z [resource.labels.containerName: caddy] {"level":"debug", "logger":"tls.handshake", "msg":"all external certificate managers yielded no certificates and no errors", "remote_ip":"10.142.0.68", "remote_port":"20515", "sni":"www.results-matter.mydomain.app", "ts":1.7264426976125338E9}
DEBUG 2024-09-15T23:24:57.612810510Z [resource.labels.containerName: caddy] {"domain":"www.results-matter.mydomain.app", "level":"debug", "logger":"tls", "msg":"asking for permission for on-demand certificate", "remote_ip":"10.142.0.68", "ts":1.7264426976125433E9}
DEBUG 2024-09-15T23:24:57.612817100Z [resource.labels.containerName: caddy] {"domain":"www.results-matter.mydomain.app", "level":"debug", "logger":"tls.permission.http", "msg":"asking permission endpoint", "remote":"10.142.0.68:20515", "ts":1.726442697612566E9, "url":"https://mock.httpstatus.io/200?domain=www.results-matter.mydomain.app"}
DEBUG 2024-09-15T23:24:57.922607981Z [resource.labels.containerName: caddy] {"domain":"www.results-matter.mydomain.app", "level":"debug", "logger":"tls.permission.http", "msg":"response from permission endpoint", "remote":"10.142.0.68:20515", "status":200, "ts":1.7264426979223557E9, "url":"https://mock.httpstatus.io/200?domain=www.results-matter.mydomain.app"}
DEBUG 2024-09-15T23:24:57.928128871Z [resource.labels.containerName: caddy] {"error":"no matching certificate to load for www.results-matter.mydomain.app: open /data/caddy/certificates/acme-v02.api.letsencrypt.org-directory/wildcard_.results-matter.mydomain.app/wildcard_.results-matter.mydomain.app.key: no such file or directory", "level":"debug", "logger":"tls.handshake", "msg":"did not load cert from storage", "remote_ip":"10.142.0.68", "remote_port":"20515", "server_name":"www.results-matter.mydomain.app", "ts":1.7264426979278815E9}
INFO 2024-09-15T23:24:57.928186741Z [resource.labels.containerName: caddy] {"level":"info", "logger":"tls.on_demand", "msg":"obtaining new certificate", "remote_ip":"10.142.0.68", "remote_port":"20515", "server_name":"www.results-matter.mydomain.app", "ts":1.7264426979279516E9}
INFO 2024-09-15T23:24:57.942735180Z [resource.labels.containerName: caddy] {"identifier":"www.results-matter.mydomain.app", "level":"info", "logger":"tls.obtain", "msg":"acquiring lock", "ts":1.7264426979424365E9}
INFO 2024-09-15T23:24:57.949721920Z [resource.labels.containerName: caddy] {"identifier":"www.results-matter.mydomain.app", "level":"info", "logger":"tls.obtain", "msg":"lock acquired", "ts":1.726442697949449E9}
INFO 2024-09-15T23:24:57.951427350Z [resource.labels.containerName: caddy] {"identifier":"www.results-matter.mydomain.app", "level":"info", "logger":"tls.obtain", "msg":"obtaining certificate", "ts":1.726442697951062E9}
DEBUG 2024-09-15T23:24:57.951515070Z [resource.labels.containerName: caddy] {"data":{…}, "id":"239d7bc0-6b26-407d-8c61-2d8b4287605d", "level":"debug", "logger":"events", "msg":"event", "name":"cert_obtaining", "origin":"tls", "ts":1.7264426979511807E9}
INFO 2024-09-15T23:24:57.951802590Z [resource.labels.containerName: caddy] {"identifiers":[…], "level":"info", "logger":"tls.issuance.zerossl", "msg":"creating certificate", "ts":1.7264426979516723E9}
INFO 2024-09-15T23:24:58.972826662Z [resource.labels.containerName: caddy] {"cert_id":"d8fd7fff821b189588450d833f86acd8", "identifiers":[…], "level":"info", "logger":"tls.issuance.zerossl", "msg":"created certificate", "ts":1.7264426989725335E9}
INFO 2024-09-15T23:24:58.980807881Z [resource.labels.containerName: caddy] {"cert_id":"d8fd7fff821b189588450d833f86acd8", "identifiers":[…], "level":"info", "logger":"tls.issuance.zerossl", "msg":"validating identifiers", "ts":1.7264426989805207E9, "verification_method":"HTTP_CSR_HASH"}
INFO 2024-09-15T23:24:59.168761406Z [resource.labels.containerName: caddy] {"challenge":"http-01", "distributed":true, "level":"info", "logger":"tls.issuance.zerossl", "msg":"served HTTP validation credential", "remote":"10.142.0.105:46738", "ts":1.7264426991685007E9, "validation_path":"http://www.results-matter.mydomain.app/.well-known/pki-validation/598E1B527935C9B6183D31647AA98856.txt"}
INFO 2024-09-15T23:24:59.807702783Z [resource.labels.containerName: caddy] {"cert_id":"d8fd7fff821b189588450d833f86acd8", "identifiers":[…], "level":"info", "logger":"tls.issuance.zerossl", "msg":"validations succeeded; waiting for certificate to be issued", "ts":1.7264426998073351E9, "verification_method":"HTTP_CSR_HASH"}
INFO 2024-09-15T23:25:00.000626942Z [resource.labels.containerName: caddy] {"challenge":"http-01", "distributed":true, "level":"info", "logger":"tls.issuance.zerossl", "msg":"served HTTP validation credential", "remote":"10.142.0.144:1327", "ts":1.7264427000002463E9, "validation_path":"http://www.results-matter.mydomain.app/.well-known/pki-validation/598E1B527935C9B6183D31647AA98856.txt"}
INFO 2024-09-15T23:25:05.201086620Z [resource.labels.containerName: caddy] {"cert_id":"d8fd7fff821b189588450d833f86acd8", "identifiers":[…], "level":"info", "logger":"tls.issuance.zerossl", "msg":"successfully downloaded issued certificate", "ts":1.7264427052007756E9, "verification_method":"HTTP_CSR_HASH"}
INFO 2024-09-15T23:25:05.233788648Z [resource.labels.containerName: caddy] {"identifier":"www.results-matter.mydomain.app", "issuer":"zerossl", "level":"info", "logger":"tls.obtain", "msg":"certificate obtained successfully", "ts":1.7264427052333343E9}
DEBUG 2024-09-15T23:25:05.234022598Z [resource.labels.containerName: caddy] {"data":{…}, "id":"488e2144-97b5-4dc7-ad49-c140098b0b63", "level":"debug", "logger":"events", "msg":"event", "name":"cert_obtained", "origin":"tls", "ts":1.7264427052337487E9}
INFO 2024-09-15T23:25:05.234088648Z [resource.labels.containerName: caddy] {"identifier":"www.results-matter.mydomain.app", "level":"info", "logger":"tls.obtain", "msg":"releasing lock", "ts":1.726442705233787E9}
DEBUG 2024-09-15T23:25:05.240837978Z [resource.labels.containerName: caddy] {"domain":"www.results-matter.mydomain.app", "expiration":1.7342208E9, "issuer_key":"zerossl", "level":"debug", "logger":"tls", "msg":"loading managed certificate", "storage":"FileStorage:/data/caddy", "ts":1.726442705240562E9}
DEBUG 2024-09-15T23:25:05.446444236Z [resource.labels.containerName: caddy] {"cache_capacity":10000, "cache_size":148, "expiration":1.7342208E9, "hash":"f4ea6660eebd43cd855988d3207acfd51605198734e898a757237682323be014", "issuer_key":"zerossl", "level":"debug", "logger":"tls.cache", "managed":true, "msg":"added certificate to cache", "subjects":[…], "ts":1.7264427054461648E9}
DEBUG 2024-09-15T23:25:05.446531266Z [resource.labels.containerName: caddy] {"data":{…}, "id":"2cddac9e-a121-4bde-a8b2-877da53f95d3", "level":"debug", "logger":"events", "msg":"event", "name":"cached_managed_cert", "origin":"tls", "ts":1.7264427054462643E9}
DEBUG 2024-09-15T23:25:05.446537416Z [resource.labels.containerName: caddy] {"expiration":1.7342208E9, "hash":"f4ea6660eebd43cd855988d3207acfd51605198734e898a757237682323be014", "level":"debug", "logger":"tls.on_demand", "managed":true, "msg":"loaded certificate from storage", "remote_ip":"10.142.0.68", "remote_port":"20515", "subjects":[…], "ts":1.7264427054462774E9}
WARNING 2024-09-15T23:25:05.523740400Z [resource.labels.containerName: caddy] {"error":"no information found to solve challenge for identifier: www.results-matter.mydomain.app", "host":"www.results-matter.mydomain.app", "level":"warn", "logger":"http", "msg":"looking up info for HTTP challenge", "remote_addr":"10.142.0.68:20515", "ts":1.7264427055234458E9, "user_agent":"Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"}
DEBUG 2024-09-15T23:25:05.528214250Z [resource.labels.containerName: caddy] {"duration":0.004259599, "headers":{…}, "level":"debug", "logger":"http.handlers.reverse_proxy", "msg":"upstream roundtrip", "request":{…}, "status":200, "ts":1.7264427055279074E9, "upstream":"app.mydomain.com:443"}
DEBUG 2024-09-15T23:25:08.001736306Z [resource.labels.containerName: caddy] {"data":{…}, "id":"a806a12c-fcc9-490d-bdad-817330ff28ab", "level":"debug", "logger":"events", "msg":"event", "name":"tls_get_certificate", "origin":"tls", "ts":1.7264427080014656E9}
DEBUG 2024-09-15T23:25:08.001777036Z [resource.labels.containerName: caddy] {"identifier":"www.results-matter.mydomain.app", "level":"debug", "logger":"tls.handshake", "msg":"no matching certificates and no custom selection logic", "ts":1.7264427080015094E9}
DEBUG 2024-09-15T23:25:08.001839366Z [resource.labels.containerName: caddy] {"level":"debug", "logger":"tls.handshake", "msg":"all external certificate managers yielded no certificates and no errors", "remote_ip":"10.142.0.58", "remote_port":"20238", "sni":"www.results-matter.mydomain.app", "ts":1.726442708001549E9}
DEBUG 2024-09-15T23:25:08.001850446Z [resource.labels.containerName: caddy] {"domain":"www.results-matter.mydomain.app", "level":"debug", "logger":"tls", "msg":"asking for permission for on-demand certificate", "remote_ip":"10.142.0.58", "ts":1.7264427080015585E9}
DEBUG 2024-09-15T23:25:08.001857866Z [resource.labels.containerName: caddy] {"domain":"www.results-matter.mydomain.app", "level":"debug", "logger":"tls.permission.http", "msg":"asking permission endpoint", "remote":"10.142.0.58:20238", "ts":1.7264427080015814E9, "url":"https://mock.httpstatus.io/200?domain=www.results-matter.mydomain.app"}
DEBUG 2024-09-15T23:25:08.201058629Z [resource.labels.containerName: caddy] {"domain":"www.results-matter.mydomain.app", "level":"debug", "logger":"tls.permission.http", "msg":"response from permission endpoint", "remote":"10.142.0.58:20238", "status":200, "ts":1.7264427082008111E9, "url":"https://mock.httpstatus.io/200?domain=www.results-matter.mydomain.app"}
DEBUG 2024-09-15T23:25:08.216632038Z [resource.labels.containerName: caddy] {"domain":"www.results-matter.mydomain.app", "expiration":1.7342208E9, "issuer_key":"zerossl", "level":"debug", "logger":"tls", "msg":"loading managed certificate", "storage":"FileStorage:/data/caddy", "ts":1.726442708216355E9}
DEBUG 2024-09-15T23:25:08.222458168Z [resource.labels.containerName: caddy] {"cache_capacity":10000, "cache_size":143, "expiration":1.7342208E9, "hash":"f4ea6660eebd43cd855988d3207acfd51605198734e898a757237682323be014", "issuer_key":"zerossl", "level":"debug", "logger":"tls.cache", "managed":true, "msg":"added certificate to cache", "subjects":[…], "ts":1.7264427082221744E9}
DEBUG 2024-09-15T23:25:08.222499928Z [resource.labels.containerName: caddy] {"data":{…}, "id":"63123551-4380-48af-ba08-714bd4beea5e", "level":"debug", "logger":"events", "msg":"event", "name":"cached_managed_cert", "origin":"tls", "ts":1.7264427082222638E9}
DEBUG 2024-09-15T23:25:08.222505357Z [resource.labels.containerName: caddy] {"expiration":1.7342208E9, "hash":"f4ea6660eebd43cd855988d3207acfd51605198734e898a757237682323be014", "level":"debug", "logger":"tls.handshake", "managed":true, "msg":"loaded certificate from storage", "remote_ip":"10.142.0.58", "remote_port":"20238", "subjects":[…], "ts":1.7264427082222772E9}
DEBUG 2024-09-15T23:25:08.299405611Z [resource.labels.containerName: caddy] {"duration":0.00519043, "headers":{…}, "level":"debug", "logger":"http.handlers.reverse_proxy", "msg":"upstream roundtrip", "request":{…}, "status":200, "ts":1.7264427082991138E9, "upstream":"app.mydomain.com:443"}
DEBUG 2024-09-15T23:25:13.033471668Z [resource.labels.containerName: caddy] {"data":{…}, "id":"22129faa-bc86-46c8-824a-f48f301144d3", "level":"debug", "logger":"events", "msg":"event", "name":"tls_get_certificate", "origin":"tls", "ts":1.7264427130330725E9}
DEBUG 2024-09-15T23:25:13.033518557Z [resource.labels.containerName: caddy] {"identifier":"www.results-matter.mydomain.app", "level":"debug", "logger":"tls.handshake", "msg":"choosing certificate", "num_choices":1, "ts":1.7264427130331123E9}
DEBUG 2024-09-15T23:25:13.033531128Z [resource.labels.containerName: caddy] {"hash":"f4ea6660eebd43cd855988d3207acfd51605198734e898a757237682323be014", "identifier":"www.results-matter.mydomain.app", "issuer_key":"zerossl", "level":"debug", "logger":"tls.handshake", "managed":true, "msg":"default certificate selection results", "subjects":[…], "ts":1.7264427130331292E9}
DEBUG 2024-09-15T23:25:13.033568038Z [resource.labels.containerName: caddy] {"expiration":1.7342208E9, "hash":"f4ea6660eebd43cd855988d3207acfd51605198734e898a757237682323be014", "level":"debug", "logger":"tls.handshake", "managed":true, "msg":"matched certificate in cache", "remote_ip":"10.142.0.45", "remote_port":"4315", "subjects":[…], "ts":1.726442713033136E9}
DEBUG 2024-09-15T23:25:13.112283882Z [resource.labels.containerName: caddy] {"duration":0.00584007, "headers":{…}, "level":"debug", "logger":"http.handlers.reverse_proxy", "msg":"upstream roundtrip", "request":{…}, "status":200, "ts":1.7264427131120343E9, "upstream":"app.mydomain.com:443"}
DEBUG 2024-09-15T23:25:13.779230294Z [resource.labels.containerName: caddy] {"data":{…}, "id":"14fae680-cc22-4324-ac29-d3f43d21d6a2", "level":"debug", "logger":"events", "msg":"event", "name":"tls_get_certificate", "origin":"tls", "ts":1.7264427137790582E9}
DEBUG 2024-09-15T23:25:13.779238214Z [resource.labels.containerName: caddy] {"identifier":"www.results-matter.mydomain.app", "level":"debug", "logger":"tls.handshake", "msg":"choosing certificate", "num_choices":1, "ts":1.7264427137790806E9}
DEBUG 2024-09-15T23:25:13.779249874Z [resource.labels.containerName: caddy] {"hash":"f4ea6660eebd43cd855988d3207acfd51605198734e898a757237682323be014", "identifier":"www.results-matter.mydomain.app", "issuer_key":"zerossl", "level":"debug", "logger":"tls.handshake", "managed":true, "msg":"default certificate selection results", "subjects":[…], "ts":1.7264427137791078E9}
DEBUG 2024-09-15T23:25:13.779254763Z [resource.labels.containerName: caddy] {"expiration":1.7342208E9, "hash":"f4ea6660eebd43cd855988d3207acfd51605198734e898a757237682323be014", "level":"debug", "logger":"tls.handshake", "managed":true, "msg":"matched certificate in cache", "remote_ip":"10.142.0.158", "remote_port":"6925", "subjects":[…], "ts":1.7264427137791233E9}
DEBUG 2024-09-15T23:25:13.858493069Z [resource.labels.containerName: caddy] {"duration":0.00458932, "headers":{…}, "level":"debug", "logger":"http.handlers.reverse_proxy", "msg":"upstream roundtrip", "request":{…}, "status":200, "ts":1.7264427138581753E9, "upstream":"app.mydomain.com:443"}

i think i know what might be going on. I see ZeroSSL certificates being issued for www.abc.mydomain.com but not for abc.mydomain.com. Notice the prefix www.

I suspect i need a config like this to make my use case work:

{
        log default {
                level debug
        }

        on_demand_tls {
                ask https://mock.httpstatus.io/200
        }

        servers {
                metrics
                strict_sni_host on
        }

        admin :2020
}

:443 {
        reverse_proxy {$UPSTREAMS}

        tls {
                on_demand
                issuer zerossl {$ZEROSSL_API_KEY}
                # fallback on letsencrypt if zerossl fails or we hit rate limits
                issuer acme "https://acme-v02.api.letsencrypt.org/directory"
        }
}

*.mydomain.app:443, *.mydomain.com:443 {
        reverse_proxy {$UPSTREAMS}

        # Caddy defaults to using Let's Encrypt for on-demand wildcard certificates
        tls {
                dns googleclouddns {
                        gcp_project {$GCP_PROJECT}
                }
        }
}

www.*.mydomain.app:443, www.*.mydomain.com:443 {
	redir https://{labels.2}.{labels.1}.{labels.0}{uri}
}

:8080 {
	respond /health 200
}

However, Caddy does not seem to be supporting this

www.*.mydomain.app:443, www.*.mydomain.com:443 {
	redir https://{labels.2}.{labels.1}.{labels.0}{uri}
}

Any idea about how to deal with this?

Okay, I gotta call you out on that one. You really can’t go about explicitly configuring the ask functionality to reach out for an online service that literally gives a 200 response to every request (thereby implicitly authorising every single domain it would be queried for!) and then say you were surprised when on_demand started trying certs for every domain queried.

Like - that’s something you specifically wrote into the config. Incidentally, you didn’t even need to use an external service for that. You could’ve just pointed it at (for a random example) localhost:9999 and set up an equivalent localhost:9999 listener in Caddy itself with no config (would also always return 200 by default) if you wanted a hacky way to bypass this. But you specifically used an external service.

This won’t work because:

  1. Caddy thinks you want HTTPS for this site because it’s on port 443, but;
  2. Caddy can’t issue a cert for the literal www.*.mydomain.app / .com, and;
  3. You have not configured On-Demand TLS for this site so Caddy can’t instead get specific certificates when required, and;
  4. Wildcard certificates aren’t suitable either even if you DID have the DNS challenge configured because for wildcard certs, the glob must be the left-most element of the domain.

Which all means there is no way for Caddy to serve HTTPS for this site whatsoever, so it can’t do what you’re asking and therefore cannot start.

Configure an ask endpoint that rejects requests for domains you don’t want to issue certs for. That is the design and purpose of the ask functionality.

2 Likes

Okay, I gotta call you out on that one. You really can’t go about explicitly configuring the ask functionality to reach out for an online service that literally gives a 200 response to every request (thereby implicitly authorising every single domain it would be queried for!) and then say you were surprised when on_demand started trying certs for every domain queried.

This is just for testing purposes, i don’t actually have this in production. Although, my production ask endpoint will respond with 200 for a valid subdomain (i.e. abc.mydomain.com or abc.mydomain.app). It will respond with 404 for arbitrary domains & subdomains not part of our system.

This won’t work because: …

Got it

Configure an ask endpoint that rejects requests for domains you don’t want to issue certs for. That is the design and purpose of the ask functionality.

Will this really solve my problem?

I want request for www.abc.mydomain.com to go to this block:

*.mydomain.app:443, *.mydomain.com:443 {
        reverse_proxy {$UPSTREAMS}

        tls {
                dns googleclouddns {
                        gcp_project {$GCP_PROJECT}
                }
        }
}

However, request for abc.mydomain.com goes to the above block (as expected) and request for www.abc.mydomain.com goes to the block below:

:443 {
        reverse_proxy {$UPSTREAMS}

        tls {
                on_demand
                issuer zerossl {$ZEROSSL_API_KEY}
                issuer acme "https://acme-v02.api.letsencrypt.org/directory"
        }
}

My ask endpoint will allow both abc.mydomain.com & www.abc.mydomain.com.

Is there a way I can configure Caddy so that certificate for www.abc.mydomain.com (by extension www.*.mydomain.com) is issued by the block below?

*.mydomain.app:443, *.mydomain.com:443 {
        reverse_proxy {$UPSTREAMS}

        # Caddy defaults to using Let's Encrypt for on-demand wildcard certificates
        tls {
                dns googleclouddns {
                        gcp_project {$GCP_PROJECT}
                }
        }
}

Would on_demand make it work? Is that the missing piece?

*.mydomain.app:443, *.mydomain.com:443 {
	reverse_proxy {$UPSTREAMS}

	# Caddy defaults to using Let's Encrypt for on-demand wildcard certificates
	tls {
		on_demand
		dns googleclouddns {
			gcp_project {$GCP_PROJECT}
		}
	}
}

Oh! Excellent.

It can’t, because your site addresses don’t cover the appropriate depth of domain elements. You would need to cover *.*.example.com for that.

You probably want to keep them separate so that *.*.example.com can be covered on-demand and *.example.com can remain a wildcard configuration.

1 Like

Multi-level wildcard certs are not supported by issuers.

1 Like

You probably want to keep them separate so that *.*.example.com can be covered on-demand and *.example.com can remain a wildcard configuration.

That makes sense. I could live with *.*.example.com being issued by LE issuer on-demand. Although, I want to stop it from going to ZeroSSL since it has hard limits on # number certs that can be issued.

Multi-level wildcard certs are not supported by issuers.

Yes, understood.

Would it be possible to do something like this? (trying to deploy it as we speak)

:443 {
	reverse_proxy {$UPSTREAMS}

	tls {
		on_demand
		issuer zerossl {$ZEROSSL_API_KEY}
		# fallback on letsencrypt if zerossl fails or we hit rate limits
		issuer acme "https://acme-v02.api.letsencrypt.org/directory"
	}
}

*.mydomain.app:443, *.mydomain.com:443 {
	reverse_proxy {$UPSTREAMS}

	tls {
		dns googleclouddns {
			gcp_project {$GCP_PROJECT}
		}
	}
}

*.*.mydomain.app:443, *.*.mydomain.com:443 {
	reverse_proxy {$UPSTREAMS}

	tls {
		on_demand
		dns googleclouddns {
			gcp_project {$GCP_PROJECT}
		}
	}
}

Or something else similar to that?

This did the trick

:443 {
	reverse_proxy {$UPSTREAMS}

	tls {
		on_demand
		issuer zerossl {$ZEROSSL_API_KEY}
		# fallback on letsencrypt if zerossl fails or we hit rate limits
		issuer acme "https://acme-v02.api.letsencrypt.org/directory"
	}
}

*.mydomain.app:443, *.mydomain.com:443 {
	reverse_proxy {$UPSTREAMS}

	tls {
		dns googleclouddns {
			gcp_project {$GCP_PROJECT}
		}
	}
}

*.*.mydomain.app:443, *.*.mydomain.com:443 {
	reverse_proxy {$UPSTREAMS}

	tls {
		on_demand
		dns googleclouddns {
			gcp_project {$GCP_PROJECT}
		}
	}
}

Thanks for the assistance!

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