mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
all: [experiment] 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:
parent
9f33aeb649
commit
11d8e4ce94
@ -1643,6 +1643,43 @@ func (c *Direct) ReportHealthChange(w *health.Warnable, us *health.UnhealthyStat
|
|||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Auto) SetDeviceAttrs(ctx context.Context, attrs map[string]any) error {
|
||||||
|
return c.direct.SetDeviceAttrs(ctx, attrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Direct) SetDeviceAttrs(ctx context.Context, attrs map[string]any) error {
|
||||||
|
np, err := c.getNoiseClient()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nodeKey, ok := c.GetPersist().PublicNodeKeyOK()
|
||||||
|
if !ok {
|
||||||
|
return errors.New("no node key")
|
||||||
|
}
|
||||||
|
if c.panicOnUse {
|
||||||
|
panic("tainted client")
|
||||||
|
}
|
||||||
|
// TODO(angott): at some point, update `Subsys` in the request to be `Warnable`
|
||||||
|
req := &tailcfg.SetDeviceAttributesRequest{
|
||||||
|
NodeKey: nodeKey,
|
||||||
|
Version: tailcfg.CurrentCapabilityVersion,
|
||||||
|
Update: attrs,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
res, err := np.doWithBody(ctx, "PATCH", "/machine/set-device-attr", nodeKey, req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
all, _ := io.ReadAll(res.Body)
|
||||||
|
if res.StatusCode != 200 {
|
||||||
|
return fmt.Errorf("HTTP error from control plane: %v: %s", res.Status, all)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func addLBHeader(req *http.Request, nodeKey key.NodePublic) {
|
func addLBHeader(req *http.Request, nodeKey key.NodePublic) {
|
||||||
if !nodeKey.IsZero() {
|
if !nodeKey.IsZero() {
|
||||||
req.Header.Add(tailcfg.LBHeader, nodeKey.String())
|
req.Header.Add(tailcfg.LBHeader, nodeKey.String())
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"log"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -380,17 +381,21 @@ func (nc *NoiseClient) dial(ctx context.Context) (*noiseconn.Conn, error) {
|
|||||||
// post does a POST to the control server at the given path, JSON-encoding body.
|
// post does a POST to the control server at the given path, JSON-encoding body.
|
||||||
// The provided nodeKey is an optional load balancing hint.
|
// The provided nodeKey is an optional load balancing hint.
|
||||||
func (nc *NoiseClient) post(ctx context.Context, path string, nodeKey key.NodePublic, body any) (*http.Response, error) {
|
func (nc *NoiseClient) post(ctx context.Context, path string, nodeKey key.NodePublic, body any) (*http.Response, error) {
|
||||||
|
return nc.doWithBody(ctx, "POST", path, nodeKey, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nc *NoiseClient) doWithBody(ctx context.Context, method, path string, nodeKey key.NodePublic, body any) (*http.Response, error) {
|
||||||
jbody, err := json.Marshal(body)
|
jbody, err := json.Marshal(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
req, err := http.NewRequestWithContext(ctx, "POST", "https://"+nc.host+path, bytes.NewReader(jbody))
|
req, err := http.NewRequestWithContext(ctx, method, "https://"+nc.host+path, bytes.NewReader(jbody))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
addLBHeader(req, nodeKey)
|
addLBHeader(req, nodeKey)
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
log.Printf("XXXX doing %+v", req)
|
||||||
conn, err := nc.getConn(ctx)
|
conn, err := nc.getConn(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -6325,6 +6325,16 @@ func (b *LocalBackend) SetExpirySooner(ctx context.Context, expiry time.Time) er
|
|||||||
return cc.SetExpirySooner(ctx, expiry)
|
return cc.SetExpirySooner(ctx, expiry)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *LocalBackend) SetDeviceAttrs(ctx context.Context, attrs map[string]any) 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
|
// exitNodeCanProxyDNS reports the DoH base URL ("http://foo/dns-query") without query parameters
|
||||||
// to exitNodeID's DoH service, if available.
|
// to exitNodeID's DoH service, if available.
|
||||||
//
|
//
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"maps"
|
"maps"
|
||||||
"mime"
|
"mime"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
@ -149,6 +150,7 @@
|
|||||||
"usermetrics": (*Handler).serveUserMetrics,
|
"usermetrics": (*Handler).serveUserMetrics,
|
||||||
"watch-ipn-bus": (*Handler).serveWatchIPNBus,
|
"watch-ipn-bus": (*Handler).serveWatchIPNBus,
|
||||||
"whois": (*Handler).serveWhoIs,
|
"whois": (*Handler).serveWhoIs,
|
||||||
|
"alpha-set-device-attrs": (*Handler).serveSetDeviceAttrs,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -226,6 +228,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
if fn, ok := handlerForPath(r.URL.Path); ok {
|
if fn, ok := handlerForPath(r.URL.Path); ok {
|
||||||
fn(h, w, r)
|
fn(h, w, r)
|
||||||
} else {
|
} else {
|
||||||
|
log.Printf("XXX nothing found for %q", r.URL.Path)
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -446,6 +449,29 @@ func (h *Handler) serveWhoIs(w http.ResponseWriter, r *http.Request) {
|
|||||||
h.serveWhoIsWithBackend(w, r, h.b)
|
h.serveWhoIsWithBackend(w, r, h.b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// localBackendWhoIsMethods is the subset of ipn.LocalBackend as needed
|
||||||
// by the localapi WhoIs method.
|
// by the localapi WhoIs method.
|
||||||
type localBackendWhoIsMethods interface {
|
type localBackendWhoIsMethods interface {
|
||||||
|
@ -2450,6 +2450,13 @@ type HealthChangeRequest struct {
|
|||||||
NodeKey key.NodePublic
|
NodeKey key.NodePublic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SetDeviceAttributesRequest struct {
|
||||||
|
Version CapabilityVersion
|
||||||
|
NodeKey key.NodePublic
|
||||||
|
|
||||||
|
Update map[string]any // attribute name => {string, float64, int64, bool, nil to delete}
|
||||||
|
}
|
||||||
|
|
||||||
// SSHPolicy is the policy for how to handle incoming SSH connections
|
// SSHPolicy is the policy for how to handle incoming SSH connections
|
||||||
// over Tailscale.
|
// over Tailscale.
|
||||||
type SSHPolicy struct {
|
type SSHPolicy struct {
|
||||||
|
Loading…
Reference in New Issue
Block a user