mirror of
https://github.com/tailscale/tailscale.git
synced 2025-11-16 02:44:28 +00:00
cmd/tailscale/cli, client/tailscale, ipn/localapi: add tailscale syspolicy {list,reload} commands
In this PR, we add the tailscale syspolicy command with two subcommands: list, which displays policy settings, and reload, which forces a reload of those settings. We also update the LocalAPI and LocalClient to facilitate these additions. Updates #12687 Signed-off-by: Nick Khyl <nickk@tailscale.com>
This commit is contained in:
@@ -62,6 +62,8 @@ import (
|
||||
"tailscale.com/util/osdiag"
|
||||
"tailscale.com/util/progresstracking"
|
||||
"tailscale.com/util/rands"
|
||||
"tailscale.com/util/syspolicy/rsop"
|
||||
"tailscale.com/util/syspolicy/setting"
|
||||
"tailscale.com/version"
|
||||
"tailscale.com/wgengine/magicsock"
|
||||
)
|
||||
@@ -76,6 +78,7 @@ var handler = map[string]localAPIHandler{
|
||||
"cert/": (*Handler).serveCert,
|
||||
"file-put/": (*Handler).serveFilePut,
|
||||
"files/": (*Handler).serveFiles,
|
||||
"policy/": (*Handler).servePolicy,
|
||||
"profiles/": (*Handler).serveProfiles,
|
||||
|
||||
// The other /localapi/v0/NAME handlers are exact matches and contain only NAME
|
||||
@@ -1332,6 +1335,53 @@ 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 "GET":
|
||||
effectivePolicy = policy.Get()
|
||||
case "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"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user