Wildcard certificate overriding subdomain specific cert on different entryPoint/certResolver

Wildcard certificates are working for my default certResolver. However, when adding a second certResolver (because I want a different keyType), the second certResolver never resolves, I get the wildcard certificate from the default certResolver.

  • I have services on http working beautifully. Subdomains go to the right places.
  • I have services on https working beautifully. The wildcard certificate is being properly generated and renewed. All services that are on this entryPoint have the wildcard certificate as expected.
  • The problem is the https-alternate entrypoint. All services here should have individual certificates, NOT wildcard certificates.

I want the https-alternate entrypoint to have certificates from the rsa2048 resolver, however, this is not occuring. When I visit a site that is on the https-alternate resolver, I get the wildcard certificate from the https resolver and acme.rsa2048.json file is empty.

Where is my issue? How can I get NON-wildcard certificates on my https-alternate entrypoint?

traefik.yml

entryPoints:
  http:
    address: ":80"
  https:
    address: ":443"
  https-alternate:
    address: ":4443"
    http:
      tls:
        certResolver: rsa2048

certificatesResolvers:
  https:
    acme:
      email: acme@domain.local
      storage: /data/acme.json
      httpChallenge:
        entryPoint: http
  rsa2048:
    acme:
      keytype: RSA2048
      email: acme@domain.local
      storage: /data/acme.rsa2048.json
      httpChallenge:
        entryPoint: http

service.traefik.yml (Docker Swarm Service)

      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.traefik-secure.entrypoints=https"
        - "traefik.http.routers.traefik-secure.rule=Host(`traefik.domain.local`)"
        - "traefik.http.routers.traefik-secure.tls=true"
        - "traefik.http.routers.traefik-secure.tls.certresolver=https"
        - "traefik.http.routers.traefik-secure.tls.domains[0].main=*.domain.local"
        - "traefik.http.routers.traefik-secure.tls.domains[0].sans=domain.local"
        - "traefik.http.routers.traefik-secure.service=api@internal"
        - "traefik.http.services.traefik.loadbalancer.server.port=8080"

service.testing.yml (Docker Swarm Service)

      labels:
        - "traefik.enable=true"
        - "traefik.docker.network=traefik"

        - "traefik.http.routers.svc1.entrypoints=http"
        - "traefik.http.routers.svc1.rule=Host(`subdomain.domain.local`)"
        - "traefik.http.routers.svc1.service=testservice"

        - "traefik.http.routers.svc1s.entrypoints=https-alternate"
        - "traefik.http.routers.svc1s.rule=Host(`subdomain.domain.local`)"
        - "traefik.http.routers.svc1s.tls=true"
        - "traefik.http.routers.svc1s.tls.domains[0].main=subdomain.domain.local"
        - "traefik.http.routers.svc1s.tls.certresolver=rsa2048"
        - "traefik.http.routers.svc1s.service=testservice"

        - "traefik.http.services.testservice.loadbalancer.server.port=80"

traefik.log

{"level":"info","msg":"Starting provider *file.Provider {\"watch\":true,\"filename\":\"/data/tls.yml\"}","time":"2020-09-05T17:40:40-04:00"}
{"level":"info","msg":"Starting provider *acme.Provider {\"email\":\"acme@domain.local\",\"caServer\":\"https://acme-v02.api.letsencrypt.org/directory\",\"storage\":\"/data/acme.json\",\"keyType\":\"RSA4096\",\"httpChallenge\":{\"entryPoint\":\"http\"},\"ResolverName\":\"https\",\"store\":{},\"ChallengeStore\":{}}","time":"2020-09-05T17:40:40-04:00"}
{"level":"info","msg":"Testing certificate renew...","providerName":"http.acme","time":"2020-09-05T17:40:40-04:00"}
{"level":"info","msg":"Starting provider *docker.Provider {\"watch\":true,\"endpoint\":\"unix:///var/run/docker.sock\",\"defaultRule\":\"Host(`{{ normalize .Name }}`)\",\"swarmMode\":true,\"network\":\"traefik\",\"swarmModeRefreshSeconds\":15000000000}","time":"2020-09-05T17:40:40-04:00"}
{"level":"info","msg":"Starting provider *traefik.Provider {}","time":"2020-09-05T17:40:40-04:00"}
{"level":"info","msg":"Starting provider *acme.Provider {\"email\":\"acme@domain.local\",\"caServer\":\"https://acme-v02.api.letsencrypt.org/directory\",\"storage\":\"/data/acme.rsa2048.json\",\"keyType\":\"RSA2048\",\"httpChallenge\":{\"entryPoint\":\"http\"},\"ResolverName\":\"rsa2048\",\"store\":{},\"ChallengeStore\":{}}","time":"2020-09-05T17:40:40-04:00"}
{"level":"info","msg":"Testing certificate renew...","providerName":"rsa2048.acme","time":"2020-09-05T17:40:40-04:00"}

traefik version

Version:      2.2.10
Codename:     chevrotin
Go version:   go1.14.8
Built:        2020-09-04T15:53:27Z
OS/Arch:      linux/amd64

Turning on Debugging, I get the following:

1: {"entryPointName":"https-alternate","level":"debug","msg":"Adding route for subdomain.domain.local with TLS options rsa2048@file","time":"2020-09-06T10:29:35-04:00"}
2: {"level":"debug","msg":"Try to challenge certificate for domain [subdomain.domain.local] found in HostSNI rule","providerName":"rsa2048.acme","routerName":"testing@docker","rule":"Host(`subdomain.domain.local`)","time":"2020-09-06T10:29:35-04:00"}
3: {"level":"debug","msg":"Looking for provided certificate(s) to validate [\"subdomain.domain.local\"]...","providerName":"rsa2048.acme","routerName":"testing@docker","rule":"Host(`subdomain.domain.local`)","time":"2020-09-06T10:29:35-04:00"}
4: {"level":"debug","msg":"No ACME certificate generation required for domains [\"subdomain.domain.local\"].","providerName":"rsa2048.acme","routerName":"testing@docker","rule":"Host(`subdomain.domain.local`)","time":"2020-09-06T10:29:35-04:00"}
5: {"level":"debug","msg":"Looking for provided certificate(s) to validate [\"*.domain.local\" \"domain.local\"]...","providerName":"linodedns.acme","time":"2020-09-06T10:29:35-04:00"}
6: {"level":"debug","msg":"No ACME certificate generation required for domains [\"*.domain.local\" \"domain.local\"].","providerName":"linodedns.acme","time":"2020-09-06T10:29:35-04:00"}

Line 1 tells me that the service found the TLS options (but is not applying them, a story for a different topic), but nonetheless, did not error.
Line 2 tells me it figured out the subdomain (yay) AND the provider name.
Line 3 tells me it's looking.
Line 4 tells me I don't need a cert. This is where I'm EXPECTING to see a cert REGARDLESS of the Wildcard Certificate on Line 5 and 6 since the certResolvers are DIFFERENT.

Is there a way to say "I don't care that you have a wildcard certificate, I want one?"

The problem was a default certificate.

The default certificate contained a wildcard domain which the subdomain used.

I restarted traefik WITHOUT the default certificate, received my acme certificate properly, then started traefik again WITH the certificate.

tls:
  stores:
    default:
      defaultCertificate:
        certFile: /certificate.crt
        keyFile: /private.key
1 Like

I'm not sure if this is exactly what you dealing with or just related (sorry, I did not digest everything you wrote carefully, just wanted to share my experience), but there is no way to force traefik to generate an LE cert if it already has a valid matching one.

It also has been my experience, that if you have a single storage for all resolvers, then this what matters, and if a matching cert is in there it will be used regardless if the resolver is the same or different.

Update: oh I see you have different storages. I never tried that, never mind me then.

Yes, in the end, that was the underlying task I was trying to do.

(Untested) I think that does not matter. Since there is only one default store, if a matching (wildcard would match) certificate is available for a site, it will use it.

I suppose someone has two paths to get a specific cert in the future if they have a wildcard certificate already taking up the spot.

If you have all of the following:

  • A Traefik instance with a wildcard certificate (e.g. * .domain.local)
  • A subdomain you want a specific certificate for (in my case, I needed it to be RSA 2048 for an IoT project, e.g. iot.domain.local)
  • Both the subdomain and the wildcard domain resolve to the SAME traefik instance.
  1. Edit DNS to point your subdomain to a different IP from the wildcard IP.
  2. Run (temporary) Traefik on subdomain/IP to get the certificate you need, saving it in a file.
  3. Copy the file to (main) Traefik instance, and load the file under certificateResolvers
  4. Move DNS back to the wildcard IP.
  5. Profit?
certificatesResolvers:
  subdomain1:
    acme:
      email: acme@domain.local
      storage: /data/subdomain.json
      httpChallenge:
        entryPoint: http

(Main) Traefik should automatically renew it on it's normal cycle and will lie in the default (because there is only one) certificate store.

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