mirror of
https://github.com/juanfont/headscale.git
synced 2026-07-08 00:50:20 +09:00
policy/v2, policy/matcher: consolidate alias and IP-set helpers
This commit is contained in:
@@ -1,38 +1,13 @@
|
||||
package matcher
|
||||
|
||||
import (
|
||||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
"go4.org/netipx"
|
||||
)
|
||||
|
||||
// MatchFromStrings builds a [Match] from raw source and destination
|
||||
// strings. Unparseable entries are silently dropped (fail-open): the
|
||||
// resulting [Match] is narrower than the input described, but never
|
||||
// wider. Callers that need strict validation should pre-validate
|
||||
// their inputs via [util.ParseIPSet].
|
||||
func MatchFromStrings(sources, destinations []string) Match {
|
||||
srcs := new(netipx.IPSetBuilder)
|
||||
dests := new(netipx.IPSetBuilder)
|
||||
|
||||
for _, srcIP := range sources {
|
||||
set, _ := util.ParseIPSet(srcIP, nil)
|
||||
|
||||
srcs.AddSet(set)
|
||||
return Match{
|
||||
srcs: buildIPSet(sources),
|
||||
dests: buildIPSet(destinations),
|
||||
}
|
||||
|
||||
for _, dest := range destinations {
|
||||
set, _ := util.ParseIPSet(dest, nil)
|
||||
|
||||
dests.AddSet(set)
|
||||
}
|
||||
|
||||
srcsSet, _ := srcs.IPSet()
|
||||
destsSet, _ := dests.IPSet()
|
||||
|
||||
match := Match{
|
||||
srcs: srcsSet,
|
||||
dests: destsSet,
|
||||
}
|
||||
|
||||
return match
|
||||
}
|
||||
|
||||
@@ -52,14 +52,8 @@ func MatchesFromFilterRules(rules []tailcfg.FilterRule) []Match {
|
||||
// [policy.ReduceNodes], hiding the cap target
|
||||
// from the source unless a companion IP-level rule also exists.
|
||||
func MatchFromFilterRule(rule tailcfg.FilterRule) Match {
|
||||
srcs := new(netipx.IPSetBuilder)
|
||||
dests := new(netipx.IPSetBuilder)
|
||||
|
||||
for _, srcIP := range rule.SrcIPs {
|
||||
set, _ := util.ParseIPSet(srcIP, nil)
|
||||
srcs.AddSet(set)
|
||||
}
|
||||
|
||||
for _, dp := range rule.DstPorts {
|
||||
set, _ := util.ParseIPSet(dp.IP, nil)
|
||||
dests.AddSet(set)
|
||||
@@ -71,15 +65,31 @@ func MatchFromFilterRule(rule tailcfg.FilterRule) Match {
|
||||
}
|
||||
}
|
||||
|
||||
srcsSet, _ := srcs.IPSet()
|
||||
destsSet, _ := dests.IPSet()
|
||||
|
||||
return Match{
|
||||
srcs: srcsSet,
|
||||
srcs: buildIPSet(rule.SrcIPs),
|
||||
dests: destsSet,
|
||||
}
|
||||
}
|
||||
|
||||
// buildIPSet parses each string via [util.ParseIPSet] and unions the
|
||||
// results into a single [netipx.IPSet]. Unparseable entries are silently
|
||||
// dropped (fail-open): the result is narrower than the input described,
|
||||
// but never wider.
|
||||
func buildIPSet(addrs []string) *netipx.IPSet {
|
||||
builder := new(netipx.IPSetBuilder)
|
||||
|
||||
for _, addr := range addrs {
|
||||
set, _ := util.ParseIPSet(addr, nil)
|
||||
builder.AddSet(set)
|
||||
}
|
||||
|
||||
set, _ := builder.IPSet()
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
func (m *Match) SrcsContainsIPs(ips ...netip.Addr) bool {
|
||||
return slices.ContainsFunc(ips, m.srcs.Contains)
|
||||
}
|
||||
|
||||
@@ -339,6 +339,20 @@ func (pol *Policy) compileOneGrant(
|
||||
return cg, nil
|
||||
}
|
||||
|
||||
// mergeResolvedSrcs merges every prefix from the resolved sources into a
|
||||
// single [resolved] address set.
|
||||
func mergeResolvedSrcs(resolvedSrcs []ResolvedAddresses) (resolved, error) {
|
||||
var b netipx.IPSetBuilder
|
||||
|
||||
for _, ips := range resolvedSrcs {
|
||||
for _, pref := range ips.Prefixes() {
|
||||
b.AddPrefix(pref)
|
||||
}
|
||||
}
|
||||
|
||||
return newResolved(&b)
|
||||
}
|
||||
|
||||
// compileOneViaGrant resolves sources for a via grant and stores the
|
||||
// deferred per-node data. The actual via-node matching and route
|
||||
// intersection happens in [compileViaForNode].
|
||||
@@ -363,15 +377,7 @@ func (pol *Policy) compileOneViaGrant(
|
||||
}
|
||||
|
||||
// Build merged SrcIPs.
|
||||
var srcIPs netipx.IPSetBuilder
|
||||
|
||||
for _, ips := range resolvedSrcs {
|
||||
for _, pref := range ips.Prefixes() {
|
||||
srcIPs.AddPrefix(pref)
|
||||
}
|
||||
}
|
||||
|
||||
srcResolved, err := newResolved(&srcIPs)
|
||||
srcResolved, err := mergeResolvedSrcs(resolvedSrcs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -445,20 +451,8 @@ func buildSrcIPStrings(
|
||||
hasWildcard, hasDangerAll bool,
|
||||
nodes views.Slice[types.NodeView],
|
||||
) []string {
|
||||
var merged netipx.IPSetBuilder
|
||||
|
||||
for _, ips := range resolvedSrcs {
|
||||
for _, pref := range ips.Prefixes() {
|
||||
merged.AddPrefix(pref)
|
||||
}
|
||||
}
|
||||
|
||||
srcResolved, err := newResolved(&merged)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if srcResolved.Empty() {
|
||||
srcResolved, err := mergeResolvedSrcs(resolvedSrcs)
|
||||
if err != nil || srcResolved.Empty() {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -105,9 +105,6 @@ var nodeAttrUnsupportedCaps = map[tailcfg.NodeCapability]string{
|
||||
|
||||
// Policy validation errors.
|
||||
var (
|
||||
ErrUnknownAliasType = errors.New("unknown alias type")
|
||||
ErrUnknownAutoApprover = errors.New("unknown auto approver type")
|
||||
ErrUnknownOwnerType = errors.New("unknown owner type")
|
||||
ErrInvalidUsername = errors.New("username must contain @")
|
||||
ErrUserNotFound = errors.New("user not found")
|
||||
ErrMultipleUsersFound = errors.New("multiple users found")
|
||||
@@ -145,8 +142,6 @@ var (
|
||||
ErrHostNotDefined = errors.New("host not defined in policy")
|
||||
ErrSSHSourceAliasNotSupported = errors.New("alias not supported for SSH source")
|
||||
ErrSSHDestAliasNotSupported = errors.New("alias not supported for SSH destination")
|
||||
ErrUnknownSSHDestAlias = errors.New("unknown SSH destination alias type")
|
||||
ErrUnknownSSHSrcAlias = errors.New("unknown SSH source alias type")
|
||||
ErrUnknownField = errors.New("unknown field")
|
||||
ErrProtocolNoSpecificPorts = errors.New("protocol does not support specific ports")
|
||||
ErrTestEmptyAssertions = errors.New("test entry must have at least one of \"accept\" or \"deny\"")
|
||||
@@ -263,26 +258,7 @@ func (a AliasWithPorts) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`""`), nil
|
||||
}
|
||||
|
||||
var alias string
|
||||
|
||||
switch v := a.Alias.(type) {
|
||||
case *Username:
|
||||
alias = string(*v)
|
||||
case *Group:
|
||||
alias = string(*v)
|
||||
case *Tag:
|
||||
alias = string(*v)
|
||||
case *Host:
|
||||
alias = string(*v)
|
||||
case *Prefix:
|
||||
alias = v.String()
|
||||
case *AutoGroup:
|
||||
alias = string(*v)
|
||||
case Asterix:
|
||||
alias = "*"
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: %T", ErrUnknownAliasType, v)
|
||||
}
|
||||
alias := a.String()
|
||||
|
||||
// If no ports are specified
|
||||
if len(a.Ports) == 0 {
|
||||
@@ -1133,24 +1109,7 @@ func (a *Aliases) MarshalJSON() ([]byte, error) {
|
||||
|
||||
aliases := make([]string, len(*a))
|
||||
for i, alias := range *a {
|
||||
switch v := alias.(type) {
|
||||
case *Username:
|
||||
aliases[i] = string(*v)
|
||||
case *Group:
|
||||
aliases[i] = string(*v)
|
||||
case *Tag:
|
||||
aliases[i] = string(*v)
|
||||
case *Host:
|
||||
aliases[i] = string(*v)
|
||||
case *Prefix:
|
||||
aliases[i] = v.String()
|
||||
case *AutoGroup:
|
||||
aliases[i] = string(*v)
|
||||
case Asterix:
|
||||
aliases[i] = "*"
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: %T", ErrUnknownAliasType, v)
|
||||
}
|
||||
aliases[i] = alias.String()
|
||||
}
|
||||
|
||||
return json.Marshal(aliases)
|
||||
@@ -1227,16 +1186,7 @@ func (aa AutoApprovers) MarshalJSON() ([]byte, error) {
|
||||
|
||||
approvers := make([]string, len(aa))
|
||||
for i, approver := range aa {
|
||||
switch v := approver.(type) {
|
||||
case *Username:
|
||||
approvers[i] = string(*v)
|
||||
case *Tag:
|
||||
approvers[i] = string(*v)
|
||||
case *Group:
|
||||
approvers[i] = string(*v)
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: %T", ErrUnknownAutoApprover, v)
|
||||
}
|
||||
approvers[i] = approver.String()
|
||||
}
|
||||
|
||||
return json.Marshal(approvers)
|
||||
@@ -1321,16 +1271,7 @@ func (o Owners) MarshalJSON() ([]byte, error) {
|
||||
|
||||
owners := make([]string, len(o))
|
||||
for i, owner := range o {
|
||||
switch v := owner.(type) {
|
||||
case *Username:
|
||||
owners[i] = string(*v)
|
||||
case *Group:
|
||||
owners[i] = string(*v)
|
||||
case *Tag:
|
||||
owners[i] = string(*v)
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: %T", ErrUnknownOwnerType, v)
|
||||
}
|
||||
owners[i] = owner.String()
|
||||
}
|
||||
|
||||
return json.Marshal(owners)
|
||||
@@ -1525,16 +1466,7 @@ func (to TagOwners) MarshalJSON() ([]byte, error) {
|
||||
ownerStrs := make([]string, len(owners))
|
||||
|
||||
for i, owner := range owners {
|
||||
switch v := owner.(type) {
|
||||
case *Username:
|
||||
ownerStrs[i] = string(*v)
|
||||
case *Group:
|
||||
ownerStrs[i] = string(*v)
|
||||
case *Tag:
|
||||
ownerStrs[i] = string(*v)
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: %T", ErrUnknownOwnerType, v)
|
||||
}
|
||||
ownerStrs[i] = owner.String()
|
||||
}
|
||||
|
||||
rawTagOwners[tagStr] = ownerStrs
|
||||
@@ -3021,22 +2953,9 @@ func (a SSHDstAliases) MarshalJSON() ([]byte, error) {
|
||||
|
||||
aliases := make([]string, len(a))
|
||||
for i, alias := range a {
|
||||
switch v := alias.(type) {
|
||||
case *Username:
|
||||
aliases[i] = string(*v)
|
||||
case *Tag:
|
||||
aliases[i] = string(*v)
|
||||
case *AutoGroup:
|
||||
aliases[i] = string(*v)
|
||||
case *Host:
|
||||
aliases[i] = string(*v)
|
||||
case Asterix:
|
||||
// Marshal wildcard as "*" so it gets rejected during unmarshal
|
||||
// with a proper error message explaining alternatives
|
||||
aliases[i] = "*"
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: %T", ErrUnknownSSHDestAlias, v)
|
||||
}
|
||||
// A wildcard renders as "*" so it gets rejected during unmarshal
|
||||
// with a proper error message explaining alternatives.
|
||||
aliases[i] = alias.String()
|
||||
}
|
||||
|
||||
return json.Marshal(aliases)
|
||||
@@ -3050,20 +2969,7 @@ func (a *SSHSrcAliases) MarshalJSON() ([]byte, error) {
|
||||
|
||||
aliases := make([]string, len(*a))
|
||||
for i, alias := range *a {
|
||||
switch v := alias.(type) {
|
||||
case *Username:
|
||||
aliases[i] = string(*v)
|
||||
case *Group:
|
||||
aliases[i] = string(*v)
|
||||
case *Tag:
|
||||
aliases[i] = string(*v)
|
||||
case *AutoGroup:
|
||||
aliases[i] = string(*v)
|
||||
case Asterix:
|
||||
aliases[i] = "*"
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: %T", ErrUnknownSSHSrcAlias, v)
|
||||
}
|
||||
aliases[i] = alias.String()
|
||||
}
|
||||
|
||||
return json.Marshal(aliases)
|
||||
|
||||
Reference in New Issue
Block a user