From b9745a5375c6fa28a50aa11b283f744c1950a9ef Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Sat, 11 Jan 2025 14:21:31 -0800 Subject: [PATCH] lanscaping: drop more ipnauth -rwxr-xr-x@ 1 bradfitz staff 9871042 Jan 11 14:21 /Users/bradfitz/bin/tailscaled.min -rwxr-xr-x@ 1 bradfitz staff 9765016 Jan 11 14:21 /Users/bradfitz/bin/tailscaled.minlinux Change-Id: I558009a377d699e51c585f8f397e1580434bdddd Signed-off-by: Brad Fitzpatrick --- cmd/tailscaled/depaware-minlinux.txt | 6 +- ipn/ipnlocal/local.go | 145 ++++------------------ ipn/ipnserver/actor.go | 176 --------------------------- ipn/ipnserver/server.go | 154 ++--------------------- ipn/localapi/localapi.go | 11 +- 5 files changed, 33 insertions(+), 459 deletions(-) delete mode 100644 ipn/ipnserver/actor.go diff --git a/cmd/tailscaled/depaware-minlinux.txt b/cmd/tailscaled/depaware-minlinux.txt index 2732d19eb..b875643f7 100644 --- a/cmd/tailscaled/depaware-minlinux.txt +++ b/cmd/tailscaled/depaware-minlinux.txt @@ -38,7 +38,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de tailscale.com/internal/noiseconn from tailscale.com/control/controlclient tailscale.com/ipn from tailscale.com/cmd/tailscaled+ tailscale.com/ipn/conffile from tailscale.com/cmd/tailscaled+ - tailscale.com/ipn/ipnauth from tailscale.com/ipn/ipnlocal+ tailscale.com/ipn/ipnlocal from tailscale.com/cmd/tailscaled+ tailscale.com/ipn/ipnserver from tailscale.com/cmd/tailscaled tailscale.com/ipn/ipnstate from tailscale.com/control/controlclient+ @@ -89,7 +88,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de tailscale.com/types/structs from tailscale.com/control/controlclient+ tailscale.com/types/views from tailscale.com/control/controlclient+ tailscale.com/util/clientmetric from tailscale.com/control/controlclient+ - tailscale.com/util/ctxkey from tailscale.com/ipn/ipnserver+ + tailscale.com/util/ctxkey from tailscale.com/types/logger 💣 tailscale.com/util/deephash from tailscale.com/ipn/ipnlocal+ tailscale.com/util/dnsname from tailscale.com/hostinfo+ tailscale.com/util/execqueue from tailscale.com/control/controlclient @@ -113,7 +112,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de tailscale.com/util/testenv from tailscale.com/control/controlclient+ tailscale.com/util/uniq from tailscale.com/ipn/ipnlocal+ tailscale.com/util/vizerror from tailscale.com/tailcfg+ - tailscale.com/util/winutil from tailscale.com/ipn/ipnauth tailscale.com/util/zstdframe from tailscale.com/control/controlclient tailscale.com/version from tailscale.com/cmd/tailscaled+ tailscale.com/version/distro from tailscale.com/cmd/tailscaled+ @@ -228,7 +226,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de os from crypto/rand+ os/exec from tailscale.com/cmd/tailscaled+ os/signal from tailscale.com/cmd/tailscaled - os/user from tailscale.com/ipn/ipnauth+ + os/user from tailscale.com/ipn/ipnserver path from io/fs+ path/filepath from crypto/x509+ reflect from crypto/x509+ diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index 23752446e..b7dbf1382 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -42,7 +42,6 @@ import ( "tailscale.com/hostinfo" "tailscale.com/ipn" "tailscale.com/ipn/conffile" - "tailscale.com/ipn/ipnauth" "tailscale.com/ipn/ipnstate" "tailscale.com/net/ipset" "tailscale.com/net/netcheck" @@ -88,6 +87,8 @@ import ( "tailscale.com/wgengine/wgcfg/nmcfg" ) +type ipnauthActor interface{} + var controlDebugFlags = getControlDebugFlags() func getControlDebugFlags() []string { @@ -124,11 +125,11 @@ func RegisterNewSSHServer(fn newSSHServerFunc) { } // watchSession represents a WatchNotifications channel, -// an [ipnauth.Actor] that owns it (e.g., a connected GUI/CLI), +// an [ipnauthActor] that owns it (e.g., a connected GUI/CLI), // and sessionID as required to close targeted buses. type watchSession struct { ch chan *ipn.Notify - owner ipnauth.Actor // or nil + owner ipnauthActor // or nil sessionID string cancel context.CancelFunc // to shut down the session } @@ -235,9 +236,9 @@ type LocalBackend struct { endpoints []tailcfg.Endpoint blocked bool keyExpired bool - authURL string // non-empty if not Running - authURLTime time.Time // when the authURL was received from the control server - authActor ipnauth.Actor // an actor who called [LocalBackend.StartLoginInteractive] last, or nil + authURL string // non-empty if not Running + authURLTime time.Time // when the authURL was received from the control server + authActor ipnauthActor // an actor who called [LocalBackend.StartLoginInteractive] last, or nil egg bool prevIfState *netmon.State loginFlags controlclient.LoginFlags @@ -260,7 +261,7 @@ type LocalBackend struct { componentLogUntil map[string]componentLogState // c2nUpdateStatus is the status of c2n-triggered client update. c2nUpdateStatus updateStatus - currentUser ipnauth.Actor + currentUser ipnauthActor selfUpdateProgress []ipnstate.UpdateProgress lastSelfUpdateState ipnstate.SelfUpdateStatus // capForcedNetfilter is the netfilter that control instructs Linux clients @@ -2249,10 +2250,10 @@ func (b *LocalBackend) WatchNotifications(ctx context.Context, mask ipn.NotifyWa b.WatchNotificationsAs(ctx, nil, mask, onWatchAdded, fn) } -// WatchNotificationsAs is like WatchNotifications but takes an [ipnauth.Actor] +// WatchNotificationsAs is like WatchNotifications but takes an [ipnauthActor] // as an additional parameter. If non-nil, the specified callback is invoked // only for notifications relevant to this actor. -func (b *LocalBackend) WatchNotificationsAs(ctx context.Context, actor ipnauth.Actor, mask ipn.NotifyWatchOpt, onWatchAdded func(), fn func(roNotify *ipn.Notify) (keepGoing bool)) { +func (b *LocalBackend) WatchNotificationsAs(ctx context.Context, actor ipnauthActor, mask ipn.NotifyWatchOpt, onWatchAdded func(), fn func(roNotify *ipn.Notify) (keepGoing bool)) { ch := make(chan *ipn.Notify, 128) sessionID := rands.HexString(16) origFn := fn @@ -2442,10 +2443,6 @@ type notificationTarget struct { // TODO(nickkhyl): make this field cross-platform rather // than Windows-specific. userID ipn.WindowsUserID - // clientID identifies a client that should be the exclusive recipient - // of the notification. A zero value indicates that notification should - // be sent to all sessions of the specified user. - clientID ipnauth.ClientID } var allClients = notificationTarget{} // broadcast to all connected clients @@ -2454,11 +2451,10 @@ var allClients = notificationTarget{} // broadcast to all connected clients // representing the same user as the specified actor. If the actor represents // a specific connected client, the [ipnauth.ClientID] must also match. // If the actor is nil, the [notificationTarget] matches all actors. -func toNotificationTarget(actor ipnauth.Actor) notificationTarget { +func toNotificationTarget(actor ipnauthActor) notificationTarget { t := notificationTarget{} if actor != nil { - t.userID = actor.UserID() - t.clientID, _ = actor.ClientID() + // lanscaping } return t } @@ -2466,22 +2462,8 @@ func toNotificationTarget(actor ipnauth.Actor) notificationTarget { // match reports whether the specified actor should receive notifications // targeting t. If the actor is nil, it should only receive notifications // intended for all users. -func (t notificationTarget) match(actor ipnauth.Actor) bool { - if t == allClients { - return true - } - if actor == nil { - return false - } - if t.userID != "" && t.userID != actor.UserID() { - return false - } - if t.clientID != ipnauth.NoClientID { - clientID, ok := actor.ClientID() - if !ok || clientID != t.clientID { - return false - } - } +func (t notificationTarget) match(actor ipnauthActor) bool { + return true } @@ -2520,7 +2502,7 @@ func (b *LocalBackend) sendToLocked(n ipn.Notify, recipient notificationTarget) // If url is "", it is equivalent to calling [LocalBackend.resetAuthURLLocked] with b.mu held. func (b *LocalBackend) setAuthURL(url string) { var popBrowser, keyExpired bool - var recipient ipnauth.Actor + var recipient ipnauthActor b.mu.Lock() switch { @@ -2555,7 +2537,7 @@ func (b *LocalBackend) setAuthURL(url string) { // keyExpired is the value of b.keyExpired upon entry and indicates // whether the node's key has expired. // It must not be called with b.mu held. -func (b *LocalBackend) popBrowserAuthNow(url string, keyExpired bool, recipient ipnauth.Actor) { +func (b *LocalBackend) popBrowserAuthNow(url string, keyExpired bool, recipient ipnauthActor) { b.logf("popBrowserAuthNow(%q): url=%v, key-expired=%v, seamless-key-renewal=%v", maybeUsernameOf(recipient), url != "", keyExpired, b.seamlessRenewalEnabled()) // Deconfigure the local network data plane if: @@ -2836,45 +2818,10 @@ func (b *LocalBackend) InServerMode() bool { // Currently (as of 2024-08-26), this is only used on Windows. // We plan to remove it as part of the multi-user and unattended mode improvements // as we progress on tailscale/corp#18342. -func (b *LocalBackend) CheckIPNConnectionAllowed(actor ipnauth.Actor) error { - b.mu.Lock() - defer b.mu.Unlock() - serverModeUid := b.pm.CurrentUserID() - if serverModeUid == "" { - // Either this platform isn't a "multi-user" platform or we're not yet - // running as one. - return nil - } - if !b.pm.CurrentPrefs().ForceDaemon() { - return nil - } - - // Always allow Windows SYSTEM user to connect, - // even if Tailscale is currently being used by another user. - if actor.IsLocalSystem() { - return nil - } - - uid := actor.UserID() - if uid == "" { - return errors.New("empty user uid in connection identity") - } - if uid != serverModeUid { - return fmt.Errorf("Tailscale running in server mode (%q); connection from %q not allowed", b.tryLookupUserName(string(serverModeUid)), b.tryLookupUserName(string(uid))) - } +func (b *LocalBackend) CheckIPNConnectionAllowed(actor ipnauthActor) error { return nil } -// tryLookupUserName tries to look up the username for the uid. -// It returns the username on success, or the UID on failure. -func (b *LocalBackend) tryLookupUserName(uid string) string { - u, err := ipnauth.LookupUserFromID(b.logf, uid) - if err != nil { - return uid - } - return u.Username -} - // StartLoginInteractive requests a new interactive login from controlclient, // unless such a flow is already in progress, in which case // StartLoginInteractive attempts to pick up the in-progress flow where it left @@ -2883,12 +2830,12 @@ func (b *LocalBackend) StartLoginInteractive(ctx context.Context) error { return b.StartLoginInteractiveAs(ctx, nil) } -// StartLoginInteractiveAs is like StartLoginInteractive but takes an [ipnauth.Actor] +// StartLoginInteractiveAs is like StartLoginInteractive but takes an [ipnauthActor] // as an additional parameter. If non-nil, the specified user is expected to complete // the interactive login, and therefore will receive the BrowseToURL notification once // the control plane sends us one. Otherwise, the notification will be delivered to all // active [watchSession]s. -func (b *LocalBackend) StartLoginInteractiveAs(ctx context.Context, user ipnauth.Actor) error { +func (b *LocalBackend) StartLoginInteractiveAs(ctx context.Context, user ipnauthActor) error { b.mu.Lock() if b.cc == nil { panic("LocalBackend.assertClient: b.cc == nil") @@ -3037,46 +2984,6 @@ func (b *LocalBackend) shouldUploadServices() bool { return !p.ShieldsUp() && b.netMap.CollectServices } -// SetCurrentUser is used to implement support for multi-user systems (only -// Windows 2022-11-25). On such systems, the uid is used to determine which -// user's state should be used. The current user is maintained by active -// connections open to the backend. -// -// When the backend initially starts it will typically start with no user. Then, -// the first connection to the backend from the GUI frontend will set the -// current user. Once set, the current user cannot be changed until all previous -// connections are closed. The user is also used to determine which -// LoginProfiles are accessible. -// -// In unattended mode, the backend will start with the user which enabled -// unattended mode. The user must disable unattended mode before the user can be -// changed. -// -// On non-multi-user systems, the user should be set to nil. -// -// SetCurrentUser returns the ipn.WindowsUserID associated with the user -// when successful. -func (b *LocalBackend) SetCurrentUser(actor ipnauth.Actor) (ipn.WindowsUserID, error) { - var uid ipn.WindowsUserID - if actor != nil { - uid = actor.UserID() - } - - unlock := b.lockAndGetUnlock() - defer unlock() - - if b.pm.CurrentUserID() == uid { - return uid, nil - } - b.pm.SetCurrentUserID(uid) - if c, ok := b.currentUser.(ipnauth.ActorCloser); ok { - c.Close() - } - b.currentUser = actor - b.resetForProfileChangeLockedOnEntry(unlock) - return uid, nil -} - func (b *LocalBackend) CheckPrefs(p *ipn.Prefs) error { b.mu.Lock() defer b.mu.Unlock() @@ -3989,12 +3896,6 @@ func (b *LocalBackend) ResetForClientDisconnect() { b.setNetMapLocked(nil) b.pm.Reset() - if b.currentUser != nil { - if c, ok := b.currentUser.(ipnauth.ActorCloser); ok { - c.Close() - } - b.currentUser = nil - } b.keyExpired = false b.resetAuthURLLocked() b.activeLogin = "" @@ -5149,12 +5050,8 @@ func (b *LocalBackend) srcIPHasCapForFilter(srcIP netip.Addr, cap tailcfg.NodeCa // maybeUsernameOf returns the actor's username if the actor // is non-nil and its username can be resolved. -func maybeUsernameOf(actor ipnauth.Actor) string { - var username string - if actor != nil { - username, _ = actor.Username() - } - return username +func maybeUsernameOf(actor ipnauthActor) string { + return "root" } // VIPServices returns the list of tailnet services that this node diff --git a/ipn/ipnserver/actor.go b/ipn/ipnserver/actor.go deleted file mode 100644 index 374dfcfdc..000000000 --- a/ipn/ipnserver/actor.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright (c) Tailscale Inc & AUTHORS -// SPDX-License-Identifier: BSD-3-Clause - -package ipnserver - -import ( - "context" - "errors" - "fmt" - "net" - "runtime" - - "tailscale.com/ipn" - "tailscale.com/ipn/ipnauth" - "tailscale.com/types/logger" - "tailscale.com/util/ctxkey" - "tailscale.com/version" -) - -var _ ipnauth.Actor = (*actor)(nil) - -// actor implements [ipnauth.Actor] and provides additional functionality that is -// specific to the current (as of 2024-08-27) permission model. -// -// Deprecated: this type exists for compatibility reasons and will be removed as -// we progress on tailscale/corp#18342. -type actor struct { - logf logger.Logf - ci *ipnauth.ConnIdentity - - clientID ipnauth.ClientID - isLocalSystem bool // whether the actor is the Windows' Local System identity. -} - -func newActor(logf logger.Logf, c net.Conn) (*actor, error) { - ci, err := ipnauth.GetConnIdentity(logf, c) - if err != nil { - return nil, err - } - var clientID ipnauth.ClientID - if pid := ci.Pid(); pid != 0 { - // Derive [ipnauth.ClientID] from the PID of the connected client process. - // TODO(nickkhyl): This is transient and will be re-worked as we - // progress on tailscale/corp#18342. At minimum, we should use a 2-tuple - // (PID + StartTime) or a 3-tuple (PID + StartTime + UID) to identify - // the client process. This helps prevent security issues where a - // terminated client process's PID could be reused by a different - // process. This is not currently an issue as we allow only one user to - // connect anyway. - // Additionally, we should consider caching authentication results since - // operations like retrieving a username by SID might require network - // connectivity on domain-joined devices and/or be slow. - clientID = ipnauth.ClientIDFrom(pid) - } - return &actor{logf: logf, ci: ci, clientID: clientID, isLocalSystem: connIsLocalSystem(ci)}, nil -} - -// IsLocalSystem implements [ipnauth.Actor]. -func (a *actor) IsLocalSystem() bool { - return a.isLocalSystem -} - -// IsLocalAdmin implements [ipnauth.Actor]. -func (a *actor) IsLocalAdmin(operatorUID string) bool { - return a.isLocalSystem || connIsLocalAdmin(a.logf, a.ci, operatorUID) -} - -// UserID implements [ipnauth.Actor]. -func (a *actor) UserID() ipn.WindowsUserID { - return a.ci.WindowsUserID() -} - -func (a *actor) pid() int { - return a.ci.Pid() -} - -// ClientID implements [ipnauth.Actor]. -func (a *actor) ClientID() (_ ipnauth.ClientID, ok bool) { - return a.clientID, a.clientID != ipnauth.NoClientID -} - -// Username implements [ipnauth.Actor]. -func (a *actor) Username() (string, error) { - if a.ci == nil { - a.logf("[unexpected] missing ConnIdentity in ipnserver.actor") - return "", errors.New("missing ConnIdentity") - } - switch runtime.GOOS { - case "windows": - tok, err := a.ci.WindowsToken() - if err != nil { - return "", fmt.Errorf("get windows token: %w", err) - } - defer tok.Close() - return tok.Username() - case "darwin", "linux", "illumos", "solaris": - return "root", nil - default: - return "", errors.New("unsupported OS") - } -} - -type actorOrError struct { - actor *actor - err error -} - -func (a actorOrError) unwrap() (*actor, error) { - return a.actor, a.err -} - -var errNoActor = errors.New("connection actor not available") - -var actorKey = ctxkey.New("ipnserver.actor", actorOrError{err: errNoActor}) - -// contextWithActor returns a new context that carries the identity of the actor -// owning the other end of the [net.Conn]. It can be retrieved with [actorFromContext]. -func contextWithActor(ctx context.Context, logf logger.Logf, c net.Conn) context.Context { - actor, err := newActor(logf, c) - return actorKey.WithValue(ctx, actorOrError{actor: actor, err: err}) -} - -// actorFromContext returns an [actor] associated with ctx, -// or an error if the context does not carry an actor's identity. -func actorFromContext(ctx context.Context) (*actor, error) { - return actorKey.Value(ctx).unwrap() -} - -func connIsLocalSystem(ci *ipnauth.ConnIdentity) bool { - token, err := ci.WindowsToken() - return err == nil && token.IsLocalSystem() -} - -// connIsLocalAdmin reports whether the connected client has administrative -// access to the local machine, for whatever that means with respect to the -// current OS. -// -// This is useful because tailscaled itself always runs with elevated rights: -// we want to avoid privilege escalation for certain mutative operations. -func connIsLocalAdmin(logf logger.Logf, ci *ipnauth.ConnIdentity, operatorUID string) bool { - if ci == nil { - logf("[unexpected] missing ConnIdentity in LocalAPI Handler") - return false - } - switch runtime.GOOS { - case "windows": - tok, err := ci.WindowsToken() - if err != nil { - if !errors.Is(err, ipnauth.ErrNotImplemented) { - logf("ipnauth.ConnIdentity.WindowsToken() error: %v", err) - } - return false - } - defer tok.Close() - - return tok.IsElevated() - - case "darwin": - // Unknown, or at least unchecked on sandboxed macOS variants. Err on - // the side of less permissions. - // - // authorizeServeConfigForGOOSAndUserContext should not call - // connIsLocalAdmin on sandboxed variants anyway. - if version.IsSandboxedMacOS() { - return false - } - // This is a standalone tailscaled setup, use the same logic as on - // Linux. - fallthrough - case "linux": - return true - - default: - return false - } -} diff --git a/ipn/ipnserver/server.go b/ipn/ipnserver/server.go index bf42ea865..464f6483d 100644 --- a/ipn/ipnserver/server.go +++ b/ipn/ipnserver/server.go @@ -8,8 +8,6 @@ package ipnserver import ( "context" "encoding/json" - "errors" - "fmt" "io" "net" "net/http" @@ -27,7 +25,6 @@ import ( "tailscale.com/net/netmon" "tailscale.com/types/logger" "tailscale.com/types/logid" - "tailscale.com/util/mak" "tailscale.com/util/set" ) @@ -49,9 +46,8 @@ type Server struct { // lock order: mu, then LocalBackend.mu mu sync.Mutex lastUserID ipn.WindowsUserID // tracks last userid; on change, Reset state for paranoia - activeReqs map[*http.Request]*actor - backendWaiter waiterSet // of LocalBackend waiters - zeroReqWaiter waiterSet // of blockUntilZeroConnections waiters + backendWaiter waiterSet // of LocalBackend waiters + zeroReqWaiter waiterSet // of blockUntilZeroConnections waiters } func (s *Server) mustBackend() *ipnlocal.LocalBackend { @@ -170,21 +166,10 @@ func (s *Server) serveHTTP(w http.ResponseWriter, r *http.Request) { return } - ci, err := actorFromContext(r.Context()) - if err != nil { - if errors.Is(err, errNoActor) { - http.Error(w, "internal error: "+err.Error(), http.StatusInternalServerError) - } else { - http.Error(w, err.Error(), http.StatusUnauthorized) - } - return - } - - onDone, err := s.addActiveHTTPRequest(r, ci) + onDone, err := s.addActiveHTTPRequest(r) if err != nil { if ou, ok := err.(inUseOtherUserError); ok && localapi.InUseOtherUserIPNStream(w, r, ou.Unwrap()) { w.(http.Flusher).Flush() - s.blockWhileIdentityInUse(ctx, ci) return } http.Error(w, err.Error(), http.StatusUnauthorized) @@ -194,8 +179,7 @@ func (s *Server) serveHTTP(w http.ResponseWriter, r *http.Request) { if strings.HasPrefix(r.URL.Path, "/localapi/") { lah := localapi.NewHandler(lb, s.logf, s.backendLogID) - lah.PermitRead, lah.PermitWrite = ci.Permissions(lb.OperatorUserID()) - lah.Actor = ci + lah.PermitRead, lah.PermitWrite = true, true lah.ServeHTTP(w, r) return } @@ -228,88 +212,11 @@ func (e inUseOtherUserError) Unwrap() error { return e.error } // The returned error, when non-nil, will be of type inUseOtherUserError. // // s.mu must be held. -func (s *Server) checkConnIdentityLocked(ci *actor) error { - // If clients are already connected, verify they're the same user. - // This mostly matters on Windows at the moment. - if len(s.activeReqs) > 0 { - var active *actor - for _, active = range s.activeReqs { - break - } - if active != nil { - // Always allow Windows SYSTEM user to connect, - // even if Tailscale is currently being used by another user. - if ci.IsLocalSystem() { - return nil - } +func (s *Server) checkConnIdentityLocked() error { - if ci.UserID() != active.UserID() { - var b strings.Builder - b.WriteString("Tailscale already in use") - if username, err := active.Username(); err == nil { - fmt.Fprintf(&b, " by %s", username) - } - fmt.Fprintf(&b, ", pid %d", active.pid()) - return inUseOtherUserError{errors.New(b.String())} - } - } - } - if err := s.mustBackend().CheckIPNConnectionAllowed(ci); err != nil { - return inUseOtherUserError{err} - } return nil } -// blockWhileIdentityInUse blocks while ci can't connect to the server because -// the server is in use by a different user. -// -// This is primarily used for the Windows GUI, to block until one user's done -// controlling the tailscaled process. -func (s *Server) blockWhileIdentityInUse(ctx context.Context, actor *actor) error { - inUse := func() bool { - s.mu.Lock() - defer s.mu.Unlock() - _, ok := s.checkConnIdentityLocked(actor).(inUseOtherUserError) - return ok - } - for inUse() { - // Check whenever the connection count drops down to zero. - ready, cleanup := s.zeroReqWaiter.add(&s.mu, ctx) - <-ready - cleanup() - if err := ctx.Err(); err != nil { - return err - } - } - return nil -} - -// Permissions returns the actor's permissions for accessing -// the Tailscale local daemon API. The operatorUID is only used on -// Unix-like platforms and specifies the ID of a local user -// (in the os/user.User.Uid string form) who is allowed -// to operate tailscaled without being root or using sudo. -func (a *actor) Permissions(operatorUID string) (read, write bool) { - switch envknob.GOOS() { - case "windows": - // As of 2024-08-27, according to the current permission model, - // Windows users always have read/write access to the local API if - // they're allowed to connect. Whether a user is allowed to connect - // is determined by [Server.checkConnIdentityLocked] when adding a - // new connection in [Server.addActiveHTTPRequest]. Therefore, it's - // acceptable to permit read and write access without any additional - // checks here. Note that this permission model is being changed in - // tailscale/corp#18342. - return true, true - case "js": - return true, true - } - if a.ci.IsUnixSock() { - return true, !a.ci.IsReadonlyConn(operatorUID, logger.Discard) - } - return false, false -} - // userIDFromString maps from either a numeric user id in string form // ("998") or username ("caddy") to its string userid ("998"). // It returns the empty string on error. @@ -339,10 +246,7 @@ func isAllDigit(s string) bool { // The returned error may be of type [inUseOtherUserError]. // // onDone must be called when the HTTP request is done. -func (s *Server) addActiveHTTPRequest(req *http.Request, actor *actor) (onDone func(), err error) { - if actor == nil { - return nil, errors.New("internal error: nil actor") - } +func (s *Server) addActiveHTTPRequest(req *http.Request) (onDone func(), err error) { lb := s.mustBackend() @@ -359,51 +263,12 @@ func (s *Server) addActiveHTTPRequest(req *http.Request, actor *actor) (onDone f s.mu.Lock() defer s.mu.Unlock() - if err := s.checkConnIdentityLocked(actor); err != nil { + if err := s.checkConnIdentityLocked(); err != nil { return nil, err } - mak.Set(&s.activeReqs, req, actor) - - if len(s.activeReqs) == 1 { - if envknob.GOOS() == "windows" && !actor.IsLocalSystem() { - // Tell the LocalBackend about the identity we're now running as, - // unless its the SYSTEM user. That user is not a real account and - // doesn't have a home directory. - uid, err := lb.SetCurrentUser(actor) - if err != nil { - return nil, err - } - if s.lastUserID != uid { - if s.lastUserID != "" { - doReset = true - } - s.lastUserID = uid - } - } - } - onDone = func() { - s.mu.Lock() - delete(s.activeReqs, req) - remain := len(s.activeReqs) - s.mu.Unlock() - if remain == 0 && s.resetOnZero { - if lb.InServerMode() { - s.logf("client disconnected; staying alive in server mode") - } else { - s.logf("client disconnected; stopping server") - lb.ResetForClientDisconnect() - } - } - - // Wake up callers waiting for the server to be idle: - if remain == 0 { - s.mu.Lock() - s.zeroReqWaiter.wakeAll() - s.mu.Unlock() - } } return onDone, nil @@ -476,10 +341,7 @@ func (s *Server) Run(ctx context.Context, ln net.Listener) error { hs := &http.Server{ Handler: http.HandlerFunc(s.serveHTTP), BaseContext: func(_ net.Listener) context.Context { return ctx }, - ConnContext: func(ctx context.Context, c net.Conn) context.Context { - return contextWithActor(ctx, s.logf, c) - }, - ErrorLog: logger.StdLogger(logger.WithPrefix(s.logf, "ipnserver: ")), + ErrorLog: logger.StdLogger(logger.WithPrefix(s.logf, "ipnserver: ")), } if err := hs.Serve(ln); err != nil { if err := ctx.Err(); err != nil { diff --git a/ipn/localapi/localapi.go b/ipn/localapi/localapi.go index 48d40fc78..047d06993 100644 --- a/ipn/localapi/localapi.go +++ b/ipn/localapi/localapi.go @@ -22,7 +22,6 @@ import ( "tailscale.com/client/tailscale/apitype" "tailscale.com/ipn" - "tailscale.com/ipn/ipnauth" "tailscale.com/ipn/ipnlocal" "tailscale.com/ipn/ipnstate" "tailscale.com/tailcfg" @@ -94,9 +93,6 @@ type Handler struct { // (operator user). PermitWrite bool - // Actor is the identity of the client connected to the Handler. - Actor ipnauth.Actor - b *ipnlocal.LocalBackend logf logger.Logf backendLogID logid.PublicID @@ -361,9 +357,6 @@ func authorizeServeConfigForGOOSAndUserContext(goos string, configIn *ipn.ServeC if !configIn.HasPathHandler() { return nil } - if h.Actor.IsLocalAdmin(h.b.OperatorUserID()) { - return nil - } switch goos { case "windows": return errors.New("must be a Windows local admin to serve a path") @@ -452,7 +445,7 @@ func (h *Handler) serveWatchIPNBus(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") ctx := r.Context() enc := json.NewEncoder(w) - h.b.WatchNotificationsAs(ctx, h.Actor, mask, f.Flush, func(roNotify *ipn.Notify) (keepGoing bool) { + h.b.WatchNotificationsAs(ctx, nil, mask, f.Flush, func(roNotify *ipn.Notify) (keepGoing bool) { err := enc.Encode(roNotify) if err != nil { h.logf("json.Encode: %v", err) @@ -472,7 +465,7 @@ func (h *Handler) serveLoginInteractive(w http.ResponseWriter, r *http.Request) http.Error(w, "want POST", http.StatusBadRequest) return } - h.b.StartLoginInteractiveAs(r.Context(), h.Actor) + h.b.StartLoginInteractiveAs(r.Context(), nil) w.WriteHeader(http.StatusNoContent) return }