Update dependencies
This commit is contained in:
17
vendor/tailscale.com/ipn/ipnauth/access.go
generated
vendored
Normal file
17
vendor/tailscale.com/ipn/ipnauth/access.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package ipnauth
|
||||
|
||||
// ProfileAccess is a bitmask representing the requested, required, or granted
|
||||
// access rights to an [ipn.LoginProfile].
|
||||
//
|
||||
// It is not to be written to disk or transmitted over the network in its integer form,
|
||||
// but rather serialized to a string or other format if ever needed.
|
||||
type ProfileAccess uint
|
||||
|
||||
// Define access rights that might be granted or denied on a per-profile basis.
|
||||
const (
|
||||
// Disconnect is required to disconnect (or switch from) a Tailscale profile.
|
||||
Disconnect = ProfileAccess(1 << iota)
|
||||
)
|
||||
87
vendor/tailscale.com/ipn/ipnauth/actor.go
generated
vendored
87
vendor/tailscale.com/ipn/ipnauth/actor.go
generated
vendored
@@ -4,9 +4,18 @@
|
||||
package ipnauth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"tailscale.com/client/tailscale/apitype"
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/tailcfg"
|
||||
)
|
||||
|
||||
// AuditLogFunc is any function that can be used to log audit actions performed by an [Actor].
|
||||
type AuditLogFunc func(action tailcfg.ClientAuditAction, details string) error
|
||||
|
||||
// Actor is any actor using the [ipnlocal.LocalBackend].
|
||||
//
|
||||
// It typically represents a specific OS user, indicating that an operation
|
||||
@@ -20,6 +29,22 @@ type Actor interface {
|
||||
// Username returns the user name associated with the receiver,
|
||||
// or "" if the actor does not represent a specific user.
|
||||
Username() (string, error)
|
||||
// ClientID returns a non-zero ClientID and true if the actor represents
|
||||
// a connected LocalAPI client. Otherwise, it returns a zero value and false.
|
||||
ClientID() (_ ClientID, ok bool)
|
||||
|
||||
// Context returns the context associated with the actor.
|
||||
// It carries additional information about the actor
|
||||
// and is canceled when the actor is done.
|
||||
Context() context.Context
|
||||
|
||||
// CheckProfileAccess checks whether the actor has the necessary access rights
|
||||
// to perform a given action on the specified Tailscale profile.
|
||||
// It returns an error if access is denied.
|
||||
//
|
||||
// If the auditLogger is non-nil, it is used to write details about the action
|
||||
// to the audit log when required by the policy.
|
||||
CheckProfileAccess(profile ipn.LoginProfileView, requestedAccess ProfileAccess, auditLogFn AuditLogFunc) error
|
||||
|
||||
// IsLocalSystem reports whether the actor is the Windows' Local System account.
|
||||
//
|
||||
@@ -45,3 +70,65 @@ type ActorCloser interface {
|
||||
// Close releases resources associated with the receiver.
|
||||
Close() error
|
||||
}
|
||||
|
||||
// ClientID is an opaque, comparable value used to identify a connected LocalAPI
|
||||
// client, such as a connected Tailscale GUI or CLI. It does not necessarily
|
||||
// correspond to the same [net.Conn] or any physical session.
|
||||
//
|
||||
// Its zero value is valid, but does not represent a specific connected client.
|
||||
type ClientID struct {
|
||||
v any
|
||||
}
|
||||
|
||||
// NoClientID is the zero value of [ClientID].
|
||||
var NoClientID ClientID
|
||||
|
||||
// ClientIDFrom returns a new [ClientID] derived from the specified value.
|
||||
// ClientIDs derived from equal values are equal.
|
||||
func ClientIDFrom[T comparable](v T) ClientID {
|
||||
return ClientID{v}
|
||||
}
|
||||
|
||||
// String implements [fmt.Stringer].
|
||||
func (id ClientID) String() string {
|
||||
if id.v == nil {
|
||||
return "(none)"
|
||||
}
|
||||
return fmt.Sprint(id.v)
|
||||
}
|
||||
|
||||
// MarshalJSON implements [json.Marshaler].
|
||||
// It is primarily used for testing.
|
||||
func (id ClientID) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(id.v)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements [json.Unmarshaler].
|
||||
// It is primarily used for testing.
|
||||
func (id *ClientID) UnmarshalJSON(b []byte) error {
|
||||
return json.Unmarshal(b, &id.v)
|
||||
}
|
||||
|
||||
type actorWithRequestReason struct {
|
||||
Actor
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// WithRequestReason returns an [Actor] that wraps the given actor and
|
||||
// carries the specified request reason in its context.
|
||||
func WithRequestReason(actor Actor, requestReason string) Actor {
|
||||
ctx := apitype.RequestReasonKey.WithValue(actor.Context(), requestReason)
|
||||
return &actorWithRequestReason{Actor: actor, ctx: ctx}
|
||||
}
|
||||
|
||||
// Context implements [Actor].
|
||||
func (a *actorWithRequestReason) Context() context.Context { return a.ctx }
|
||||
|
||||
type withoutCloseActor struct{ Actor }
|
||||
|
||||
// WithoutClose returns an [Actor] that does not expose the [ActorCloser] interface.
|
||||
// In other words, _, ok := WithoutClose(actor).(ActorCloser) will always be false,
|
||||
// even if the original actor implements [ActorCloser].
|
||||
func WithoutClose(actor Actor) Actor {
|
||||
return withoutCloseActor{actor}
|
||||
}
|
||||
|
||||
102
vendor/tailscale.com/ipn/ipnauth/actor_windows.go
generated
vendored
Normal file
102
vendor/tailscale.com/ipn/ipnauth/actor_windows.go
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package ipnauth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/types/lazy"
|
||||
)
|
||||
|
||||
// WindowsActor implements [Actor].
|
||||
var _ Actor = (*WindowsActor)(nil)
|
||||
|
||||
// WindowsActor represents a logged in Windows user.
|
||||
type WindowsActor struct {
|
||||
ctx context.Context
|
||||
cancelCtx context.CancelFunc
|
||||
token WindowsToken
|
||||
uid ipn.WindowsUserID
|
||||
username lazy.SyncValue[string]
|
||||
}
|
||||
|
||||
// NewWindowsActorWithToken returns a new [WindowsActor] for the user
|
||||
// represented by the given [windows.Token].
|
||||
// It takes ownership of the token.
|
||||
func NewWindowsActorWithToken(t windows.Token) (_ *WindowsActor, err error) {
|
||||
tok := newToken(t)
|
||||
uid, err := tok.UID()
|
||||
if err != nil {
|
||||
t.Close()
|
||||
return nil, err
|
||||
}
|
||||
ctx, cancelCtx := context.WithCancel(context.Background())
|
||||
return &WindowsActor{ctx: ctx, cancelCtx: cancelCtx, token: tok, uid: uid}, nil
|
||||
}
|
||||
|
||||
// UserID implements [Actor].
|
||||
func (a *WindowsActor) UserID() ipn.WindowsUserID {
|
||||
return a.uid
|
||||
}
|
||||
|
||||
// Username implements [Actor].
|
||||
func (a *WindowsActor) Username() (string, error) {
|
||||
return a.username.GetErr(a.token.Username)
|
||||
}
|
||||
|
||||
// ClientID implements [Actor].
|
||||
func (a *WindowsActor) ClientID() (_ ClientID, ok bool) {
|
||||
// TODO(nickkhyl): assign and return a client ID when the actor
|
||||
// represents a connected LocalAPI client.
|
||||
return NoClientID, false
|
||||
}
|
||||
|
||||
// Context implements [Actor].
|
||||
func (a *WindowsActor) Context() context.Context {
|
||||
return a.ctx
|
||||
}
|
||||
|
||||
// CheckProfileAccess implements [Actor].
|
||||
func (a *WindowsActor) CheckProfileAccess(profile ipn.LoginProfileView, _ ProfileAccess, _ AuditLogFunc) error {
|
||||
if profile.LocalUserID() != a.UserID() {
|
||||
// TODO(nickkhyl): return errors of more specific types and have them
|
||||
// translated to the appropriate HTTP status codes in the API handler.
|
||||
return errors.New("the target profile does not belong to the user")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsLocalSystem implements [Actor].
|
||||
//
|
||||
// Deprecated: this method exists for compatibility with the current (as of 2025-02-06)
|
||||
// permission model and will be removed as we progress on tailscale/corp#18342.
|
||||
func (a *WindowsActor) IsLocalSystem() bool {
|
||||
// https://web.archive.org/web/2024/https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-security-identifiers
|
||||
const systemUID = ipn.WindowsUserID("S-1-5-18")
|
||||
return a.uid == systemUID
|
||||
}
|
||||
|
||||
// IsLocalAdmin implements [Actor].
|
||||
//
|
||||
// Deprecated: this method exists for compatibility with the current (as of 2025-02-06)
|
||||
// permission model and will be removed as we progress on tailscale/corp#18342.
|
||||
func (a *WindowsActor) IsLocalAdmin(operatorUID string) bool {
|
||||
return a.token.IsElevated()
|
||||
}
|
||||
|
||||
// Close releases resources associated with the actor
|
||||
// and cancels its context.
|
||||
func (a *WindowsActor) Close() error {
|
||||
if a.token != nil {
|
||||
if err := a.token.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
a.token = nil
|
||||
}
|
||||
a.cancelCtx()
|
||||
return nil
|
||||
}
|
||||
4
vendor/tailscale.com/ipn/ipnauth/ipnauth_notwindows.go
generated
vendored
4
vendor/tailscale.com/ipn/ipnauth/ipnauth_notwindows.go
generated
vendored
@@ -18,7 +18,9 @@ import (
|
||||
func GetConnIdentity(_ logger.Logf, c net.Conn) (ci *ConnIdentity, err error) {
|
||||
ci = &ConnIdentity{conn: c, notWindows: true}
|
||||
_, ci.isUnixSock = c.(*net.UnixConn)
|
||||
ci.creds, _ = peercred.Get(c)
|
||||
if ci.creds, _ = peercred.Get(c); ci.creds != nil {
|
||||
ci.pid, _ = ci.creds.PID()
|
||||
}
|
||||
return ci, nil
|
||||
}
|
||||
|
||||
|
||||
10
vendor/tailscale.com/ipn/ipnauth/ipnauth_windows.go
generated
vendored
10
vendor/tailscale.com/ipn/ipnauth/ipnauth_windows.go
generated
vendored
@@ -36,6 +36,12 @@ type token struct {
|
||||
t windows.Token
|
||||
}
|
||||
|
||||
func newToken(t windows.Token) *token {
|
||||
tok := &token{t: t}
|
||||
runtime.SetFinalizer(tok, func(t *token) { t.Close() })
|
||||
return tok
|
||||
}
|
||||
|
||||
func (t *token) UID() (ipn.WindowsUserID, error) {
|
||||
sid, err := t.uid()
|
||||
if err != nil {
|
||||
@@ -184,7 +190,5 @@ func (ci *ConnIdentity) WindowsToken() (WindowsToken, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := &token{t: windows.Token(h)}
|
||||
runtime.SetFinalizer(result, func(t *token) { t.Close() })
|
||||
return result, nil
|
||||
return newToken(windows.Token(h)), nil
|
||||
}
|
||||
|
||||
74
vendor/tailscale.com/ipn/ipnauth/policy.go
generated
vendored
Normal file
74
vendor/tailscale.com/ipn/ipnauth/policy.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package ipnauth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"tailscale.com/client/tailscale/apitype"
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/util/syspolicy"
|
||||
)
|
||||
|
||||
type actorWithPolicyChecks struct{ Actor }
|
||||
|
||||
// WithPolicyChecks returns an [Actor] that wraps the given actor and
|
||||
// performs additional policy checks on top of the access checks
|
||||
// implemented by the wrapped actor.
|
||||
func WithPolicyChecks(actor Actor) Actor {
|
||||
// TODO(nickkhyl): We should probably exclude the Windows Local System
|
||||
// account from policy checks as well.
|
||||
switch actor.(type) {
|
||||
case unrestricted:
|
||||
return actor
|
||||
default:
|
||||
return &actorWithPolicyChecks{Actor: actor}
|
||||
}
|
||||
}
|
||||
|
||||
// CheckProfileAccess implements [Actor].
|
||||
func (a actorWithPolicyChecks) CheckProfileAccess(profile ipn.LoginProfileView, requestedAccess ProfileAccess, auditLogger AuditLogFunc) error {
|
||||
if err := a.Actor.CheckProfileAccess(profile, requestedAccess, auditLogger); err != nil {
|
||||
return err
|
||||
}
|
||||
requestReason := apitype.RequestReasonKey.Value(a.Context())
|
||||
return CheckDisconnectPolicy(a.Actor, profile, requestReason, auditLogger)
|
||||
}
|
||||
|
||||
// CheckDisconnectPolicy checks if the policy allows the specified actor to disconnect
|
||||
// Tailscale with the given optional reason. It returns nil if the operation is allowed,
|
||||
// or an error if it is not. If auditLogger is non-nil, it is called to log the action
|
||||
// when required by the policy.
|
||||
//
|
||||
// Note: this function only checks the policy and does not check whether the actor has
|
||||
// the necessary access rights to the device or profile. It is intended to be used by
|
||||
// [Actor] implementations on platforms where [syspolicy] is supported.
|
||||
//
|
||||
// TODO(nickkhyl): unexport it when we move [ipn.Actor] implementations from [ipnserver]
|
||||
// and corp to this package.
|
||||
func CheckDisconnectPolicy(actor Actor, profile ipn.LoginProfileView, reason string, auditFn AuditLogFunc) error {
|
||||
if alwaysOn, _ := syspolicy.GetBoolean(syspolicy.AlwaysOn, false); !alwaysOn {
|
||||
return nil
|
||||
}
|
||||
if allowWithReason, _ := syspolicy.GetBoolean(syspolicy.AlwaysOnOverrideWithReason, false); !allowWithReason {
|
||||
return errors.New("disconnect not allowed: always-on mode is enabled")
|
||||
}
|
||||
if reason == "" {
|
||||
return errors.New("disconnect not allowed: reason required")
|
||||
}
|
||||
if auditFn != nil {
|
||||
var details string
|
||||
if username, _ := actor.Username(); username != "" { // best-effort; we don't have it on all platforms
|
||||
details = fmt.Sprintf("%q is being disconnected by %q: %v", profile.Name(), username, reason)
|
||||
} else {
|
||||
details = fmt.Sprintf("%q is being disconnected: %v", profile.Name(), reason)
|
||||
}
|
||||
if err := auditFn(tailcfg.AuditNodeDisconnect, details); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
51
vendor/tailscale.com/ipn/ipnauth/self.go
generated
vendored
Normal file
51
vendor/tailscale.com/ipn/ipnauth/self.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package ipnauth
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"tailscale.com/ipn"
|
||||
)
|
||||
|
||||
// Self is a caller identity that represents the tailscaled itself and therefore
|
||||
// has unlimited access.
|
||||
var Self Actor = unrestricted{}
|
||||
|
||||
// unrestricted is an [Actor] that has unlimited access to the currently running
|
||||
// tailscaled instance. It's typically used for operations performed by tailscaled
|
||||
// on its own, or upon a request from the control plane, rather on behalf of a user.
|
||||
type unrestricted struct{}
|
||||
|
||||
// UserID implements [Actor].
|
||||
func (unrestricted) UserID() ipn.WindowsUserID { return "" }
|
||||
|
||||
// Username implements [Actor].
|
||||
func (unrestricted) Username() (string, error) { return "", nil }
|
||||
|
||||
// Context implements [Actor].
|
||||
func (unrestricted) Context() context.Context { return context.Background() }
|
||||
|
||||
// ClientID implements [Actor].
|
||||
// It always returns (NoClientID, false) because the tailscaled itself
|
||||
// is not a connected LocalAPI client.
|
||||
func (unrestricted) ClientID() (_ ClientID, ok bool) { return NoClientID, false }
|
||||
|
||||
// CheckProfileAccess implements [Actor].
|
||||
func (unrestricted) CheckProfileAccess(_ ipn.LoginProfileView, _ ProfileAccess, _ AuditLogFunc) error {
|
||||
// Unrestricted access to all profiles.
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsLocalSystem implements [Actor].
|
||||
//
|
||||
// Deprecated: this method exists for compatibility with the current (as of 2025-01-28)
|
||||
// permission model and will be removed as we progress on tailscale/corp#18342.
|
||||
func (unrestricted) IsLocalSystem() bool { return false }
|
||||
|
||||
// IsLocalAdmin implements [Actor].
|
||||
//
|
||||
// Deprecated: this method exists for compatibility with the current (as of 2025-01-28)
|
||||
// permission model and will be removed as we progress on tailscale/corp#18342.
|
||||
func (unrestricted) IsLocalAdmin(operatorUID string) bool { return false }
|
||||
48
vendor/tailscale.com/ipn/ipnauth/test_actor.go
generated
vendored
Normal file
48
vendor/tailscale.com/ipn/ipnauth/test_actor.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package ipnauth
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"tailscale.com/ipn"
|
||||
)
|
||||
|
||||
var _ Actor = (*TestActor)(nil)
|
||||
|
||||
// TestActor is an [Actor] used exclusively for testing purposes.
|
||||
type TestActor struct {
|
||||
UID ipn.WindowsUserID // OS-specific UID of the user, if the actor represents a local Windows user
|
||||
Name string // username associated with the actor, or ""
|
||||
NameErr error // error to be returned by [TestActor.Username]
|
||||
CID ClientID // non-zero if the actor represents a connected LocalAPI client
|
||||
Ctx context.Context // context associated with the actor
|
||||
LocalSystem bool // whether the actor represents the special Local System account on Windows
|
||||
LocalAdmin bool // whether the actor has local admin access
|
||||
}
|
||||
|
||||
// UserID implements [Actor].
|
||||
func (a *TestActor) UserID() ipn.WindowsUserID { return a.UID }
|
||||
|
||||
// Username implements [Actor].
|
||||
func (a *TestActor) Username() (string, error) { return a.Name, a.NameErr }
|
||||
|
||||
// ClientID implements [Actor].
|
||||
func (a *TestActor) ClientID() (_ ClientID, ok bool) { return a.CID, a.CID != NoClientID }
|
||||
|
||||
// Context implements [Actor].
|
||||
func (a *TestActor) Context() context.Context { return cmp.Or(a.Ctx, context.Background()) }
|
||||
|
||||
// CheckProfileAccess implements [Actor].
|
||||
func (a *TestActor) CheckProfileAccess(profile ipn.LoginProfileView, _ ProfileAccess, _ AuditLogFunc) error {
|
||||
return errors.New("profile access denied")
|
||||
}
|
||||
|
||||
// IsLocalSystem implements [Actor].
|
||||
func (a *TestActor) IsLocalSystem() bool { return a.LocalSystem }
|
||||
|
||||
// IsLocalAdmin implements [Actor].
|
||||
func (a *TestActor) IsLocalAdmin(operatorUID string) bool { return a.LocalAdmin }
|
||||
Reference in New Issue
Block a user