all: add means to set device posture attributes from node

Updates tailscale/corp#24690
Updates #4077

Change-Id: I05fe799beb1d2a71d1ec3ae08744cc68bcadae2a
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2024-11-19 20:17:58 -08:00
committed by Brad Fitzpatrick
parent 30d3e7b242
commit ff095606cc
5 changed files with 125 additions and 2 deletions
+14
View File
@@ -6408,6 +6408,20 @@ func (b *LocalBackend) SetExpirySooner(ctx context.Context, expiry time.Time) er
return cc.SetExpirySooner(ctx, expiry)
}
// SetDeviceAttrs does a synchronous call to the control plane to update
// the node's attributes.
//
// See docs on [tailcfg.SetDeviceAttributesRequest] for background.
func (b *LocalBackend) SetDeviceAttrs(ctx context.Context, attrs tailcfg.AttrUpdate) error {
b.mu.Lock()
cc := b.ccAuto
b.mu.Unlock()
if cc == nil {
return errors.New("not running")
}
return cc.SetDeviceAttrs(ctx, attrs)
}
// exitNodeCanProxyDNS reports the DoH base URL ("http://foo/dns-query") without query parameters
// to exitNodeID's DoH service, if available.
//
+28
View File
@@ -83,6 +83,7 @@ var handler = map[string]localAPIHandler{
// The other /localapi/v0/NAME handlers are exact matches and contain only NAME
// without a trailing slash:
"alpha-set-device-attrs": (*Handler).serveSetDeviceAttrs, // see tailscale/corp#24690
"bugreport": (*Handler).serveBugReport,
"check-ip-forwarding": (*Handler).serveCheckIPForwarding,
"check-prefs": (*Handler).serveCheckPrefs,
@@ -446,6 +447,33 @@ func (h *Handler) serveWhoIs(w http.ResponseWriter, r *http.Request) {
h.serveWhoIsWithBackend(w, r, h.b)
}
// serveSetDeviceAttrs is (as of 2024-12-30) an experimental LocalAPI handler to
// set device attributes via the control plane.
//
// See tailscale/corp#24690.
func (h *Handler) serveSetDeviceAttrs(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if !h.PermitWrite {
http.Error(w, "set-device-attrs access denied", http.StatusForbidden)
return
}
if r.Method != "PATCH" {
http.Error(w, "only PATCH allowed", http.StatusMethodNotAllowed)
return
}
var req map[string]any
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := h.b.SetDeviceAttrs(ctx, req); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
io.WriteString(w, "{}\n")
}
// localBackendWhoIsMethods is the subset of ipn.LocalBackend as needed
// by the localapi WhoIs method.
type localBackendWhoIsMethods interface {