diff --git a/ipn/ipnserver/server.go b/ipn/ipnserver/server.go index a7ded9c00..fdbd82b0b 100644 --- a/ipn/ipnserver/server.go +++ b/ipn/ipnserver/server.go @@ -199,7 +199,13 @@ func (s *Server) serveHTTP(w http.ResponseWriter, r *http.Request) { ci = actorWithAccessOverride(actor, string(reason)) } - lah := localapi.NewHandler(ci, lb, s.logf, s.backendLogID) + lah := localapi.NewHandler(localapi.HandlerConfig{ + Actor: ci, + Backend: lb, + Logf: s.logf, + LogID: s.backendLogID, + EventBus: lb.Sys().Bus.Get(), + }) if actor, ok := ci.(*actor); ok { lah.PermitRead, lah.PermitWrite = actor.Permissions(lb.OperatorUserID()) lah.PermitCert = actor.CanFetchCerts() diff --git a/ipn/localapi/localapi.go b/ipn/localapi/localapi.go index 0acc5a65f..a199a2908 100644 --- a/ipn/localapi/localapi.go +++ b/ipn/localapi/localapi.go @@ -172,9 +172,26 @@ var ( metrics = map[string]*clientmetric.Metric{} ) -// NewHandler creates a new LocalAPI HTTP handler. All parameters are required. -func NewHandler(actor ipnauth.Actor, b *ipnlocal.LocalBackend, logf logger.Logf, logID logid.PublicID) *Handler { - return &Handler{Actor: actor, b: b, logf: logf, backendLogID: logID, clock: tstime.StdClock{}} +// NewHandler creates a new LocalAPI HTTP handler from the given config. +func NewHandler(cfg HandlerConfig) *Handler { + return &Handler{ + Actor: cfg.Actor, + b: cfg.Backend, + logf: cfg.Logf, + backendLogID: cfg.LogID, + clock: tstime.StdClock{}, + eventBus: cfg.EventBus, + } +} + +// HandlerConfig carries the settings for a local API handler. +// All fields are required. +type HandlerConfig struct { + Actor ipnauth.Actor + Backend *ipnlocal.LocalBackend + Logf logger.Logf + LogID logid.PublicID + EventBus *eventbus.Bus } type Handler struct { @@ -203,6 +220,7 @@ type Handler struct { logf logger.Logf backendLogID logid.PublicID clock tstime.Clock + eventBus *eventbus.Bus // read-only after initialization } func (h *Handler) Logf(format string, args ...any) { @@ -850,6 +868,7 @@ func (h *Handler) serveDebugPortmap(w http.ResponseWriter, r *http.Request) { NetMon: h.b.NetMon(), DebugKnobs: debugKnobs, ControlKnobs: h.b.ControlKnobs(), + EventBus: h.eventBus, OnChange: func() { logf("portmapping changed.") logf("have mapping: %v", c.HaveMapping()) diff --git a/net/portmapper/portmapper.go b/net/portmapper/portmapper.go index 30535157c..a1ab86815 100644 --- a/net/portmapper/portmapper.go +++ b/net/portmapper/portmapper.go @@ -209,11 +209,8 @@ func (m *pmpMapping) Release(ctx context.Context) { // Config carries the settings for a [Client]. type Config struct { - // EventBus, if non-nil, is used for event publication and subscription by - // portmapper clients created from this config. - // - // TODO(creachadair): As of 2025-03-19 this is optional, but is intended to - // become required non-nil. + // EventBus, which must be non-nil, is used for event publication and + // subscription by portmapper clients created from this config. EventBus *eventbus.Bus // Logf is called to generate text logs for the client. If nil, logger.Discard is used. diff --git a/tsnet/tsnet.go b/tsnet/tsnet.go index 2715917a2..06709bf8b 100644 --- a/tsnet/tsnet.go +++ b/tsnet/tsnet.go @@ -274,7 +274,13 @@ func (s *Server) Loopback() (addr string, proxyCred, localAPICred string, err er // out the CONNECT code from tailscaled/proxy.go that uses // httputil.ReverseProxy and adding auth support. go func() { - lah := localapi.NewHandler(ipnauth.Self, s.lb, s.logf, s.logid) + lah := localapi.NewHandler(localapi.HandlerConfig{ + Actor: ipnauth.Self, + Backend: s.lb, + Logf: s.logf, + LogID: s.logid, + EventBus: s.sys.Bus.Get(), + }) lah.PermitWrite = true lah.PermitRead = true lah.RequiredPassword = s.localAPICred @@ -676,7 +682,13 @@ func (s *Server) start() (reterr error) { go s.printAuthURLLoop() // Run the localapi handler, to allow fetching LetsEncrypt certs. - lah := localapi.NewHandler(ipnauth.Self, lb, tsLogf, s.logid) + lah := localapi.NewHandler(localapi.HandlerConfig{ + Actor: ipnauth.Self, + Backend: lb, + Logf: tsLogf, + LogID: s.logid, + EventBus: sys.Bus.Get(), + }) lah.PermitWrite = true lah.PermitRead = true