mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-25 18:21:01 +00:00

We have a lot of access checks spread around the ipnserver, ipnlocal, localapi, and ipnauth packages, with a significant number of platform-specific checks that are used exclusively on either Windows or Unix-like platforms. Additionally, with the exception of a few Windows-specific checks, most of these checks are per-device rather than per-profile, which is not always correct even on single-user/single-session environments, but even more problematic on multi-user/multi-session environments such as Windows. We initially attempted to map all possible operations onto the permitRead/permitWrite access flags. However, these flags are not utilized on Windows and prove insufficient on Unix machines. Specifically, on Windows, the first user to connect is granted full access, while subsequent logged-in users have no access to the LocalAPI at all. This restriction applies regardless of the environment, local user roles (e.g., whether a Windows user is a local admin), or whether they are the active user on a shared Windows client device. Conversely, on Unix, we introduced the permitCert flag to enable granting non-root web servers (such as www-data, caddy, nginx, etc.) access to certificates. We also added additional access check to distinguish local admins (root on Unix-like platforms, elevated admins on Windows) from users with permitWrite access, and used it as a fix for the serve path LPE. A more fine-grained access control system could better suit our current and future needs, especially in improving the UX across various scenarios on corporate and personal Windows devices. This adds an API surface in ipnauth that will be used in LocalBackend to check access to individual Tailscale profiles as well as any device-wide information and operations. Updates tailscale/corp#18342 Signed-off-by: Nick Khyl <nickk@tailscale.com>
74 lines
2.8 KiB
Go
74 lines
2.8 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package ipnauth
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
|
|
"tailscale.com/ipn"
|
|
"tailscale.com/types/logger"
|
|
)
|
|
|
|
// Identity is any caller identity.
|
|
//
|
|
// It typically represents a specific OS user, indicating that an operation
|
|
// is performed on behalf of this user, should be evaluated against their
|
|
// access rights, and performed in their security context when applicable.
|
|
//
|
|
// However, it can also represent an unrestricted identity (e.g. ipnauth.Self) when an operation
|
|
// is executed on behalf of tailscaled itself, in response to a control plane request,
|
|
// or when a user's access rights have been verified via other means.
|
|
type Identity interface {
|
|
// UserID returns an OS-specific UID of the user represented by the identity,
|
|
// or "" if the receiver does not represent a specific user.
|
|
// As of 2024-04-08, it is only used on Windows.
|
|
UserID() ipn.WindowsUserID
|
|
// Username returns the user name associated with the receiver,
|
|
// or "" if the receiver does not represent a specific user.
|
|
Username() (string, error)
|
|
// CheckAccess reports whether the receiver is allowed or denied the requested device access.
|
|
//
|
|
// This method ignores environment factors, Group Policy, and MDM settings that might
|
|
// override access permissions at a higher level than individual user identities.
|
|
// Therefore, most callers should use ipnauth.CheckAccess instead.
|
|
CheckAccess(requested DeviceAccess) AccessCheckResult
|
|
// CheckProfileAccess reports whether the receiver is allowed or denied the requested access
|
|
// to a specific profile and its prefs.
|
|
//
|
|
// This method ignores environment factors, Group Policy, and MDM settings that might
|
|
// override access permissions at a higher level than individual user identities.
|
|
// Therefore, most callers should use ipnauth.CheckProfileAccess instead.
|
|
CheckProfileAccess(profile ipn.LoginProfileView, prefs ipn.PrefsGetter, requested ProfileAccess) AccessCheckResult
|
|
}
|
|
|
|
type identityContextKey struct{}
|
|
|
|
var errNoSecContext = ipn.NewAccessDeniedError("security context not available")
|
|
|
|
// RequestIdentity returns a user identity associated with ctx,
|
|
// or an error if the context does not carry a user's identity.
|
|
func RequestIdentity(ctx context.Context) (Identity, error) {
|
|
switch v := ctx.Value(identityContextKey{}).(type) {
|
|
case Identity:
|
|
return v, nil
|
|
case error:
|
|
return nil, v
|
|
case nil:
|
|
return nil, errNoSecContext
|
|
default:
|
|
panic("unreachable")
|
|
}
|
|
}
|
|
|
|
// ContextWithConnIdentity returns a new context that carries the identity of the user
|
|
// owning the other end of the connection.
|
|
func ContextWithConnIdentity(ctx context.Context, logf logger.Logf, c net.Conn) context.Context {
|
|
ci, err := GetConnIdentity(logf, c)
|
|
if err != nil {
|
|
return context.WithValue(ctx, identityContextKey{}, err)
|
|
}
|
|
return context.WithValue(ctx, identityContextKey{}, ci)
|
|
}
|