mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-12 13:48:01 +00:00
ipn/{ipnauth,ipnlocal,ipnserver,localapi}: start baby step toward moving access checks from the localapi.Handler to the LocalBackend
Currently, we use PermitRead/PermitWrite/PermitCert permission flags to determine which operations are allowed for a LocalAPI client. These checks are performed when localapi.Handler handles a request. Additionally, certain operations (e.g., changing the serve config) requires the connected user to be a local admin. This approach is inherently racey and is subject to TOCTOU issues. We consider it to be more critical on Windows environments, which are inherently multi-user, and therefore we prevent more than one OS user from connecting and utilizing the LocalBackend at the same time. However, the same type of issues is also applicable to other platforms when switching between profiles that have different OperatorUser values in ipn.Prefs. We'd like to allow more than one Windows user to connect, but limit what they can see and do based on their access rights on the device (e.g., an local admin or not) and to the currently active LoginProfile (e.g., owner/operator or not), while preventing TOCTOU issues on Windows and other platforms. Therefore, we'd like to pass an actor from the LocalAPI to the LocalBackend to represent the user performing the operation. The LocalBackend, or the profileManager down the line, will then check the actor's access rights to perform a given operation on the device and against the current (and/or the target) profile. This PR does not change the current permission model in any way, but it introduces the concept of an actor and includes some preparatory work to pass it around. Temporarily, the ipnauth.Actor interface has methods like IsLocalSystem and IsLocalAdmin, which are only relevant to the current permission model. It also lacks methods that will actually be used in the new model. We'll be adding these gradually in the next PRs and removing the deprecated methods and the Permit* flags at the end of the transition. Updates tailscale/corp#18342 Signed-off-by: Nick Khyl <nickk@tailscale.com>
This commit is contained in:
@@ -291,7 +291,7 @@ type LocalBackend struct {
|
||||
componentLogUntil map[string]componentLogState
|
||||
// c2nUpdateStatus is the status of c2n-triggered client update.
|
||||
c2nUpdateStatus updateStatus
|
||||
currentUser ipnauth.WindowsToken
|
||||
currentUser ipnauth.Actor
|
||||
selfUpdateProgress []ipnstate.UpdateProgress
|
||||
lastSelfUpdateState ipnstate.SelfUpdateStatus
|
||||
// capForcedNetfilter is the netfilter that control instructs Linux clients
|
||||
@@ -3101,13 +3101,13 @@ func (b *LocalBackend) InServerMode() bool {
|
||||
return b.pm.CurrentPrefs().ForceDaemon()
|
||||
}
|
||||
|
||||
// CheckIPNConnectionAllowed returns an error if the identity in ci should not
|
||||
// CheckIPNConnectionAllowed returns an error if the specified actor should not
|
||||
// be allowed to connect or make requests to the LocalAPI currently.
|
||||
//
|
||||
// Currently (as of 2022-11-23), this is only used on Windows to check if
|
||||
// we started in server mode and ci is from an identity other than the one
|
||||
// that started the server.
|
||||
func (b *LocalBackend) CheckIPNConnectionAllowed(ci *ipnauth.ConnIdentity) error {
|
||||
// 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()
|
||||
@@ -3122,14 +3122,11 @@ func (b *LocalBackend) CheckIPNConnectionAllowed(ci *ipnauth.ConnIdentity) error
|
||||
|
||||
// Always allow Windows SYSTEM user to connect,
|
||||
// even if Tailscale is currently being used by another user.
|
||||
if tok, err := ci.WindowsToken(); err == nil {
|
||||
defer tok.Close()
|
||||
if tok.IsLocalSystem() {
|
||||
return nil
|
||||
}
|
||||
if actor.IsLocalSystem() {
|
||||
return nil
|
||||
}
|
||||
|
||||
uid := ci.WindowsUserID()
|
||||
uid := actor.UserID()
|
||||
if uid == "" {
|
||||
return errors.New("empty user uid in connection identity")
|
||||
}
|
||||
@@ -3308,18 +3305,14 @@ func (b *LocalBackend) shouldUploadServices() bool {
|
||||
// unattended mode. The user must disable unattended mode before the user can be
|
||||
// changed.
|
||||
//
|
||||
// On non-multi-user systems, the token should be set to nil.
|
||||
// On non-multi-user systems, the user should be set to nil.
|
||||
//
|
||||
// SetCurrentUser returns the ipn.WindowsUserID associated with token
|
||||
// SetCurrentUser returns the ipn.WindowsUserID associated with the user
|
||||
// when successful.
|
||||
func (b *LocalBackend) SetCurrentUser(token ipnauth.WindowsToken) (ipn.WindowsUserID, error) {
|
||||
func (b *LocalBackend) SetCurrentUser(actor ipnauth.Actor) (ipn.WindowsUserID, error) {
|
||||
var uid ipn.WindowsUserID
|
||||
if token != nil {
|
||||
var err error
|
||||
uid, err = token.UID()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if actor != nil {
|
||||
uid = actor.UserID()
|
||||
}
|
||||
|
||||
unlock := b.lockAndGetUnlock()
|
||||
@@ -3331,10 +3324,10 @@ func (b *LocalBackend) SetCurrentUser(token ipnauth.WindowsToken) (ipn.WindowsUs
|
||||
if err := b.pm.SetCurrentUserID(uid); err != nil {
|
||||
return uid, nil
|
||||
}
|
||||
if b.currentUser != nil {
|
||||
b.currentUser.Close()
|
||||
if c, ok := b.currentUser.(ipnauth.ActorCloser); ok {
|
||||
c.Close()
|
||||
}
|
||||
b.currentUser = token
|
||||
b.currentUser = actor
|
||||
b.resetForProfileChangeLockedOnEntry(unlock)
|
||||
return uid, nil
|
||||
}
|
||||
@@ -5037,7 +5030,9 @@ func (b *LocalBackend) ResetForClientDisconnect() {
|
||||
b.setNetMapLocked(nil)
|
||||
b.pm.Reset()
|
||||
if b.currentUser != nil {
|
||||
b.currentUser.Close()
|
||||
if c, ok := b.currentUser.(ipnauth.ActorCloser); ok {
|
||||
c.Close()
|
||||
}
|
||||
b.currentUser = nil
|
||||
}
|
||||
b.keyExpired = false
|
||||
|
Reference in New Issue
Block a user