I am trying to use traefik to terminate a DNS-over-TLS server. Current using unbound because I was already using it.
I cannot get traefik to serve the proper certificate for this entrypoint. It only seems to serve the default traefik cert which results in an error due to mismatch. This post seems similiar, but not quite and answer does't seem to help me.
relevant docker-compose.yml sections
services:
unbound:
container_name: unbound
image: mvance/unbound:latest
hostname: unbound
networks:
- dot-net
ports:
- 53:53/tcp
- 53:53/udp
environment:
TZ: $TZ
volumes:
- ./unbound/unbound.conf:/opt/unbound/etc/unbound/unbound.conf
- ./unbound/unbound.log:/opt/unbound/etc/unbound/unbound.log
restart: unless-stopped
labels:
- traefik.enable=true
- traefik.tcp.routers.dnsovertls.rule=HostSNI(`dot.$DOMAINNAME`)
- traefik.tcp.routers.dnsovertls.entrypoints=dot
- traefik.tcp.routers.dnsovertls.tls.certResolver=cloudflare
- traefik.tcp.routers.dnsovertls.service=unbound
- traefik.tcp.services.unbound.loadbalancer.server.port=53
traefik:
image: traefik:v3.0.0-beta5
container_name: traefik
logging:
options:
max-size: "3m"
max-file: "3"
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- dot-net
ports:
- 80:80
- 443:443
- 853:853
- 8082:8080
secrets:
- cf_dns_api
- cf_zone_api_all
- cf_api_email
environment:
- CF_ZONE_API_TOKEN_FILE=/run/secrets/cf_zone_api_all
- CF_DNS_API_TOKEN_FILE=/run/secrets/cf_dns_api
- CF_API_EMAIL_FILE=/run/secrets/cf_api_email
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik:/etc/traefik
command:
- --api.dashboard=true
- --api.insecure=true
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --providers.docker.network=dot-net
- --providers.file.filename=/etc/traefik/dynamic-conf.yml
- --log.level=DEBUG #DEBUG INFO WARN ERROR PANIC FATAL
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --entrypoints.dot.address=:853
- --certificatesresolvers.cloudflare.acme.storage=/etc/traefik/acme.json
- --certificatesresolvers.cloudflare.acme.dnschallenge.provider=cloudflare
- --certificatesresolvers.cloudflare.acme.dnschallenge.resolvers=1.1.1.1:53,1.0.0.1:53
dynamic-conf.yml
tls:
options:
default:
alpnProtocols:
- http/1.1
- h2
- dot
- acme-tls/1
Plain DNS resolution works:
dig +tcp @192.168.2.6 example.com
results in NOERROR and returned IP
dns-over-tls results in error:
kdig -d @192.168.2.6 +tls-ca +tls-host=dot.mydomain.com example.com
;; DEBUG: Querying for owner(example.com.), class(1), type(1), server(192.168.2.6), port(853), protocol(TCP)
;; DEBUG: TLS, imported 137 system certificates
;; DEBUG: TLS, received certificate hierarchy:
;; DEBUG: #1, CN=TRAEFIK DEFAULT CERT
;; DEBUG: SHA-256 PIN: Q3IXFGc1HQA5pgq462PVzUeHUT1op8utxDFNSy62jk4=
;; DEBUG: TLS, skipping certificate PIN check
;; DEBUG: TLS, The certificate is NOT trusted. The certificate issuer is unknown. The name in the certificate does not match the expected.
;; WARNING: TLS, handshake failed (Error in the certificate.)
;; ERROR: failed to query server 192.168.2.6@853(TCP)
and in traefik I see:
2024-02-05T12:32:32-05:00 DBG github.com/traefik/traefik/v3/pkg/tls/tlsmanager.go:220 > Serving default certificate for request: ""
2024-02-05T12:32:32-05:00 DBG log/log.go:245 > http: TLS handshake error from 192.168.1.10:50785: read tcp 172.24.0.3:853->192.168.1.10:50785: read: connection reset by peer
Seems it sends the default certificate because there is no host name sent. Am I doing something wrong with my router?
I am able to get around this if I set the default certificate as my ACME certificate, because then the 'default' matches what my request is expecting, but I was hoping to avoid this.