mirror of
https://github.com/juanfont/headscale.git
synced 2026-09-18 14:32:01 +09:00
mapper: filter peer-change patches by ACL visibility
buildFromChange added PeersChangedPatch (online/offline, endpoint, key-expiry) directly, skipping the policy.ReduceNodes visibility filter that buildTailPeers applies to full peers. A node thus received the existence, presence, and addresses of peers its ACL forbids accessing. Restrict patches to the recipient's visible peer set.
This commit is contained in:
committed by
Kristoffer Dalby
parent
0fdff0c79b
commit
cd1c208980
@@ -428,8 +428,9 @@ func (m *mapper) buildFromChange(
|
||||
}
|
||||
}
|
||||
|
||||
if len(resp.PeerPatches) > 0 {
|
||||
builder.WithPeerChangedPatch(resp.PeerPatches)
|
||||
patches := m.filterVisiblePeerPatches(nodeID, resp.PeerPatches)
|
||||
if len(patches) > 0 {
|
||||
builder.WithPeerChangedPatch(patches)
|
||||
}
|
||||
|
||||
if resp.PingRequest != nil {
|
||||
@@ -439,6 +440,49 @@ func (m *mapper) buildFromChange(
|
||||
return builder.Build()
|
||||
}
|
||||
|
||||
// filterVisiblePeerPatches drops peer-change patches whose target peer the
|
||||
// recipient cannot see under the ACL policy, mirroring the [policy.ReduceNodes]
|
||||
// visibility filter [MapResponseBuilder.buildTailPeers] applies to full peer
|
||||
// objects. Without it, online/offline, endpoint, and key-expiry patches
|
||||
// disclose the existence, presence, and addresses of peers the recipient's
|
||||
// policy forbids it from accessing.
|
||||
func (m *mapper) filterVisiblePeerPatches(
|
||||
nodeID types.NodeID,
|
||||
patches []*tailcfg.PeerChange,
|
||||
) []*tailcfg.PeerChange {
|
||||
node, ok := m.state.GetNodeByID(nodeID)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
matchers, err := m.state.MatchersForNode(node)
|
||||
if err != nil {
|
||||
// Fail closed: if visibility cannot be resolved, send no patches
|
||||
// rather than risk leaking peers the node may not access.
|
||||
return nil
|
||||
}
|
||||
|
||||
// No matchers means no policy restrictions, so every peer is visible —
|
||||
// the same default buildTailPeers applies.
|
||||
if len(matchers) == 0 {
|
||||
return patches
|
||||
}
|
||||
|
||||
var filtered []*tailcfg.PeerChange
|
||||
for _, patch := range patches {
|
||||
peer, ok := m.state.GetNodeByID(types.NodeID(patch.NodeID))
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if node.CanAccess(matchers, peer) || peer.CanAccess(matchers, node) {
|
||||
filtered = append(filtered, patch)
|
||||
}
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
func writeDebugMapResponse(
|
||||
resp *tailcfg.MapResponse,
|
||||
t debugType,
|
||||
|
||||
Reference in New Issue
Block a user