How to get Let's Encrypt SAN certificate for domain and subdomains in one place?

So I have traefik on traefik.example.com and my-service on example.com in docker-swarm mode and I want to get and define Let's Encrypt certificate for example.com and SAN for *.example.com (tls/http challenge only). Other words any other services on www.example.com, smth.example.com etc. should work by https with this settings. What should I write in config?

Current configuration (doesn't work correct):

# docker-compose.traefik.yaml
version: '3.9'

services:
  traefik:
    image: traefik:v2.10
    
    ports:
      - 80:80
      - 443:443
      
    deploy:
      restart_policy:
        condition: on-failure
      placement:
        constraints:
          - node.labels.certificate-storage == true
          - node.role == manager
      labels:
        - traefik.enable=true

        - traefik.http.middlewares.https-redirect.redirectscheme.scheme=https
        - traefik.http.middlewares.https-redirect.redirectscheme.permanent=true

        - traefik.http.routers.traefik-public-http.rule=Host(`traefik.example.com`)
        - traefik.http.routers.traefik-public-http.entrypoints=http
        - traefik.http.routers.traefik-public-http.middlewares=https-redirect

        - traefik.http.routers.traefik-public-https.rule=Host(`traefik.example.com`)
        - traefik.http.routers.traefik-public-https.entrypoints=https
        - traefik.http.routers.traefik-public-https.tls=true
        - traefik.http.routers.traefik-public-https.tls.certresolver=le
        - traefik.http.routers.traefik-public-https.tls.domains[0].main=example.com
        - traefik.http.routers.traefik-public-https.tls.domains[0].sans=*.example.com
        - traefik.http.routers.traefik-public-https.service=api@internal
        
        - traefik.http.services.traefik.loadbalancer.server.port=8080

    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - traefik-public-certificates:/certificates

    command:
      - --providers.docker
      - --providers.docker.exposedbydefault=false
      - --providers.docker.swarmmode
      
      - --entrypoints.http.address=:80
      - --entrypoints.https.address=:443
      
      - --certificatesresolvers.le.acme.email=test@gmail.com
      - --certificatesresolvers.le.acme.storage=/certificates/acme.json
      - --certificatesresolvers.le.acme.tlschallenge=true

    networks:
      - traefik-public

volumes:
  traefik-public-certificates:

networks:
  traefik-public:
    driver: overlay
    attachable: true
# docker-compose.yaml
version: "3.9"

services:
  my-service:
    image: my-service:latest
    
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
      labels:
        - traefik.enable=true
        
        - traefik.http.services.my-service.loadbalancer.server.port=8080

        - traefik.http.middlewares.https-redirect.redirectscheme.scheme=https
        - traefik.http.middlewares.https-redirect.redirectscheme.permanent=true

        - traefik.http.routers.my-service-http.rule=Host(`example.com`)
        - traefik.http.routers.my-service-http.entrypoints=http
        - traefik.http.routers.my-service-http.middlewares=https-redirect
        
        - traefik.http.routers.my-service-https.rule=Host(`example.com`)
        - traefik.http.routers.my-service-https.entrypoints=https
        - traefik.http.routers.my-service-https.tls=true
        - traefik.http.routers.my-service-https.tls.certresolver=le

    networks:
      - traefik-public

networks:
  traefik-public:

How many Docker Swarm managers do you have?

In general Traefik open source and LetsEncrypt only works with a single instance.

One manager. And all services in one node for now

For wildcards you need to use dnsChallenge, define via main/sans, see Traefik LE doc.

For a couple of sub-domains you can just get separate certs, AFAIK current LE limit is 50 different per week.

Yeah, separate certificates is my way for now. Anyway it's strange that Let's Encrypt provides wildcard SAN certificates, Traefik seems to provide them (by tls/http challenge) but I can't configure them

And one more question.

I don't understand why we describe SAN certificates for particular route?

- "traefik.http.routers.traefik-public-https.tls.domains[0].main=example.com"
- "traefik.http.routers.traefik-public-https.tls.domains[0].sans=traefik.example.com, www.example.com"

Looks like it should be in a global config because of we use certificate for domain, not for route (generally).

I also found config for SAN certificates looks like more correct

# traefik.yaml
certificatesResolvers:
  le:
    acme:
      email: $EMAIL
      storage: /acme.json
      httpChallenge:
        entryPoint: http
      tlsChallenge:
        keyType: "EC256"
      domains:
        - main: "example.com"
          sans:
            - "traefik.example.com"
            - "www.example.com"

But why I can't describe this resolver inside command section of docker compose? Why I can set both httpChallenge and tlsChallenge? Is this gonna work? Why I have to set keyType value instead of true if we have default value (according doc)? Why this way of using SAN certificates doesn't work for me? Bad config or this is beta-feature?

You can declare certResolver via command, see doc.

AFAIK you can only use one type of challenge.

But I can't declare main/sans via CLI certResolver

Can I use defaultGeneratedCert settings for my case?

Yes, you can:

--entrypoints..http.tls.domains[n].main:
Default subject name.

--entrypoints..http.tls.domains[n].sans:
Subject alternative names.

command traefik error: failed to decode configuration from flags: field not found, node: tls

How about you search this forum, as this has been discussed and solved here many times before.

Note the [n] is an index, should be:

[0].main=example.com
[0].sans=*.example.com

My experiments show that this is the only way to get a SAN certificate:

#docker-compose.traefik (services.traefik.deploy.labels)
- traefik.http.routers.traefik.tls.domains[0].main=example.com
- traefik.http.routers.traefik.tls.domains[0].sans=sub1.example.com, sub2.example.com, etc.example.com

Asterisk template unfortunatelly doesn't work

- traefik.http.routers.traefik.tls.domains[0].sans=*.example.com

Unable to obtain ACME certificate for domains "example.com,.example.com"" rule="Host(traefik.example.com)" error="unable to generate a certificate for the domains [example.com .example.com]: error: one or more domains had a problem:\n[.example.com] [.example.com] acme: could not determine solvers\n" routerName=traefik@docker providerName=le.acme ACME CA="https://acme-v02.api.letsencrypt.org/directory"

Did you try with quotes?

- "--entrypoints.websecure.http.tls.domains[0].main=${DOMAIN}"
- "--entrypoints.websecure.http.tls.domains[0].sans=*.${DOMAIN}"