Files
tailscale/ipn/localapi/syspolicy_api.go
T
kari-ts a89b32904f ipn/localapi: use caller scope for syspolicy requests (#21701)
This resolves unscoped policy requests to the connected user's effective
policy instead of the default scope.

Tested by changing the device-scoped AlwaysOn policy at runtime and
running tailscale syspolicy reload. Cofnirmed tailscaled immediately
enforced the updated policy with tailscale down

Updates tailscale/corp#41463

Signed-off-by: kari <kari@tailscale.com>
2026-10-08 08:35:14 -07:00

74 lines
1.6 KiB
Go

// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
//go:build !ts_omit_syspolicy
package localapi
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"tailscale.com/util/httpm"
"tailscale.com/util/syspolicy/rsop"
"tailscale.com/util/syspolicy/setting"
)
func init() {
Register("policy/", (*Handler).servePolicy)
}
func (h *Handler) servePolicy(w http.ResponseWriter, r *http.Request) {
if !h.PermitRead {
http.Error(w, "policy access denied", http.StatusForbidden)
return
}
suffix, ok := strings.CutPrefix(r.URL.EscapedPath(), "/localapi/v0/policy/")
if !ok {
http.Error(w, "misconfigured", http.StatusInternalServerError)
return
}
var scope setting.PolicyScope
if suffix == "" {
policyUID := string(h.Actor.UserID())
if policyUID == "" {
scope = setting.DefaultScope()
} else {
scope = setting.UserScopeOf(policyUID)
}
} else if err := scope.UnmarshalText([]byte(suffix)); err != nil {
http.Error(w, fmt.Sprintf("%q is not a valid scope", suffix), http.StatusBadRequest)
return
}
policy, err := rsop.PolicyFor(scope)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
var effectivePolicy *setting.Snapshot
switch r.Method {
case httpm.GET:
effectivePolicy = policy.Get()
case httpm.POST:
effectivePolicy, err = policy.Reload()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
default:
http.Error(w, "unsupported method", http.StatusMethodNotAllowed)
return
}
w.Header().Set("Content-Type", "application/json")
e := json.NewEncoder(w)
e.SetIndent("", "\t")
e.Encode(effectivePolicy)
}