From 1402b1bda2bc4b93a43574d19b042a8af3d0edf8 Mon Sep 17 00:00:00 2001 From: Will Norris Date: Fri, 17 May 2024 15:13:01 -0700 Subject: [PATCH] certs: update tscert dialer to connect to tsnet server This provides an alternate implementation for tscert.TailscaledDialer that tries to find a tsnet server for the requested certificate. This allows caddy-tailscale to be used with caddy's auto_https support. Fixes #19 Signed-off-by: Will Norris --- module.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/module.go b/module.go index d66c5bf..91c032c 100644 --- a/module.go +++ b/module.go @@ -14,6 +14,9 @@ import ( "strings" "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/modules/caddytls" + "github.com/caddyserver/certmagic" + "github.com/tailscale/tscert" "go.uber.org/zap" "tailscale.com/tsnet" ) @@ -21,6 +24,8 @@ import ( func init() { caddy.RegisterNetwork("tailscale", getPlainListener) caddy.RegisterNetwork("tailscale+tls", getTLSListener) + + tscert.TailscaledDialer = localAPIDialer } func getPlainListener(c context.Context, _ string, addr string, _ net.ListenConfig) (any, error) { @@ -262,3 +267,44 @@ func (t *tsnetServerListener) Close() error { _, err := nodes.Delete(t.name) return err } + +// localAPIDialer finds the node that matches the requested certificate in ctx +// and dials that node's local API. +// If not match node is found, the default dailer is used, +// which tries to connect to a local tailscaled on the machine. +func localAPIDialer(ctx context.Context, network, addr string) (net.Conn, error) { + if addr != "local-tailscaled.sock:80" { + return nil, fmt.Errorf("unexpected URL address %q", addr) + } + + serverName, ok := ctx.Value(caddytls.SNIServerNameCtxKey).(string) + if !ok { + return tscert.DefaultDialer(ctx, network, addr) + } + + var tn *tailscaleNode + nodes.Range(func(key, value any) bool { + n, ok := value.(*tailscaleNode) + if !ok || n == nil { + return true + } + for _, d := range n.CertDomains() { + // MatchWildcard is not really necessary since Tailscale doesn't do wildcard certs, + // but caddy uses it for the built-in Tailscale CertGetter, so we do so here as well. + if certmagic.MatchWildcard(serverName, d) { + tn = n + return false + } + } + + return true + }) + + if tn != nil { + if lc, err := tn.LocalClient(); err == nil { + return lc.Dial(ctx, network, addr) + } + } + + return tscert.DefaultDialer(ctx, network, addr) +}