mirror of
https://github.com/tailscale/tailscale.git
synced 2025-11-16 02:44:28 +00:00
This is step 1 of ~3, breaking up #14720 into reviewable chunks, with the aim to make syspolicy be a build-time configurable feature. In this first (very noisy) step, all the syspolicy string key constants move to a new constant-only (code-free) package. This will make future steps more reviewable, without this movement noise. There are no code or behavior changes here. The future steps of this series can be seen in #14720: removing global funcs from syspolicy resolution and using an interface that's plumbed around instead. Then adding build tags. Updates #12614 Change-Id: If73bf2c28b9c9b1a408fe868b0b6a25b03eeabd1 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
76 lines
2.7 KiB
Go
76 lines
2.7 KiB
Go
// 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"
|
|
"tailscale.com/util/syspolicy/pkey"
|
|
)
|
|
|
|
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(pkey.AlwaysOn, false); !alwaysOn {
|
|
return nil
|
|
}
|
|
if allowWithReason, _ := syspolicy.GetBoolean(pkey.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
|
|
}
|