mirror of
https://github.com/juanfont/headscale.git
synced 2026-04-23 17:18:50 +02:00
policy: include IPv6 in identity-based alias resolution
AppendToIPSet now adds both IPv4 and IPv6 addresses for nodes, matching Tailscale's FilterRule wire format where identity-based aliases (tags, users, groups, autogroups) resolve to both address families. Address-based aliases (raw IPs, host names) are unchanged: they resolve to exactly the literal prefix. The appendIfNodeHasIP helper that incorrectly expanded address aliases to include the matching node's other IPs is removed, fixing the RAW_IPV6_ADDR_EXPANSION bug where a raw fd7a: IPv6 address would incorrectly include the node's IPv4. Updates #2180
This commit is contained in:
@@ -578,7 +578,7 @@ func (h *Host) Resolve(p *Policy, _ types.Users, nodes views.Slice[types.NodeVie
|
|||||||
return newResolvedAddresses(h.resolve(p, nil, nodes))
|
return newResolvedAddresses(h.resolve(p, nil, nodes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Host) resolve(p *Policy, _ types.Users, nodes views.Slice[types.NodeView]) (*netipx.IPSet, error) {
|
func (h *Host) resolve(p *Policy, _ types.Users, _ views.Slice[types.NodeView]) (*netipx.IPSet, error) {
|
||||||
var (
|
var (
|
||||||
ips netipx.IPSetBuilder
|
ips netipx.IPSetBuilder
|
||||||
errs []error
|
errs []error
|
||||||
@@ -594,25 +594,11 @@ func (h *Host) resolve(p *Policy, _ types.Users, nodes views.Slice[types.NodeVie
|
|||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Address-based aliases (host names) resolve to exactly the
|
||||||
|
// literal prefix from the hosts map. They do NOT expand to
|
||||||
|
// include the matching node's other IP addresses.
|
||||||
ips.AddPrefix(netip.Prefix(pref))
|
ips.AddPrefix(netip.Prefix(pref))
|
||||||
|
|
||||||
// If the IP is a single host, look for a node to ensure we add all the IPs of
|
|
||||||
// the node to the IPSet.
|
|
||||||
appendIfNodeHasIP(nodes, &ips, netip.Prefix(pref))
|
|
||||||
|
|
||||||
// TODO(kradalby): I am a bit unsure what is the correct way to do this,
|
|
||||||
// should a host with a non single IP be able to resolve the full host (inc all IPs).
|
|
||||||
ipsTemp, err := ips.IPSet()
|
|
||||||
if err != nil {
|
|
||||||
errs = append(errs, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, node := range nodes.All() {
|
|
||||||
if node.InIPSet(ipsTemp) {
|
|
||||||
node.AppendToIPSet(&ips)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buildIPSetMultiErr(&ips, errs)
|
return buildIPSetMultiErr(&ips, errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -679,34 +665,20 @@ func (p *Prefix) Resolve(_ *Policy, _ types.Users, nodes views.Slice[types.NodeV
|
|||||||
return newResolvedAddresses(p.resolve(nil, nil, nodes))
|
return newResolvedAddresses(p.resolve(nil, nil, nodes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Prefix) resolve(_ *Policy, _ types.Users, nodes views.Slice[types.NodeView]) (*netipx.IPSet, error) {
|
func (p *Prefix) resolve(_ *Policy, _ types.Users, _ views.Slice[types.NodeView]) (*netipx.IPSet, error) {
|
||||||
var (
|
var (
|
||||||
ips netipx.IPSetBuilder
|
ips netipx.IPSetBuilder
|
||||||
errs []error
|
errs []error
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Address-based aliases resolve to exactly the literal prefix.
|
||||||
|
// Unlike identity-based aliases (tags, users, groups), they do
|
||||||
|
// NOT expand to include the matching node's other IP addresses.
|
||||||
ips.AddPrefix(netip.Prefix(*p))
|
ips.AddPrefix(netip.Prefix(*p))
|
||||||
// If the IP is a single host, look for a node to ensure we add all the IPs of
|
|
||||||
// the node to the IPSet.
|
|
||||||
appendIfNodeHasIP(nodes, &ips, netip.Prefix(*p))
|
|
||||||
|
|
||||||
return buildIPSetMultiErr(&ips, errs)
|
return buildIPSetMultiErr(&ips, errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendIfNodeHasIP appends the IPs of the nodes to the IPSet if the node has the
|
|
||||||
// IP address in the prefix.
|
|
||||||
func appendIfNodeHasIP(nodes views.Slice[types.NodeView], ips *netipx.IPSetBuilder, pref netip.Prefix) {
|
|
||||||
if !pref.IsSingleIP() && !tsaddr.IsTailscaleIP(pref.Addr()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, node := range nodes.All() {
|
|
||||||
if node.HasIP(pref.Addr()) {
|
|
||||||
node.AppendToIPSet(ips)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AutoGroup is a special string which is always prefixed with `autogroup:`.
|
// AutoGroup is a special string which is always prefixed with `autogroup:`.
|
||||||
type AutoGroup string
|
type AutoGroup string
|
||||||
|
|
||||||
|
|||||||
@@ -297,25 +297,18 @@ func (node *Node) InIPSet(set *netipx.IPSet) bool {
|
|||||||
return slices.ContainsFunc(node.IPs(), set.Contains)
|
return slices.ContainsFunc(node.IPs(), set.Contains)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendToIPSet adds the individual ips in NodeAddresses to a
|
// AppendToIPSet adds all IP addresses of the node to the given
|
||||||
// given netipx.IPSetBuilder.
|
// netipx.IPSetBuilder. For identity-based aliases (tags, users,
|
||||||
|
// groups, autogroups), both IPv4 and IPv6 must be included to
|
||||||
|
// match Tailscale's behavior in the FilterRule wire format.
|
||||||
func (node *Node) AppendToIPSet(build *netipx.IPSetBuilder) {
|
func (node *Node) AppendToIPSet(build *netipx.IPSetBuilder) {
|
||||||
if node.IPv4 != nil {
|
if node.IPv4 != nil {
|
||||||
build.Add(*node.IPv4)
|
build.Add(*node.IPv4)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.IPv6 != nil {
|
if node.IPv6 != nil {
|
||||||
build.Add(*node.IPv6)
|
build.Add(*node.IPv6)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(kradalby): Evaluate what we want to do here:
|
|
||||||
// Tailscale only adds the IPv4 addresses to any packet filter rule that is resolved to a given node.
|
|
||||||
// Presumably, it will add the IPv4 if a node does not have an IPv4.
|
|
||||||
// Until this change, we always added both, and that might be something people are dependent on, and we might want to keep it.
|
|
||||||
// for _, ip := range node.IPs() {
|
|
||||||
// build.Add(ip)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node *Node) CanAccess(matchers []matcher.Match, node2 *Node) bool {
|
func (node *Node) CanAccess(matchers []matcher.Match, node2 *Node) bool {
|
||||||
|
|||||||
Reference in New Issue
Block a user