-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
all: add means to set device posture attributes from node #14163
Conversation
11d8e4c
to
fb47521
Compare
fb47521
to
6cb8872
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add a locally enforced rate limit and perhaps a 429 pacer too, to avoid issues with users runaway scripts
// for internal testing. See tailscale/corp#24690. | ||
type SetDeviceAttributesRequest struct { | ||
Version CapabilityVersion | ||
NodeKey key.NodePublic |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be good to say what the semantics are for how it gets used / how authentication works?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's identical to how all the machine requests work (with a Version and then a NodeKey that must match the machine key used for the ts2021 transport) but I can say a bit more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -1643,6 +1643,50 @@ func (c *Direct) ReportHealthChange(w *health.Warnable, us *health.UnhealthyStat | |||
res.Body.Close() | |||
} | |||
|
|||
// SetDeviceAttrs does a synchronous call to the control plane to update | |||
// the node's attributes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest s/node's attributes/device posture attributes/g
to align with how we're calling these in the docs and minimize potential confusion with nodeAttrs
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
control/controlclient/direct.go
Outdated
// | ||
// See docs on [tailcfg.SetDeviceAttributesRequest] for background. | ||
func (c *Direct) SetDeviceAttrs(ctx context.Context, attrs tailcfg.AttrUpdate) error { | ||
np, err := c.getNoiseClient() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am curious why we don't call DoNoiseRequest
here, which already checks for panicOnUse
, and seems to use the same transport.
This comment implies that using getConn
directly (like doWithBody
does) would attach node key proofs, but I don't think that's actually true? I only see KeyProvingNoiseRoundTripper
used in tsnet.
tailscale/control/controlclient/noise.go
Lines 37 to 41 in 30d3e7b
// Client is an HTTP client to talk to the coordination server. | |
// It automatically makes a new Noise connection as needed. | |
// It does not support node key proofs. To do that, call | |
// noiseClient.getConn instead to make a connection. | |
*http.Client |
I am likely missing something, since it's my first time taking a closer look at noise client, but I would like to understand it better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, right. That was added for one caller ages ago that needed to do it from afar, but all the existing callers (who used the helpers that automatically JSON-encode the request body) were never updated to use it, because the exported one isn't as helpful.
But then apparently a handful of people have since moved on to using DoNoiseRequest directly, and doing the JSON encoding themselves. But those callers aren't setting the load balancer hint header:
func addLBHeader(req *http.Request, nodeKey key.NodePublic) {
if !nodeKey.IsZero() {
req.Header.Add(tailcfg.LBHeader, nodeKey.String())
}
}
Which makes the load balancer work a bit harder when it's absent.
There's about a 50/50 split between the two styles right now and I don't want to clean them up one way or another in this PR, but I do want to clean it up in its own change later.
I'll keep this as is for now (as it calls addLBHeader
) but yeah, I don't like the split.
} | ||
|
||
// AttrUpdate is a map of attributes to update. | ||
// Attributes not in the map are left unchanged. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest linking to https://tailscale.com/s/api-device-posture-attrs (which I am adding in https://github.com/tailscale/tailscale-www/pull/7256) to provide more context on supported keys and values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
tailcfg/tailcfg.go
Outdated
Version CapabilityVersion | ||
NodeKey key.NodePublic | ||
|
||
// Update is a map of attributes to update. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect at some point we might want clients to be able to set an expiry time, like our HTTP API supports: https://tailscale.com/api#tag/devices/POST/device/{deviceId}/attributes/{attributeKey}
Would that be another map of key->time.Time? (similar to the expiries
field returned by our get api)
Or should the API be mapping keys to another struct, which would contain value alongside an optional expiry time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about we keep it any-to-any now (where the value any
can be float64/bool/string/nil) and in the future we could add a struct (JSON object) type for other metadata like expiries?
The capver will at least tell us server-side how long like the client will block (30 seconds today) for a response, so we can also rate limit them server side. If we added some client-side protection, I'd want to do it for all machine requests, not just for this one. |
Updates tailscale/corp#24690 Updates #4077 Change-Id: I05fe799beb1d2a71d1ec3ae08744cc68bcadae2a Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
6cb8872
to
ca2ba26
Compare
Updates tailscale/corp#24690
Updates #4077