tl;dr: I want Traefik to serve a TLS certificate for go.<internal> for requests to https://go/. As I think this will be accepted by a browser.
I've recently setup ACME+LetsEncrypt for several domain served via traefik on my internal home network. And I think I don't understand how Traefik choses which TLS certificate to present based on router rules, and why it's using a self signed "traefik default" cert instead of one of the LetsEncrypt ones.
One of the services I have is a "go link" service (like https://www.golinks.io/, or https://www.trot.to/) and serves go.<internal>, and I have configured Traefik to also match go because my home network is configured to use <internal> as a DNS Search Suffix. This makes anything performing a go DNS query lookup go.<internal>, and land in the same place. This is extremely convenient for links because i can write out go/abc and get to the service.
After adding TLS support, it appears that Traefik has more complex TLS & HTTP Host matching than I expected, as the original rules do not function with the short name go but only the fully qualified domain name: go.<internal>. I suspect this is because the TLS settings (tls.domains) are matched before the Host rule can be checked, and go does not match any of the existing names.
I was wondering if it was possible to have Traefik present go.<internal> in this case as I expect? I'm a little fuzzy on if the browser would accept a certificate for go.<internal> when searching for go with a <internal> dns suffix, but getting Traefik to present the cert is the first step in troubleshooting that.
# Note this is also exposed over VPN, hence the go.<vpn> match & certs.
# Access over go.<internal> and go.<vpn> both work as expected.
http:
routers:
go-router:
entryPoints:
- "https-entry"
rule: "Host(`go`, `go.<internal>`, `go.<vpn>`)"
tls:
certResolver: "internal-certs"
domains:
- main: "go.<internal>"
sans:
- "go.<vpn>"
service: "goto-service"
AFAIK the request URL domain name needs to match the cert, either main, sans or wildcard sans. Otherwise the browser will show an error.
You can declare a default cert that is used when there is no domain match. (with client error)
A regular LetsEncrypt cert can only be issued for a domain that’s reachable on the Internet, either with httpChallenge or tlsChallenge. dnsChallenge enables sub-domains not reachable on the Internet and wildcards.
I hadn't considered explicitly setting a default certificate to help handle this situation. I can check that to see how the browser validates the certificates, and then make further choices about how to proceed.
Assuming default go.<internal> certificate doesn't work, then I think the solution for this use case is one of: 1) modify browser clients to perform go -> go.<internal> request rewriting, or 2) change Traefik config to expose go over http, and go.<internal> over https (and handle the added complexity of http -> https redirection in the config).
I've been using go.<internal> as shorthand for (effectively) go.internal.example.com. It's an address that only resolves to an IP inside my home network. I've been successfully using DNS-01 to get TLS certificates for it.
What I think is happening: putting https://go/ into a browser on my home network resolves to the same IP address as go.internal.example.com (because i have internal.example.com as a DNS search suffix on my home network). Then Traefik gets a request with (I think) go as the hostname, and can't find a matching TLS Cert and provides the default certificate.
I still need to check if using the go.internal.example.com certificate will satisfy the browsers request for go given the DNS settings on the network, or not.
Checking what clients do: curl sends go as the hostname in the TLS connection when it would resolve to the same address as go.internal.example.com. I assume it's going to be the same for all other clients (eg: browsers). Somewhat expected, further validation shows that setting go.internal.example.com as the internal certificate fails to validate for a client expecting go.
# Install Lets Encrypt Staging CA:
# See https://letsencrypt.org/docs/staging-environment/#root-certificates
> curl --connect-to go:443:127.0.0.1:8443 \
--trace /dev/stdout 'https://go/to/home' \
--cacert letsencrypt-stg-root-x1.pem
# Lots of output that takes some squinting to see what hostname was sent,
# and what hostnames the returned cert supports.
All of this to say: even when Traefik returns the certs I was hoping it would, this functionality can not be accomplished, because that's not how browsers validate TLS hostnames.
Thanks for the help. I'm going to setup some HTTP routers to redirect http://go/ to https://go.internal.example.com/, and fiddle with HTTP->HTTPS redirects.