mirror of
https://github.com/tailscale/tailscale.git
synced 2025-07-29 15:23:45 +00:00
tailcfg: add Hostinfo.ExitNodeID to report the selected exit node (#16625)
When a client selects a particular exit node, Control may use that as a signal for deciding other routes. This patch causes the client to report whenever the current exit node changes, through tailcfg.Hostinfo.ExitNodeID. It relies on a properly set ipn.Prefs.ExitNodeID, which should already be resolved by `tailscale set`. Updates tailscale/corp#30536 Signed-off-by: Simon Law <sfllaw@tailscale.com>
This commit is contained in:
parent
19faaff95c
commit
729d6532ff
@ -5612,6 +5612,11 @@ func (b *LocalBackend) applyPrefsToHostinfoLocked(hi *tailcfg.Hostinfo, prefs ip
|
|||||||
// WireIngress.
|
// WireIngress.
|
||||||
hi.WireIngress = b.shouldWireInactiveIngressLocked()
|
hi.WireIngress = b.shouldWireInactiveIngressLocked()
|
||||||
hi.AppConnector.Set(prefs.AppConnector().Advertise)
|
hi.AppConnector.Set(prefs.AppConnector().Advertise)
|
||||||
|
|
||||||
|
// The [tailcfg.Hostinfo.ExitNodeID] field tells control which exit node
|
||||||
|
// was selected, if any. Since [LocalBackend.resolveExitNodeIPLocked]
|
||||||
|
// has already run, there is no need to consult [ipn.Prefs.ExitNodeIP].
|
||||||
|
hi.ExitNodeID = prefs.ExitNodeID()
|
||||||
}
|
}
|
||||||
|
|
||||||
// enterState transitions the backend into newState, updating internal
|
// enterState transitions the backend into newState, updating internal
|
||||||
@ -6136,6 +6141,10 @@ func (b *LocalBackend) resolveExitNode() (changed bool) {
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
b.logf("failed to save exit node changes: %v", err)
|
b.logf("failed to save exit node changes: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send the resolved exit node to Control via Hostinfo.
|
||||||
|
b.hostinfo.ExitNodeID = prefs.ExitNodeID
|
||||||
|
|
||||||
b.sendToLocked(ipn.Notify{Prefs: ptr.To(prefs.View())}, allClients)
|
b.sendToLocked(ipn.Notify{Prefs: ptr.To(prefs.View())}, allClients)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -614,19 +614,20 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
prefs ipn.Prefs
|
prefs ipn.Prefs
|
||||||
netMap *netmap.NetworkMap
|
netMap *netmap.NetworkMap
|
||||||
report *netcheck.Report
|
report *netcheck.Report
|
||||||
changePrefs *ipn.MaskedPrefs
|
changePrefs *ipn.MaskedPrefs
|
||||||
useExitNodeEnabled *bool
|
useExitNodeEnabled *bool
|
||||||
exitNodeIDPolicy *tailcfg.StableNodeID
|
exitNodeIDPolicy *tailcfg.StableNodeID
|
||||||
exitNodeIPPolicy *netip.Addr
|
exitNodeIPPolicy *netip.Addr
|
||||||
exitNodeAllowedIDs []tailcfg.StableNodeID // nil if all IDs are allowed for auto exit nodes
|
exitNodeAllowedIDs []tailcfg.StableNodeID // nil if all IDs are allowed for auto exit nodes
|
||||||
exitNodeAllowOverride bool // whether [syspolicy.AllowExitNodeOverride] should be set to true
|
exitNodeAllowOverride bool // whether [syspolicy.AllowExitNodeOverride] should be set to true
|
||||||
wantChangePrefsErr error // if non-nil, the error we expect from [LocalBackend.EditPrefsAs]
|
wantChangePrefsErr error // if non-nil, the error we expect from [LocalBackend.EditPrefsAs]
|
||||||
wantPrefs ipn.Prefs
|
wantPrefs ipn.Prefs
|
||||||
wantExitNodeToggleErr error // if non-nil, the error we expect from [LocalBackend.SetUseExitNodeEnabled]
|
wantExitNodeToggleErr error // if non-nil, the error we expect from [LocalBackend.SetUseExitNodeEnabled]
|
||||||
|
wantHostinfoExitNodeID *tailcfg.StableNodeID
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "exit-node-id-via-prefs", // set exit node ID via prefs
|
name: "exit-node-id-via-prefs", // set exit node ID via prefs
|
||||||
@ -643,6 +644,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ControlURL: controlURL,
|
ControlURL: controlURL,
|
||||||
ExitNodeID: exitNode1.StableID(),
|
ExitNodeID: exitNode1.StableID(),
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode1.StableID()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "exit-node-ip-via-prefs", // set exit node IP via prefs (should be resolved to an ID)
|
name: "exit-node-ip-via-prefs", // set exit node IP via prefs (should be resolved to an ID)
|
||||||
@ -659,6 +661,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ControlURL: controlURL,
|
ControlURL: controlURL,
|
||||||
ExitNodeID: exitNode1.StableID(),
|
ExitNodeID: exitNode1.StableID(),
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode1.StableID()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-exit-node-via-prefs/any", // set auto exit node via prefs
|
name: "auto-exit-node-via-prefs/any", // set auto exit node via prefs
|
||||||
@ -676,6 +679,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ExitNodeID: exitNode1.StableID(),
|
ExitNodeID: exitNode1.StableID(),
|
||||||
AutoExitNode: "any",
|
AutoExitNode: "any",
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode1.StableID()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-exit-node-via-prefs/set-exit-node-id-via-prefs", // setting exit node ID explicitly should disable auto exit node
|
name: "auto-exit-node-via-prefs/set-exit-node-id-via-prefs", // setting exit node ID explicitly should disable auto exit node
|
||||||
@ -695,6 +699,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ExitNodeID: exitNode2.StableID(),
|
ExitNodeID: exitNode2.StableID(),
|
||||||
AutoExitNode: "", // should be unset
|
AutoExitNode: "", // should be unset
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode2.StableID()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-exit-node-via-prefs/any/no-report", // set auto exit node via prefs, but no report means we can't resolve the exit node ID
|
name: "auto-exit-node-via-prefs/any/no-report", // set auto exit node via prefs, but no report means we can't resolve the exit node ID
|
||||||
@ -711,6 +716,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ExitNodeID: unresolvedExitNodeID, // cannot resolve; traffic will be dropped
|
ExitNodeID: unresolvedExitNodeID, // cannot resolve; traffic will be dropped
|
||||||
AutoExitNode: "any",
|
AutoExitNode: "any",
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(unresolvedExitNodeID),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-exit-node-via-prefs/any/no-netmap", // similarly, but without a netmap (no exit node should be selected)
|
name: "auto-exit-node-via-prefs/any/no-netmap", // similarly, but without a netmap (no exit node should be selected)
|
||||||
@ -727,6 +733,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ExitNodeID: unresolvedExitNodeID, // cannot resolve; traffic will be dropped
|
ExitNodeID: unresolvedExitNodeID, // cannot resolve; traffic will be dropped
|
||||||
AutoExitNode: "any",
|
AutoExitNode: "any",
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(unresolvedExitNodeID),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-exit-node-via-prefs/foo", // set auto exit node via prefs with an unknown/unsupported expression
|
name: "auto-exit-node-via-prefs/foo", // set auto exit node via prefs with an unknown/unsupported expression
|
||||||
@ -744,6 +751,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ExitNodeID: exitNode1.StableID(), // unknown exit node expressions should work as "any"
|
ExitNodeID: exitNode1.StableID(), // unknown exit node expressions should work as "any"
|
||||||
AutoExitNode: "foo",
|
AutoExitNode: "foo",
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode1.StableID()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-exit-node-via-prefs/off", // toggle the exit node off after it was set to "any"
|
name: "auto-exit-node-via-prefs/off", // toggle the exit node off after it was set to "any"
|
||||||
@ -763,6 +771,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
AutoExitNode: "",
|
AutoExitNode: "",
|
||||||
InternalExitNodePrior: "auto:any",
|
InternalExitNodePrior: "auto:any",
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(tailcfg.StableNodeID("")),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-exit-node-via-prefs/on", // toggle the exit node on
|
name: "auto-exit-node-via-prefs/on", // toggle the exit node on
|
||||||
@ -779,6 +788,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
AutoExitNode: "any",
|
AutoExitNode: "any",
|
||||||
InternalExitNodePrior: "auto:any",
|
InternalExitNodePrior: "auto:any",
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode1.StableID()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "id-via-policy", // set exit node ID via syspolicy
|
name: "id-via-policy", // set exit node ID via syspolicy
|
||||||
@ -791,6 +801,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ControlURL: controlURL,
|
ControlURL: controlURL,
|
||||||
ExitNodeID: exitNode1.StableID(),
|
ExitNodeID: exitNode1.StableID(),
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode1.StableID()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "id-via-policy/cannot-override-via-prefs/by-id", // syspolicy should take precedence over prefs
|
name: "id-via-policy/cannot-override-via-prefs/by-id", // syspolicy should take precedence over prefs
|
||||||
@ -809,7 +820,8 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ControlURL: controlURL,
|
ControlURL: controlURL,
|
||||||
ExitNodeID: exitNode1.StableID(),
|
ExitNodeID: exitNode1.StableID(),
|
||||||
},
|
},
|
||||||
wantChangePrefsErr: errManagedByPolicy,
|
wantHostinfoExitNodeID: ptr.To(exitNode1.StableID()),
|
||||||
|
wantChangePrefsErr: errManagedByPolicy,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "id-via-policy/cannot-override-via-prefs/by-ip", // syspolicy should take precedence over prefs
|
name: "id-via-policy/cannot-override-via-prefs/by-ip", // syspolicy should take precedence over prefs
|
||||||
@ -828,7 +840,8 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ControlURL: controlURL,
|
ControlURL: controlURL,
|
||||||
ExitNodeID: exitNode1.StableID(),
|
ExitNodeID: exitNode1.StableID(),
|
||||||
},
|
},
|
||||||
wantChangePrefsErr: errManagedByPolicy,
|
wantHostinfoExitNodeID: ptr.To(exitNode1.StableID()),
|
||||||
|
wantChangePrefsErr: errManagedByPolicy,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "id-via-policy/cannot-override-via-prefs/by-auto-expr", // syspolicy should take precedence over prefs
|
name: "id-via-policy/cannot-override-via-prefs/by-auto-expr", // syspolicy should take precedence over prefs
|
||||||
@ -860,6 +873,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ControlURL: controlURL,
|
ControlURL: controlURL,
|
||||||
ExitNodeID: exitNode2.StableID(),
|
ExitNodeID: exitNode2.StableID(),
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode2.StableID()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-any-via-policy", // set auto exit node via syspolicy (an exit node should be selected)
|
name: "auto-any-via-policy", // set auto exit node via syspolicy (an exit node should be selected)
|
||||||
@ -874,6 +888,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ExitNodeID: exitNode1.StableID(),
|
ExitNodeID: exitNode1.StableID(),
|
||||||
AutoExitNode: "any",
|
AutoExitNode: "any",
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode1.StableID()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-any-via-policy/no-report", // set auto exit node via syspolicy without a netcheck report (no exit node should be selected)
|
name: "auto-any-via-policy/no-report", // set auto exit node via syspolicy without a netcheck report (no exit node should be selected)
|
||||||
@ -888,6 +903,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ExitNodeID: unresolvedExitNodeID,
|
ExitNodeID: unresolvedExitNodeID,
|
||||||
AutoExitNode: "any",
|
AutoExitNode: "any",
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(unresolvedExitNodeID),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-any-via-policy/no-netmap", // similarly, but without a netmap (no exit node should be selected)
|
name: "auto-any-via-policy/no-netmap", // similarly, but without a netmap (no exit node should be selected)
|
||||||
@ -902,6 +918,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ExitNodeID: unresolvedExitNodeID,
|
ExitNodeID: unresolvedExitNodeID,
|
||||||
AutoExitNode: "any",
|
AutoExitNode: "any",
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(unresolvedExitNodeID),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-any-via-policy/no-netmap/with-existing", // set auto exit node via syspolicy without a netmap, but with a previously set exit node ID
|
name: "auto-any-via-policy/no-netmap/with-existing", // set auto exit node via syspolicy without a netmap, but with a previously set exit node ID
|
||||||
@ -918,6 +935,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ExitNodeID: exitNode2.StableID(),
|
ExitNodeID: exitNode2.StableID(),
|
||||||
AutoExitNode: "any",
|
AutoExitNode: "any",
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode2.StableID()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-any-via-policy/no-netmap/with-allowed-existing", // same, but now with a syspolicy setting that explicitly allows the existing exit node ID
|
name: "auto-any-via-policy/no-netmap/with-allowed-existing", // same, but now with a syspolicy setting that explicitly allows the existing exit node ID
|
||||||
@ -936,6 +954,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ExitNodeID: exitNode2.StableID(),
|
ExitNodeID: exitNode2.StableID(),
|
||||||
AutoExitNode: "any",
|
AutoExitNode: "any",
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode2.StableID()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-any-via-policy/no-netmap/with-disallowed-existing", // same, but now with a syspolicy setting that does not allow the existing exit node ID
|
name: "auto-any-via-policy/no-netmap/with-disallowed-existing", // same, but now with a syspolicy setting that does not allow the existing exit node ID
|
||||||
@ -954,6 +973,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ExitNodeID: unresolvedExitNodeID, // we don't have a netmap yet, and the current exit node ID is not allowed; block traffic
|
ExitNodeID: unresolvedExitNodeID, // we don't have a netmap yet, and the current exit node ID is not allowed; block traffic
|
||||||
AutoExitNode: "any",
|
AutoExitNode: "any",
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(unresolvedExitNodeID),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-any-via-policy/with-netmap/with-allowed-existing", // same, but now with a syspolicy setting that does not allow the existing exit node ID
|
name: "auto-any-via-policy/with-netmap/with-allowed-existing", // same, but now with a syspolicy setting that does not allow the existing exit node ID
|
||||||
@ -972,6 +992,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ExitNodeID: exitNode2.StableID(), // we have a netmap; switch to the best allowed exit node
|
ExitNodeID: exitNode2.StableID(), // we have a netmap; switch to the best allowed exit node
|
||||||
AutoExitNode: "any",
|
AutoExitNode: "any",
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode2.StableID()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-any-via-policy/with-netmap/switch-to-better", // if all exit nodes are allowed, switch to the best one once we have a netmap
|
name: "auto-any-via-policy/with-netmap/switch-to-better", // if all exit nodes are allowed, switch to the best one once we have a netmap
|
||||||
@ -987,6 +1008,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ExitNodeID: exitNode1.StableID(), // switch to the best exit node
|
ExitNodeID: exitNode1.StableID(), // switch to the best exit node
|
||||||
AutoExitNode: "any",
|
AutoExitNode: "any",
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode1.StableID()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-foo-via-policy", // set auto exit node via syspolicy with an unknown/unsupported expression
|
name: "auto-foo-via-policy", // set auto exit node via syspolicy with an unknown/unsupported expression
|
||||||
@ -1001,6 +1023,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ExitNodeID: exitNode1.StableID(), // unknown exit node expressions should work as "any"
|
ExitNodeID: exitNode1.StableID(), // unknown exit node expressions should work as "any"
|
||||||
AutoExitNode: "foo",
|
AutoExitNode: "foo",
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode1.StableID()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-foo-via-edit-prefs", // set auto exit node via EditPrefs with an unknown/unsupported expression
|
name: "auto-foo-via-edit-prefs", // set auto exit node via EditPrefs with an unknown/unsupported expression
|
||||||
@ -1018,6 +1041,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ExitNodeID: exitNode1.StableID(), // unknown exit node expressions should work as "any"
|
ExitNodeID: exitNode1.StableID(), // unknown exit node expressions should work as "any"
|
||||||
AutoExitNode: "foo",
|
AutoExitNode: "foo",
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode1.StableID()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-any-via-policy/toggle-off", // cannot toggle off the exit node if it was set via syspolicy
|
name: "auto-any-via-policy/toggle-off", // cannot toggle off the exit node if it was set via syspolicy
|
||||||
@ -1035,6 +1059,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
AutoExitNode: "any",
|
AutoExitNode: "any",
|
||||||
InternalExitNodePrior: "",
|
InternalExitNodePrior: "",
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode1.StableID()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-any-via-policy/allow-override/change", // changing the exit node is allowed by [syspolicy.AllowExitNodeOverride]
|
name: "auto-any-via-policy/allow-override/change", // changing the exit node is allowed by [syspolicy.AllowExitNodeOverride]
|
||||||
@ -1056,6 +1081,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
ExitNodeID: exitNode2.StableID(), // overridden by user
|
ExitNodeID: exitNode2.StableID(), // overridden by user
|
||||||
AutoExitNode: "", // cleared, as we are setting the exit node ID explicitly
|
AutoExitNode: "", // cleared, as we are setting the exit node ID explicitly
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode2.StableID()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-any-via-policy/allow-override/clear", // clearing the exit node ID is not allowed by [syspolicy.AllowExitNodeOverride]
|
name: "auto-any-via-policy/allow-override/clear", // clearing the exit node ID is not allowed by [syspolicy.AllowExitNodeOverride]
|
||||||
@ -1079,6 +1105,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
AutoExitNode: "any",
|
AutoExitNode: "any",
|
||||||
InternalExitNodePrior: "",
|
InternalExitNodePrior: "",
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode1.StableID()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-any-via-policy/allow-override/toggle-off", // similarly, toggling off the exit node is not allowed even with [syspolicy.AllowExitNodeOverride]
|
name: "auto-any-via-policy/allow-override/toggle-off", // similarly, toggling off the exit node is not allowed even with [syspolicy.AllowExitNodeOverride]
|
||||||
@ -1097,6 +1124,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
AutoExitNode: "any",
|
AutoExitNode: "any",
|
||||||
InternalExitNodePrior: "",
|
InternalExitNodePrior: "",
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode1.StableID()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-any-via-initial-prefs/no-netmap/clear-auto-exit-node",
|
name: "auto-any-via-initial-prefs/no-netmap/clear-auto-exit-node",
|
||||||
@ -1117,6 +1145,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
AutoExitNode: "", // cleared
|
AutoExitNode: "", // cleared
|
||||||
ExitNodeID: "", // has never been resolved, so it should be cleared as well
|
ExitNodeID: "", // has never been resolved, so it should be cleared as well
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(tailcfg.StableNodeID("")),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto-any-via-initial-prefs/with-netmap/clear-auto-exit-node",
|
name: "auto-any-via-initial-prefs/with-netmap/clear-auto-exit-node",
|
||||||
@ -1137,6 +1166,7 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
AutoExitNode: "", // cleared
|
AutoExitNode: "", // cleared
|
||||||
ExitNodeID: exitNode1.StableID(), // a resolved exit node ID should be retained
|
ExitNodeID: exitNode1.StableID(), // a resolved exit node ID should be retained
|
||||||
},
|
},
|
||||||
|
wantHostinfoExitNodeID: ptr.To(exitNode1.StableID()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
syspolicy.RegisterWellKnownSettingsForTest(t)
|
syspolicy.RegisterWellKnownSettingsForTest(t)
|
||||||
@ -1197,6 +1227,13 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
if diff := cmp.Diff(&tt.wantPrefs, lb.Prefs().AsStruct(), opts...); diff != "" {
|
if diff := cmp.Diff(&tt.wantPrefs, lb.Prefs().AsStruct(), opts...); diff != "" {
|
||||||
t.Errorf("Prefs(+got -want): %v", diff)
|
t.Errorf("Prefs(+got -want): %v", diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// And check Hostinfo.
|
||||||
|
if tt.wantHostinfoExitNodeID != nil {
|
||||||
|
if got := lb.hostinfo.ExitNodeID; got != *tt.wantHostinfoExitNodeID {
|
||||||
|
t.Errorf("Hostinfo.ExitNodeID got %v, want %v", got, *tt.wantHostinfoExitNodeID)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,7 +166,8 @@ type CapabilityVersion int
|
|||||||
// - 119: 2025-07-10: Client uses Hostinfo.Location.Priority to prioritize one route over another.
|
// - 119: 2025-07-10: Client uses Hostinfo.Location.Priority to prioritize one route over another.
|
||||||
// - 120: 2025-07-15: Client understands peer relay disco messages, and implements peer client and relay server functions
|
// - 120: 2025-07-15: Client understands peer relay disco messages, and implements peer client and relay server functions
|
||||||
// - 121: 2025-07-19: Client understands peer relay endpoint alloc with [disco.AllocateUDPRelayEndpointRequest] & [disco.AllocateUDPRelayEndpointResponse]
|
// - 121: 2025-07-19: Client understands peer relay endpoint alloc with [disco.AllocateUDPRelayEndpointRequest] & [disco.AllocateUDPRelayEndpointResponse]
|
||||||
const CurrentCapabilityVersion CapabilityVersion = 121
|
// - 122: 2025-07-21: Client sends Hostinfo.ExitNodeID to report which exit node it has selected, if any.
|
||||||
|
const CurrentCapabilityVersion CapabilityVersion = 122
|
||||||
|
|
||||||
// ID is an integer ID for a user, node, or login allocated by the
|
// ID is an integer ID for a user, node, or login allocated by the
|
||||||
// control plane.
|
// control plane.
|
||||||
@ -875,6 +876,7 @@ type Hostinfo struct {
|
|||||||
UserspaceRouter opt.Bool `json:",omitempty"` // if the client's subnet router is running in userspace (netstack) mode
|
UserspaceRouter opt.Bool `json:",omitempty"` // if the client's subnet router is running in userspace (netstack) mode
|
||||||
AppConnector opt.Bool `json:",omitempty"` // if the client is running the app-connector service
|
AppConnector opt.Bool `json:",omitempty"` // if the client is running the app-connector service
|
||||||
ServicesHash string `json:",omitempty"` // opaque hash of the most recent list of tailnet services, change in hash indicates config should be fetched via c2n
|
ServicesHash string `json:",omitempty"` // opaque hash of the most recent list of tailnet services, change in hash indicates config should be fetched via c2n
|
||||||
|
ExitNodeID StableNodeID `json:",omitzero"` // the client’s selected exit node, empty when unselected.
|
||||||
|
|
||||||
// Location represents geographical location data about a
|
// Location represents geographical location data about a
|
||||||
// Tailscale host. Location is optional and only set if
|
// Tailscale host. Location is optional and only set if
|
||||||
|
@ -186,6 +186,7 @@ var _HostinfoCloneNeedsRegeneration = Hostinfo(struct {
|
|||||||
UserspaceRouter opt.Bool
|
UserspaceRouter opt.Bool
|
||||||
AppConnector opt.Bool
|
AppConnector opt.Bool
|
||||||
ServicesHash string
|
ServicesHash string
|
||||||
|
ExitNodeID StableNodeID
|
||||||
Location *Location
|
Location *Location
|
||||||
TPM *TPMInfo
|
TPM *TPMInfo
|
||||||
StateEncrypted opt.Bool
|
StateEncrypted opt.Bool
|
||||||
|
@ -67,6 +67,7 @@ func TestHostinfoEqual(t *testing.T) {
|
|||||||
"UserspaceRouter",
|
"UserspaceRouter",
|
||||||
"AppConnector",
|
"AppConnector",
|
||||||
"ServicesHash",
|
"ServicesHash",
|
||||||
|
"ExitNodeID",
|
||||||
"Location",
|
"Location",
|
||||||
"TPM",
|
"TPM",
|
||||||
"StateEncrypted",
|
"StateEncrypted",
|
||||||
@ -273,6 +274,21 @@ func TestHostinfoEqual(t *testing.T) {
|
|||||||
&Hostinfo{IngressEnabled: true},
|
&Hostinfo{IngressEnabled: true},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
&Hostinfo{ExitNodeID: "stable-exit"},
|
||||||
|
&Hostinfo{ExitNodeID: "stable-exit"},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&Hostinfo{ExitNodeID: ""},
|
||||||
|
&Hostinfo{},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&Hostinfo{ExitNodeID: ""},
|
||||||
|
&Hostinfo{ExitNodeID: "stable-exit"},
|
||||||
|
false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
got := tt.a.Equal(tt.b)
|
got := tt.a.Equal(tt.b)
|
||||||
|
@ -300,6 +300,7 @@ func (v HostinfoView) Userspace() opt.Bool { return v.ж.User
|
|||||||
func (v HostinfoView) UserspaceRouter() opt.Bool { return v.ж.UserspaceRouter }
|
func (v HostinfoView) UserspaceRouter() opt.Bool { return v.ж.UserspaceRouter }
|
||||||
func (v HostinfoView) AppConnector() opt.Bool { return v.ж.AppConnector }
|
func (v HostinfoView) AppConnector() opt.Bool { return v.ж.AppConnector }
|
||||||
func (v HostinfoView) ServicesHash() string { return v.ж.ServicesHash }
|
func (v HostinfoView) ServicesHash() string { return v.ж.ServicesHash }
|
||||||
|
func (v HostinfoView) ExitNodeID() StableNodeID { return v.ж.ExitNodeID }
|
||||||
func (v HostinfoView) Location() LocationView { return v.ж.Location.View() }
|
func (v HostinfoView) Location() LocationView { return v.ж.Location.View() }
|
||||||
func (v HostinfoView) TPM() views.ValuePointer[TPMInfo] { return views.ValuePointerOf(v.ж.TPM) }
|
func (v HostinfoView) TPM() views.ValuePointer[TPMInfo] { return views.ValuePointerOf(v.ж.TPM) }
|
||||||
|
|
||||||
@ -345,6 +346,7 @@ var _HostinfoViewNeedsRegeneration = Hostinfo(struct {
|
|||||||
UserspaceRouter opt.Bool
|
UserspaceRouter opt.Bool
|
||||||
AppConnector opt.Bool
|
AppConnector opt.Bool
|
||||||
ServicesHash string
|
ServicesHash string
|
||||||
|
ExitNodeID StableNodeID
|
||||||
Location *Location
|
Location *Location
|
||||||
TPM *TPMInfo
|
TPM *TPMInfo
|
||||||
StateEncrypted opt.Bool
|
StateEncrypted opt.Bool
|
||||||
|
Loading…
x
Reference in New Issue
Block a user