From f0ae4ce2905ae135f90f55e5f752ac326263def4 Mon Sep 17 00:00:00 2001 From: 9seconds Date: Mon, 23 Mar 2026 19:19:21 +0100 Subject: [PATCH] Validate domain fronting availability --- internal/cli/doctor.go | 43 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/internal/cli/doctor.go b/internal/cli/doctor.go index 9b8d736..21ea0e1 100644 --- a/internal/cli/doctor.go +++ b/internal/cli/doctor.go @@ -8,6 +8,7 @@ import ( "net" "os" "slices" + "strconv" "strings" "text/template" "time" @@ -56,6 +57,13 @@ var ( tplEDNSSNIMatch = template.Must( template.New("").Parse(" ❌ Hostname {{ .hostname }} {{ if .resolved }}is resolved to {{ .resolved }} addresses, not {{ if .ip4 }}{{ .ip4 }}{{ else }}{{ .ip6 }}{{ end }}{{ else }}cannot be resolved to any host{{ end }}\n"), ) + + tplOFrontingDomain = template.Must( + template.New("").Parse(" ✅ {{ .address }} is reachable\n"), + ) + tplEFrontingDomain = template.Must( + template.New("").Parse(" ❌ {{ .address }}: {{ .error }}\n"), + ) ) type Doctor struct { @@ -104,6 +112,9 @@ func (d *Doctor) Run(cli *CLI, version string) error { everythingOK = d.checkNetwork(value) && everythingOK } + fmt.Println("Validate fronting domain connectivity") + everythingOK = d.checkFrontingDomain(base) && everythingOK + fmt.Println("Validate SNI-DNS match") everythingOK = d.checkSecretHost(resolver, base) && everythingOK @@ -279,6 +290,38 @@ func (d *Doctor) checkNetworkAddresses(ntw mtglib.Network, addresses []string) e return err } +func (d *Doctor) checkFrontingDomain(ntw mtglib.Network) bool { + host := d.conf.Secret.Host + if ip := d.conf.GetDomainFrontingIP(nil); ip != "" { + host = ip + } + + port := d.conf.GetDomainFrontingPort(mtglib.DefaultDomainFrontingPort) + address := net.JoinHostPort(host, strconv.Itoa(int(port))) + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + dialer := ntw.NativeDialer() + + conn, err := dialer.DialContext(ctx, "tcp", address) + if err != nil { + tplEFrontingDomain.Execute(os.Stdout, map[string]any{ //nolint: errcheck + "address": address, + "error": err, + }) + return false + } + + conn.Close() //nolint: errcheck + + tplOFrontingDomain.Execute(os.Stdout, map[string]any{ //nolint: errcheck + "address": address, + }) + + return true +} + func (d *Doctor) checkSecretHost(resolver *net.Resolver, ntw mtglib.Network) bool { addresses, err := resolver.LookupIPAddr(context.Background(), d.conf.Secret.Host) if err != nil {