mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
ipn/ipnlocal: allowed suggested exit nodes policy (#12240)
Updates tailscale/corp#19681 Signed-off-by: Claire Wang <claire@tailscale.com>
This commit is contained in:
parent
5ad0dad15e
commit
f1d10c12ac
@ -6453,8 +6453,17 @@ func suggestExitNode(report *netcheck.Report, netMap *netmap.NetworkMap, r *rand
|
||||
if report.PreferredDERP == 0 {
|
||||
return res, ErrNoPreferredDERP
|
||||
}
|
||||
var allowedCandidates set.Set[string]
|
||||
if allowed, err := syspolicy.GetStringArray(syspolicy.AllowedSuggestedExitNodes, nil); err != nil {
|
||||
return res, fmt.Errorf("unable to read %s policy: %w", syspolicy.AllowedSuggestedExitNodes, err)
|
||||
} else if allowed != nil {
|
||||
allowedCandidates = set.SetOf(allowed)
|
||||
}
|
||||
candidates := make([]tailcfg.NodeView, 0, len(netMap.Peers))
|
||||
for _, peer := range netMap.Peers {
|
||||
if allowedCandidates != nil && !allowedCandidates.Contains(string(peer.StableID())) {
|
||||
continue
|
||||
}
|
||||
if peer.CapMap().Has(tailcfg.NodeAttrSuggestExitNode) && tsaddr.ContainsExitRoutes(peer.AllowedIPs()) {
|
||||
candidates = append(candidates, peer)
|
||||
}
|
||||
|
@ -1595,6 +1595,9 @@ type mockSyspolicyHandler struct {
|
||||
// queried by the current test. If the policy is expected but unset, then
|
||||
// use nil, otherwise use a string equal to the policy's desired value.
|
||||
stringPolicies map[syspolicy.Key]*string
|
||||
// stringArrayPolicies is the collection of policies that we expected to see
|
||||
// queries by the current test, that return policy string arrays.
|
||||
stringArrayPolicies map[syspolicy.Key][]string
|
||||
// failUnknownPolicies is set if policies other than those in stringPolicies
|
||||
// (uint64 or bool policies are not supported by mockSyspolicyHandler yet)
|
||||
// should be considered a test failure if they are queried.
|
||||
@ -1632,6 +1635,12 @@ func (h *mockSyspolicyHandler) ReadStringArray(key string) ([]string, error) {
|
||||
if h.failUnknownPolicies {
|
||||
h.t.Errorf("ReadStringArray(%q) unexpectedly called", key)
|
||||
}
|
||||
if s, ok := h.stringArrayPolicies[syspolicy.Key(key)]; ok {
|
||||
if s == nil {
|
||||
return []string{}, syspolicy.ErrNoSuchKey
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
return nil, syspolicy.ErrNoSuchKey
|
||||
}
|
||||
|
||||
@ -3474,6 +3483,7 @@ func TestLocalBackendSuggestExitNode(t *testing.T) {
|
||||
lastSuggestedExitNode lastSuggestedExitNode
|
||||
report *netcheck.Report
|
||||
netMap netmap.NetworkMap
|
||||
allowedSuggestedExitNodes []string
|
||||
wantID tailcfg.StableNodeID
|
||||
wantName string
|
||||
wantErr error
|
||||
@ -3766,10 +3776,138 @@ func TestLocalBackendSuggestExitNode(t *testing.T) {
|
||||
},
|
||||
wantErr: ErrCannotSuggestExitNode,
|
||||
},
|
||||
{
|
||||
name: "only pick from allowed suggested exit nodes",
|
||||
lastSuggestedExitNode: lastSuggestedExitNode{name: "test", id: "test"},
|
||||
report: &netcheck.Report{
|
||||
RegionLatency: map[int]time.Duration{
|
||||
1: 10,
|
||||
2: 10,
|
||||
3: 5,
|
||||
},
|
||||
PreferredDERP: 1,
|
||||
},
|
||||
netMap: netmap.NetworkMap{
|
||||
SelfNode: (&tailcfg.Node{
|
||||
Addresses: []netip.Prefix{
|
||||
netip.MustParsePrefix("100.64.1.1/32"),
|
||||
netip.MustParsePrefix("fe70::1/128"),
|
||||
},
|
||||
}).View(),
|
||||
DERPMap: &tailcfg.DERPMap{
|
||||
Regions: map[int]*tailcfg.DERPRegion{
|
||||
1: {},
|
||||
2: {},
|
||||
3: {},
|
||||
},
|
||||
},
|
||||
Peers: []tailcfg.NodeView{
|
||||
(&tailcfg.Node{
|
||||
ID: 2,
|
||||
StableID: "test",
|
||||
Name: "test",
|
||||
DERP: "127.3.3.40:1",
|
||||
AllowedIPs: []netip.Prefix{
|
||||
netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0"),
|
||||
},
|
||||
CapMap: (tailcfg.NodeCapMap)(map[tailcfg.NodeCapability][]tailcfg.RawMessage{
|
||||
tailcfg.NodeAttrSuggestExitNode: {},
|
||||
tailcfg.NodeAttrAutoExitNode: {},
|
||||
}),
|
||||
}).View(),
|
||||
(&tailcfg.Node{
|
||||
ID: 3,
|
||||
StableID: "foo",
|
||||
Name: "foo",
|
||||
DERP: "127.3.3.40:3",
|
||||
AllowedIPs: []netip.Prefix{
|
||||
netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0"),
|
||||
},
|
||||
CapMap: (tailcfg.NodeCapMap)(map[tailcfg.NodeCapability][]tailcfg.RawMessage{
|
||||
tailcfg.NodeAttrSuggestExitNode: {},
|
||||
tailcfg.NodeAttrAutoExitNode: {},
|
||||
}),
|
||||
}).View(),
|
||||
},
|
||||
},
|
||||
allowedSuggestedExitNodes: []string{"test"},
|
||||
wantID: "test",
|
||||
wantName: "test",
|
||||
wantLastSuggestedExitNode: lastSuggestedExitNode{name: "test", id: "test"},
|
||||
},
|
||||
{
|
||||
name: "allowed suggested exit nodes not nil but length 0",
|
||||
lastSuggestedExitNode: lastSuggestedExitNode{name: "test", id: "test"},
|
||||
report: &netcheck.Report{
|
||||
RegionLatency: map[int]time.Duration{
|
||||
1: 10,
|
||||
2: 10,
|
||||
3: 5,
|
||||
},
|
||||
PreferredDERP: 1,
|
||||
},
|
||||
netMap: netmap.NetworkMap{
|
||||
SelfNode: (&tailcfg.Node{
|
||||
Addresses: []netip.Prefix{
|
||||
netip.MustParsePrefix("100.64.1.1/32"),
|
||||
netip.MustParsePrefix("fe70::1/128"),
|
||||
},
|
||||
}).View(),
|
||||
DERPMap: &tailcfg.DERPMap{
|
||||
Regions: map[int]*tailcfg.DERPRegion{
|
||||
1: {},
|
||||
2: {},
|
||||
3: {},
|
||||
},
|
||||
},
|
||||
Peers: []tailcfg.NodeView{
|
||||
(&tailcfg.Node{
|
||||
ID: 2,
|
||||
StableID: "test",
|
||||
Name: "test",
|
||||
DERP: "127.3.3.40:1",
|
||||
AllowedIPs: []netip.Prefix{
|
||||
netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0"),
|
||||
},
|
||||
CapMap: (tailcfg.NodeCapMap)(map[tailcfg.NodeCapability][]tailcfg.RawMessage{
|
||||
tailcfg.NodeAttrSuggestExitNode: {},
|
||||
tailcfg.NodeAttrAutoExitNode: {},
|
||||
}),
|
||||
}).View(),
|
||||
(&tailcfg.Node{
|
||||
ID: 3,
|
||||
StableID: "foo",
|
||||
Name: "foo",
|
||||
DERP: "127.3.3.40:3",
|
||||
AllowedIPs: []netip.Prefix{
|
||||
netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0"),
|
||||
},
|
||||
CapMap: (tailcfg.NodeCapMap)(map[tailcfg.NodeCapability][]tailcfg.RawMessage{
|
||||
tailcfg.NodeAttrSuggestExitNode: {},
|
||||
tailcfg.NodeAttrAutoExitNode: {},
|
||||
}),
|
||||
}).View(),
|
||||
},
|
||||
},
|
||||
allowedSuggestedExitNodes: []string{},
|
||||
wantID: "foo",
|
||||
wantName: "foo",
|
||||
wantLastSuggestedExitNode: lastSuggestedExitNode{name: "foo", id: "foo"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
lb := newTestLocalBackend(t)
|
||||
msh := &mockSyspolicyHandler{
|
||||
t: t,
|
||||
stringArrayPolicies: map[syspolicy.Key][]string{
|
||||
syspolicy.AllowedSuggestedExitNodes: nil,
|
||||
},
|
||||
}
|
||||
if len(tt.allowedSuggestedExitNodes) != 0 {
|
||||
msh.stringArrayPolicies[syspolicy.AllowedSuggestedExitNodes] = tt.allowedSuggestedExitNodes
|
||||
}
|
||||
syspolicy.SetHandlerForTest(t, msh)
|
||||
lb.lastSuggestedExitNode = tt.lastSuggestedExitNode
|
||||
lb.netMap = &tt.netMap
|
||||
lb.sys.MagicSock.Get().SetLastNetcheckReportForTest(context.Background(), tt.report)
|
||||
|
Loading…
Reference in New Issue
Block a user