From 6beb3184d5df04012798a0dfd0a8285628dfe3dd Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 9 Nov 2022 21:16:20 -0800 Subject: [PATCH] ipn/ipnlocal: don't serve a TLS cert unless it has webserver config Even if the name is right, or is configured on a different port. Updates tailscale/corp#7515 Change-Id: I8b721968f3241af10d98431e1b5ba075223e6cd3 Signed-off-by: Brad Fitzpatrick --- ipn/ipnlocal/serve.go | 62 +++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/ipn/ipnlocal/serve.go b/ipn/ipnlocal/serve.go index 614e25075..ec971fb1c 100644 --- a/ipn/ipnlocal/serve.go +++ b/ipn/ipnlocal/serve.go @@ -54,7 +54,7 @@ func (b *LocalBackend) HandleInterceptedTCPConn(dport uint16, srcAddr netip.Addr } hs := &http.Server{ TLSConfig: &tls.Config{ - GetCertificate: b.getTLSServeCert, + GetCertificate: b.getTLSServeCertForPort(dport), }, Handler: http.HandlerFunc(b.serveWebHandler), BaseContext: func(_ net.Listener) context.Context { @@ -123,20 +123,11 @@ func (b *LocalBackend) getServeHandler(r *http.Request) (_ ipn.HTTPHandlerView, b.logf("[unexpected] localbackend: no serveHTTPContext in request") return z, false } - sni := r.TLS.ServerName - key := ipn.HostPort(fmt.Sprintf("%s:%v", sni, sctx.DestPort)) - - b.mu.Lock() - defer b.mu.Unlock() - - if !b.serveConfig.Valid() { - return z, false - } - - wsc, ok := b.serveConfig.Web().GetOk(key) + wsc, ok := b.webServerConfig(r.TLS.ServerName, sctx.DestPort) if !ok { return z, false } + path := r.URL.Path for { if h, ok := wsc.Handlers().GetOk(path); ok { @@ -172,19 +163,38 @@ func (b *LocalBackend) serveWebHandler(w http.ResponseWriter, r *http.Request) { http.Error(w, "empty handler", 500) } -func (b *LocalBackend) getTLSServeCert(hi *tls.ClientHelloInfo) (*tls.Certificate, error) { - if hi == nil || hi.ServerName == "" { - return nil, errors.New("no SNI ServerName") +func (b *LocalBackend) webServerConfig(sniName string, port uint16) (c ipn.WebServerConfigView, ok bool) { + key := ipn.HostPort(fmt.Sprintf("%s:%v", sniName, port)) + + b.mu.Lock() + defer b.mu.Unlock() + + if !b.serveConfig.Valid() { + return c, false + } + return b.serveConfig.Web().GetOk(key) +} + +func (b *LocalBackend) getTLSServeCertForPort(port uint16) func(hi *tls.ClientHelloInfo) (*tls.Certificate, error) { + return func(hi *tls.ClientHelloInfo) (*tls.Certificate, error) { + if hi == nil || hi.ServerName == "" { + return nil, errors.New("no SNI ServerName") + } + _, ok := b.webServerConfig(hi.ServerName, port) + if !ok { + return nil, errors.New("no webserver configured for name/port") + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + pair, err := b.GetCertPEM(ctx, hi.ServerName) + if err != nil { + return nil, err + } + cert, err := tls.X509KeyPair(pair.CertPEM, pair.KeyPEM) + if err != nil { + return nil, err + } + return &cert, nil } - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) - defer cancel() - pair, err := b.GetCertPEM(ctx, hi.ServerName) - if err != nil { - return nil, err - } - cert, err := tls.X509KeyPair(pair.CertPEM, pair.KeyPEM) - if err != nil { - return nil, err - } - return &cert, nil }