@ -22,6 +22,7 @@ import (
"tailscale.com/envknob"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnauth"
"tailscale.com/ipn/ipnlocal"
"tailscale.com/ipn/localapi"
"tailscale.com/net/netmon"
@ -30,6 +31,7 @@ import (
"tailscale.com/util/mak"
"tailscale.com/util/set"
"tailscale.com/util/systemd"
"tailscale.com/util/testenv"
)
// Server is an IPN backend and its set of 0 or more active localhost
@ -50,7 +52,7 @@ 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 ] * a ctor
activeReqs map [ * http . Request ] ipnauth . A ctor
backendWaiter waiterSet // of LocalBackend waiters
zeroReqWaiter waiterSet // of blockUntilZeroConnections waiters
}
@ -195,8 +197,12 @@ 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 . PermitCert = ci . CanFetchCerts ( )
if actor , ok := ci . ( * actor ) ; ok {
lah . PermitRead , lah . PermitWrite = actor . Permissions ( lb . OperatorUserID ( ) )
lah . PermitCert = actor . CanFetchCerts ( )
} else if testenv . InTest ( ) {
lah . PermitRead , lah . PermitWrite = true , true
}
lah . Actor = ci
lah . ServeHTTP ( w , r )
return
@ -230,11 +236,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 * a ctor) error {
func ( s * Server ) checkConnIdentityLocked ( ci ipnauth . A ctor) 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 * a ctor
var active ipnauth . A ctor
for _ , active = range s . activeReqs {
break
}
@ -251,7 +257,9 @@ func (s *Server) checkConnIdentityLocked(ci *actor) error {
if username , err := active . Username ( ) ; err == nil {
fmt . Fprintf ( & b , " by %s" , username )
}
fmt . Fprintf ( & b , ", pid %d" , active . pid ( ) )
if active , ok := active . ( * actor ) ; ok {
fmt . Fprintf ( & b , ", pid %d" , active . pid ( ) )
}
return inUseOtherUserError { errors . New ( b . String ( ) ) }
}
}
@ -267,7 +275,7 @@ func (s *Server) checkConnIdentityLocked(ci *actor) error {
//
// 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 * a ctor) error {
func ( s * Server ) blockWhileIdentityInUse ( ctx context . Context , actor ipnauth . A ctor) error {
inUse := func ( ) bool {
s . mu . Lock ( )
defer s . mu . Unlock ( )
@ -361,7 +369,7 @@ func (a *actor) CanFetchCerts() 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 * a ctor) ( onDone func ( ) , err error ) {
func ( s * Server ) addActiveHTTPRequest ( req * http . Request , actor ipnauth . A ctor) ( onDone func ( ) , err error ) {
if actor == nil {
return nil , errors . New ( "internal error: nil actor" )
}