Browse Source

util/syspolicy: finish adding ts_omit_syspolicy build tags, tests

Fixes #16998
Updates #12614

Change-Id: Idf2b1657898111df4be31f356091b2376d0d7f0b
Signed-off-by: Brad Fitzpatrick <[email protected]>
Brad Fitzpatrick 6 months ago
parent
commit
21f21bd2a2

+ 1 - 1
build_dist.sh

@@ -41,7 +41,7 @@ while [ "$#" -gt 1 ]; do
 		fi
 		shift
 		ldflags="$ldflags -w -s"
-		tags="${tags:+$tags,}ts_omit_aws,ts_omit_bird,ts_omit_tap,ts_omit_kube,ts_omit_completion,ts_omit_ssh,ts_omit_wakeonlan,ts_omit_capture,ts_omit_relayserver,ts_omit_systray,ts_omit_taildrop,ts_omit_tpm"
+		tags="${tags:+$tags,}ts_omit_aws,ts_omit_bird,ts_omit_tap,ts_omit_kube,ts_omit_completion,ts_omit_ssh,ts_omit_wakeonlan,ts_omit_capture,ts_omit_relayserver,ts_omit_systray,ts_omit_taildrop,ts_omit_tpm,ts_omit_syspolicy"
 		;;
 	--box)
 		if [ ! -z "${TAGS:-}" ]; then

+ 0 - 28
client/local/local.go

@@ -43,7 +43,6 @@ import (
 	"tailscale.com/types/key"
 	"tailscale.com/types/tkatype"
 	"tailscale.com/util/eventbus"
-	"tailscale.com/util/syspolicy/setting"
 )
 
 // defaultClient is the default Client when using the legacy
@@ -926,33 +925,6 @@ func (lc *Client) EditPrefs(ctx context.Context, mp *ipn.MaskedPrefs) (*ipn.Pref
 	return decodeJSON[*ipn.Prefs](body)
 }
 
-// GetEffectivePolicy returns the effective policy for the specified scope.
-func (lc *Client) GetEffectivePolicy(ctx context.Context, scope setting.PolicyScope) (*setting.Snapshot, error) {
-	scopeID, err := scope.MarshalText()
-	if err != nil {
-		return nil, err
-	}
-	body, err := lc.get200(ctx, "/localapi/v0/policy/"+string(scopeID))
-	if err != nil {
-		return nil, err
-	}
-	return decodeJSON[*setting.Snapshot](body)
-}
-
-// ReloadEffectivePolicy reloads the effective policy for the specified scope
-// by reading and merging policy settings from all applicable policy sources.
-func (lc *Client) ReloadEffectivePolicy(ctx context.Context, scope setting.PolicyScope) (*setting.Snapshot, error) {
-	scopeID, err := scope.MarshalText()
-	if err != nil {
-		return nil, err
-	}
-	body, err := lc.send(ctx, "POST", "/localapi/v0/policy/"+string(scopeID), 200, http.NoBody)
-	if err != nil {
-		return nil, err
-	}
-	return decodeJSON[*setting.Snapshot](body)
-}
-
 // GetDNSOSConfig returns the system DNS configuration for the current device.
 // That is, it returns the DNS configuration that the system would use if Tailscale weren't being used.
 func (lc *Client) GetDNSOSConfig(ctx context.Context) (*apitype.DNSOSConfig, error) {

+ 40 - 0
client/local/syspolicy.go

@@ -0,0 +1,40 @@
+// Copyright (c) Tailscale Inc & AUTHORS
+// SPDX-License-Identifier: BSD-3-Clause
+
+//go:build !ts_omit_syspolicy
+
+package local
+
+import (
+	"context"
+	"net/http"
+
+	"tailscale.com/util/syspolicy/setting"
+)
+
+// GetEffectivePolicy returns the effective policy for the specified scope.
+func (lc *Client) GetEffectivePolicy(ctx context.Context, scope setting.PolicyScope) (*setting.Snapshot, error) {
+	scopeID, err := scope.MarshalText()
+	if err != nil {
+		return nil, err
+	}
+	body, err := lc.get200(ctx, "/localapi/v0/policy/"+string(scopeID))
+	if err != nil {
+		return nil, err
+	}
+	return decodeJSON[*setting.Snapshot](body)
+}
+
+// ReloadEffectivePolicy reloads the effective policy for the specified scope
+// by reading and merging policy settings from all applicable policy sources.
+func (lc *Client) ReloadEffectivePolicy(ctx context.Context, scope setting.PolicyScope) (*setting.Snapshot, error) {
+	scopeID, err := scope.MarshalText()
+	if err != nil {
+		return nil, err
+	}
+	body, err := lc.send(ctx, "POST", "/localapi/v0/policy/"+string(scopeID), 200, http.NoBody)
+	if err != nil {
+		return nil, err
+	}
+	return decodeJSON[*setting.Snapshot](body)
+}

+ 2 - 1
cmd/tailscale/cli/cli.go

@@ -209,6 +209,7 @@ func noDupFlagify(c *ffcli.Command) {
 }
 
 var fileCmd func() *ffcli.Command
+var sysPolicyCmd func() *ffcli.Command
 
 func newRootCmd() *ffcli.Command {
 	rootfs := newFlagSet("tailscale")
@@ -239,7 +240,7 @@ change in the future.
 			logoutCmd,
 			switchCmd,
 			configureCmd(),
-			syspolicyCmd,
+			nilOrCall(sysPolicyCmd),
 			netcheckCmd,
 			ipCmd,
 			dnsCmd,

+ 38 - 33
cmd/tailscale/cli/syspolicy.go

@@ -1,6 +1,8 @@
 // Copyright (c) Tailscale Inc & AUTHORS
 // SPDX-License-Identifier: BSD-3-Clause
 
+//go:build !ts_omit_syspolicy
+
 package cli
 
 import (
@@ -20,38 +22,42 @@ var syspolicyArgs struct {
 	json bool // JSON output mode
 }
 
-var syspolicyCmd = &ffcli.Command{
-	Name:       "syspolicy",
-	ShortHelp:  "Diagnose the MDM and system policy configuration",
-	LongHelp:   "The 'tailscale syspolicy' command provides tools for diagnosing the MDM and system policy configuration.",
-	ShortUsage: "tailscale syspolicy <subcommand>",
-	UsageFunc:  usageFuncNoDefaultValues,
-	Subcommands: []*ffcli.Command{
-		{
-			Name:       "list",
-			ShortUsage: "tailscale syspolicy list",
-			Exec:       runSysPolicyList,
-			ShortHelp:  "Print effective policy settings",
-			LongHelp:   "The 'tailscale syspolicy list' subcommand displays the effective policy settings and their sources (e.g., MDM or environment variables).",
-			FlagSet: (func() *flag.FlagSet {
-				fs := newFlagSet("syspolicy list")
-				fs.BoolVar(&syspolicyArgs.json, "json", false, "output in JSON format")
-				return fs
-			})(),
-		},
-		{
-			Name:       "reload",
-			ShortUsage: "tailscale syspolicy reload",
-			Exec:       runSysPolicyReload,
-			ShortHelp:  "Force a reload of policy settings, even if no changes are detected, and prints the result",
-			LongHelp:   "The 'tailscale syspolicy reload' subcommand forces a reload of policy settings, even if no changes are detected, and prints the result.",
-			FlagSet: (func() *flag.FlagSet {
-				fs := newFlagSet("syspolicy reload")
-				fs.BoolVar(&syspolicyArgs.json, "json", false, "output in JSON format")
-				return fs
-			})(),
-		},
-	},
+func init() {
+	sysPolicyCmd = func() *ffcli.Command {
+		return &ffcli.Command{
+			Name:       "syspolicy",
+			ShortHelp:  "Diagnose the MDM and system policy configuration",
+			LongHelp:   "The 'tailscale syspolicy' command provides tools for diagnosing the MDM and system policy configuration.",
+			ShortUsage: "tailscale syspolicy <subcommand>",
+			UsageFunc:  usageFuncNoDefaultValues,
+			Subcommands: []*ffcli.Command{
+				{
+					Name:       "list",
+					ShortUsage: "tailscale syspolicy list",
+					Exec:       runSysPolicyList,
+					ShortHelp:  "Print effective policy settings",
+					LongHelp:   "The 'tailscale syspolicy list' subcommand displays the effective policy settings and their sources (e.g., MDM or environment variables).",
+					FlagSet: (func() *flag.FlagSet {
+						fs := newFlagSet("syspolicy list")
+						fs.BoolVar(&syspolicyArgs.json, "json", false, "output in JSON format")
+						return fs
+					})(),
+				},
+				{
+					Name:       "reload",
+					ShortUsage: "tailscale syspolicy reload",
+					Exec:       runSysPolicyReload,
+					ShortHelp:  "Force a reload of policy settings, even if no changes are detected, and prints the result",
+					LongHelp:   "The 'tailscale syspolicy reload' subcommand forces a reload of policy settings, even if no changes are detected, and prints the result.",
+					FlagSet: (func() *flag.FlagSet {
+						fs := newFlagSet("syspolicy reload")
+						fs.BoolVar(&syspolicyArgs.json, "json", false, "output in JSON format")
+						return fs
+					})(),
+				},
+			},
+		}
+	}
 }
 
 func runSysPolicyList(ctx context.Context, args []string) error {
@@ -61,7 +67,6 @@ func runSysPolicyList(ctx context.Context, args []string) error {
 	}
 	printPolicySettings(policy)
 	return nil
-
 }
 
 func runSysPolicyReload(ctx context.Context, args []string) error {

+ 14 - 0
cmd/tailscaled/deps_test.go

@@ -27,3 +27,17 @@ func TestOmitSSH(t *testing.T) {
 		},
 	}.Check(t)
 }
+
+func TestOmitSyspolicy(t *testing.T) {
+	const msg = "unexpected syspolicy usage with ts_omit_syspolicy"
+	deptest.DepChecker{
+		GOOS:   "linux",
+		GOARCH: "amd64",
+		Tags:   "ts_omit_syspolicy,ts_include_cli",
+		BadDeps: map[string]string{
+			"tailscale.com/util/syspolicy":         msg,
+			"tailscale.com/util/syspolicy/setting": msg,
+			"tailscale.com/util/syspolicy/rsop":    msg,
+		},
+	}.Check(t)
+}

+ 0 - 50
ipn/localapi/localapi.go

@@ -58,8 +58,6 @@ import (
 	"tailscale.com/util/mak"
 	"tailscale.com/util/osdiag"
 	"tailscale.com/util/rands"
-	"tailscale.com/util/syspolicy/rsop"
-	"tailscale.com/util/syspolicy/setting"
 	"tailscale.com/version"
 	"tailscale.com/wgengine/magicsock"
 )
@@ -79,7 +77,6 @@ type LocalAPIHandler func(*Handler, http.ResponseWriter, *http.Request)
 var handler = map[string]LocalAPIHandler{
 	// The prefix match handlers end with a slash:
 	"cert/":     (*Handler).serveCert,
-	"policy/":   (*Handler).servePolicy,
 	"profiles/": (*Handler).serveProfiles,
 
 	// The other /localapi/v0/NAME handlers are exact matches and contain only NAME
@@ -1603,53 +1600,6 @@ func (h *Handler) servePrefs(w http.ResponseWriter, r *http.Request) {
 	e.Encode(prefs)
 }
 
-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 == "" {
-		scope = setting.DefaultScope()
-	} 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)
-}
-
 type resJSON struct {
 	Error string `json:",omitempty"`
 }

+ 68 - 0
ipn/localapi/syspolicy_api.go

@@ -0,0 +1,68 @@
+// Copyright (c) Tailscale Inc & AUTHORS
+// 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() {
+	handler["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 == "" {
+		scope = setting.DefaultScope()
+	} 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)
+}