mapper, state: deliver nodeAttrs through MapResponse and harden nextdns DoH rewrite
WithSelfNode and buildTailPeers merge each node's policy CapMap
into the tailcfg.Node.CapMap they emit. State.NodeCapMap and
State.NodeCapMaps wrap the policy manager: NodeCapMap returns a
defensive clone per call; NodeCapMaps snapshots the full per-node
map once for batched callers, amortising pm.mu acquisition across
a peer build.
generateDNSConfig grew a per-node CapMap argument so it can apply
nodeAttr-driven DNS overlays. The nextdns DoH rewrite hardens against
policy-controlled inputs:
- nextDNSDoHHost anchors the prefix match instead of substring,
so a hostile resolver URL cannot smuggle a nextdns hostname in
a path or query.
- nextDNSProfileFromCapMap accepts only profile names matching
[A-Za-z0-9._-]{1,64} and picks the lexicographically first when
multiple are granted -- deterministic, no shell metacharacters
or URL fragments through.
- addNextDNSMetadata composes the rewritten URL via url.Parse +
url.Values rather than fmt.Sprintf, so existing query strings
on the resolver URL survive and metadata cannot inject a new
component.
WithTaildropEnabled in servertest controls cfg.Taildrop.Enabled per
test so cap/file-sharing emission can be toggled in tests that need
to verify the off path.
This commit is contained in:
parent
a4f05b0962
commit
6fcff9e352
5 changed files with 312 additions and 19 deletions
|
|
@ -313,6 +313,18 @@ func (s *State) ReloadPolicy() ([]change.Change, error) {
|
|||
//nolint:prealloc // cs starts with one element and may grow
|
||||
cs := []change.Change{change.PolicyChange()}
|
||||
|
||||
// Per-node selective self refresh for nodeAttrs. A broadcast
|
||||
// PolicyChange() re-renders peer lists and packet filters but
|
||||
// never repopulates a node's own [tailcfg.Node.CapMap]; that
|
||||
// lives on the self entry only. The drain returns every node ID
|
||||
// whose cap output shifted across recent updateLocked calls —
|
||||
// refreshNodeAttrsLocked appends rather than overwrites so a
|
||||
// concurrent SetUsers/SetNodes between SetPolicy and the drain
|
||||
// cannot silently lose the policy-reload diff.
|
||||
for _, id := range s.polMan.NodesWithChangedCapMap() {
|
||||
cs = append(cs, change.SelfUpdate(id))
|
||||
}
|
||||
|
||||
// Always call autoApproveNodes during policy reload, regardless of whether
|
||||
// the policy content has changed. This ensures that routes are re-evaluated
|
||||
// when they might have been manually disabled but could now be auto-approved
|
||||
|
|
@ -1048,6 +1060,19 @@ func (s *State) MatchersForNode(node types.NodeView) ([]matcher.Match, error) {
|
|||
return s.polMan.MatchersForNode(node)
|
||||
}
|
||||
|
||||
// NodeCapMap returns the policy-derived CapMap for the given node, suitable
|
||||
// for merging into tailcfg.Node.CapMap when the node is rendered as self or
|
||||
// as someone else's peer.
|
||||
func (s *State) NodeCapMap(id types.NodeID) tailcfg.NodeCapMap {
|
||||
return s.polMan.NodeCapMap(id)
|
||||
}
|
||||
|
||||
// NodeCapMaps returns a snapshot of every node's policy CapMap so
|
||||
// callers can amortise lock acquisition over a peer loop.
|
||||
func (s *State) NodeCapMaps() map[types.NodeID]tailcfg.NodeCapMap {
|
||||
return s.polMan.NodeCapMaps()
|
||||
}
|
||||
|
||||
// NodeCanHaveTag checks if a node is allowed to have a specific tag.
|
||||
func (s *State) NodeCanHaveTag(node types.NodeView, tag string) bool {
|
||||
return s.polMan.NodeCanHaveTag(node, tag)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue