mirror of
https://github.com/juanfont/headscale.git
synced 2024-11-29 18:33:05 +00:00
testing without that horrible filtercode
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
parent
1700a747f6
commit
889d5a1b29
6 changed files with 510 additions and 345 deletions
263
acls.go
263
acls.go
|
@ -132,16 +132,17 @@ func (h *Headscale) UpdateACLRules() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace().Interface("ACL", rules).Msg("ACL rules generated")
|
log.Trace().Interface("ACL", rules).Msg("ACL rules generated")
|
||||||
h.aclRules = rules
|
h.aclRules = rules
|
||||||
|
|
||||||
// Precompute a map of which sources can reach each destination, this is
|
// Precompute a map of which sources can reach each destination, this is
|
||||||
// to provide quicker lookup when we calculate the peerlist for the map
|
// to provide quicker lookup when we calculate the peerlist for the map
|
||||||
// response to nodes.
|
// response to nodes.
|
||||||
aclPeerCacheMap := generateACLPeerCacheMap(rules)
|
// aclPeerCacheMap := generateACLPeerCacheMap(rules)
|
||||||
h.aclPeerCacheMapRW.Lock()
|
// h.aclPeerCacheMapRW.Lock()
|
||||||
h.aclPeerCacheMap = aclPeerCacheMap
|
// h.aclPeerCacheMap = aclPeerCacheMap
|
||||||
h.aclPeerCacheMapRW.Unlock()
|
// h.aclPeerCacheMapRW.Unlock()
|
||||||
|
|
||||||
if featureEnableSSH() {
|
if featureEnableSSH() {
|
||||||
sshRules, err := h.generateSSHRules()
|
sshRules, err := h.generateSSHRules()
|
||||||
|
@ -160,69 +161,69 @@ func (h *Headscale) UpdateACLRules() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateACLPeerCacheMap takes a list of Tailscale filter rules and generates a map
|
// // generateACLPeerCacheMap takes a list of Tailscale filter rules and generates a map
|
||||||
// of which Sources ("*" and IPs) can access destinations. This is to speed up the
|
// // of which Sources ("*" and IPs) can access destinations. This is to speed up the
|
||||||
// process of generating MapResponses when deciding which Peers to inform nodes about.
|
// // process of generating MapResponses when deciding which Peers to inform nodes about.
|
||||||
func generateACLPeerCacheMap(rules []tailcfg.FilterRule) map[string][]string {
|
// func generateACLPeerCacheMap(rules []tailcfg.FilterRule) map[string][]string {
|
||||||
aclCachePeerMap := make(map[string][]string)
|
// aclCachePeerMap := make(map[string][]string)
|
||||||
for _, rule := range rules {
|
// for _, rule := range rules {
|
||||||
for _, srcIP := range rule.SrcIPs {
|
// for _, srcIP := range rule.SrcIPs {
|
||||||
for _, ip := range expandACLPeerAddr(srcIP) {
|
// for _, ip := range expandACLPeerAddr(srcIP) {
|
||||||
if data, ok := aclCachePeerMap[ip]; ok {
|
// if data, ok := aclCachePeerMap[ip]; ok {
|
||||||
for _, dstPort := range rule.DstPorts {
|
// for _, dstPort := range rule.DstPorts {
|
||||||
data = append(data, dstPort.IP)
|
// data = append(data, dstPort.IP)
|
||||||
}
|
// }
|
||||||
aclCachePeerMap[ip] = data
|
// aclCachePeerMap[ip] = data
|
||||||
} else {
|
// } else {
|
||||||
dstPortsMap := make([]string, 0)
|
// dstPortsMap := make([]string, 0)
|
||||||
for _, dstPort := range rule.DstPorts {
|
// for _, dstPort := range rule.DstPorts {
|
||||||
dstPortsMap = append(dstPortsMap, dstPort.IP)
|
// dstPortsMap = append(dstPortsMap, dstPort.IP)
|
||||||
}
|
// }
|
||||||
aclCachePeerMap[ip] = dstPortsMap
|
// aclCachePeerMap[ip] = dstPortsMap
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
log.Trace().Interface("ACL Cache Map", aclCachePeerMap).Msg("ACL Peer Cache Map generated")
|
// log.Trace().Interface("ACL Cache Map", aclCachePeerMap).Msg("ACL Peer Cache Map generated")
|
||||||
|
//
|
||||||
return aclCachePeerMap
|
// return aclCachePeerMap
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// expandACLPeerAddr takes a "tailcfg.FilterRule" "IP" and expands it into
|
// // expandACLPeerAddr takes a "tailcfg.FilterRule" "IP" and expands it into
|
||||||
// something our cache logic can look up, which is "*" or single IP addresses.
|
// // something our cache logic can look up, which is "*" or single IP addresses.
|
||||||
// This is probably quite inefficient, but it is a result of
|
// // This is probably quite inefficient, but it is a result of
|
||||||
// "make it work, then make it fast", and a lot of the ACL stuff does not
|
// // "make it work, then make it fast", and a lot of the ACL stuff does not
|
||||||
// work, but people have tried to make it fast.
|
// // work, but people have tried to make it fast.
|
||||||
func expandACLPeerAddr(srcIP string) []string {
|
// func expandACLPeerAddr(srcIP string) []string {
|
||||||
if ip, err := netip.ParseAddr(srcIP); err == nil {
|
// if ip, err := netip.ParseAddr(srcIP); err == nil {
|
||||||
return []string{ip.String()}
|
// return []string{ip.String()}
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if cidr, err := netip.ParsePrefix(srcIP); err == nil {
|
// if cidr, err := netip.ParsePrefix(srcIP); err == nil {
|
||||||
addrs := []string{}
|
// addrs := []string{}
|
||||||
|
//
|
||||||
ipRange := netipx.RangeOfPrefix(cidr)
|
// ipRange := netipx.RangeOfPrefix(cidr)
|
||||||
|
//
|
||||||
from := ipRange.From()
|
// from := ipRange.From()
|
||||||
too := ipRange.To()
|
// too := ipRange.To()
|
||||||
|
//
|
||||||
if from == too {
|
// if from == too {
|
||||||
return []string{from.String()}
|
// return []string{from.String()}
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
for from != too && from.Less(too) {
|
// for from != too && from.Less(too) {
|
||||||
addrs = append(addrs, from.String())
|
// addrs = append(addrs, from.String())
|
||||||
from = from.Next()
|
// from = from.Next()
|
||||||
}
|
// }
|
||||||
addrs = append(addrs, too.String()) // Add the last IP address in the range
|
// addrs = append(addrs, too.String()) // Add the last IP address in the range
|
||||||
|
//
|
||||||
return addrs
|
// return addrs
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// probably "*" or other string based "IP"
|
// // probably "*" or other string based "IP"
|
||||||
return []string{srcIP}
|
// return []string{srcIP}
|
||||||
}
|
// }
|
||||||
|
|
||||||
// generateFilterRules takes a set of machines and an ACLPolicy and generates a
|
// generateFilterRules takes a set of machines and an ACLPolicy and generates a
|
||||||
// set of Tailscale compatible FilterRules used to allow traffic on clients.
|
// set of Tailscale compatible FilterRules used to allow traffic on clients.
|
||||||
|
@ -879,3 +880,131 @@ func (pol *ACLPolicy) getIPsFromIPPrefix(
|
||||||
|
|
||||||
return lo.Uniq(val), nil
|
return lo.Uniq(val), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is borrowed from
|
||||||
|
// https://github.com/tailscale/tailscale/blob/71029cea2ddf82007b80f465b256d027eab0f02d/wgengine/filter/tailcfg.go#L97-L162
|
||||||
|
var (
|
||||||
|
zeroIP4 = netip.AddrFrom4([4]byte{})
|
||||||
|
zeroIP6 = netip.AddrFrom16([16]byte{})
|
||||||
|
)
|
||||||
|
|
||||||
|
// parseIPSet parses arg as one:
|
||||||
|
//
|
||||||
|
// - an IP address (IPv4 or IPv6)
|
||||||
|
// - the string "*" to match everything (both IPv4 & IPv6)
|
||||||
|
// - a CIDR (e.g. "192.168.0.0/16")
|
||||||
|
// - a range of two IPs, inclusive, separated by hyphen ("2eff::1-2eff::0800")
|
||||||
|
//
|
||||||
|
// bits, if non-nil, is the legacy SrcBits CIDR length to make a IP
|
||||||
|
// address (without a slash) treated as a CIDR of *bits length.
|
||||||
|
//
|
||||||
|
// TODO(bradfitz): make this return an IPSet and plumb that all
|
||||||
|
// around, and ultimately use a new version of IPSet.ContainsFunc like
|
||||||
|
// Contains16Func that works in [16]byte address, so we we can match
|
||||||
|
// at runtime without allocating?
|
||||||
|
func parseIPSet(arg string, bits *int) ([]netip.Prefix, error) {
|
||||||
|
if arg == "*" {
|
||||||
|
// User explicitly requested wildcard.
|
||||||
|
return []netip.Prefix{
|
||||||
|
netip.PrefixFrom(zeroIP4, 0),
|
||||||
|
netip.PrefixFrom(zeroIP6, 0),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
if strings.Contains(arg, "/") {
|
||||||
|
pfx, err := netip.ParsePrefix(arg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if pfx != pfx.Masked() {
|
||||||
|
return nil, fmt.Errorf("%v contains non-network bits set", pfx)
|
||||||
|
}
|
||||||
|
return []netip.Prefix{pfx}, nil
|
||||||
|
}
|
||||||
|
if strings.Count(arg, "-") == 1 {
|
||||||
|
ip1s, ip2s, _ := strings.Cut(arg, "-")
|
||||||
|
ip1, err := netip.ParseAddr(ip1s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ip2, err := netip.ParseAddr(ip2s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r := netipx.IPRangeFrom(ip1, ip2)
|
||||||
|
if !r.Valid() {
|
||||||
|
return nil, fmt.Errorf("invalid IP range %q", arg)
|
||||||
|
}
|
||||||
|
return r.Prefixes(), nil
|
||||||
|
}
|
||||||
|
ip, err := netip.ParseAddr(arg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid IP address %q", arg)
|
||||||
|
}
|
||||||
|
bits8 := uint8(ip.BitLen())
|
||||||
|
if bits != nil {
|
||||||
|
if *bits < 0 || *bits > int(bits8) {
|
||||||
|
return nil, fmt.Errorf("invalid CIDR size %d for IP %q", *bits, arg)
|
||||||
|
}
|
||||||
|
bits8 = uint8(*bits)
|
||||||
|
}
|
||||||
|
return []netip.Prefix{netip.PrefixFrom(ip, int(bits8))}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ipInPrefixList(ip netip.Addr, netlist []netip.Prefix) bool {
|
||||||
|
for _, net := range netlist {
|
||||||
|
if net.Contains(ip) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type Match struct {
|
||||||
|
Srcs []netip.Prefix
|
||||||
|
Dests []netip.Prefix
|
||||||
|
}
|
||||||
|
|
||||||
|
func MatchFromFilterRule(rule tailcfg.FilterRule) Match {
|
||||||
|
match := Match{
|
||||||
|
Srcs: []netip.Prefix{},
|
||||||
|
Dests: []netip.Prefix{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, srcIP := range rule.SrcIPs {
|
||||||
|
prefix, _ := parseIPSet(srcIP, nil)
|
||||||
|
|
||||||
|
match.Srcs = append(match.Srcs, prefix...)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dest := range rule.DstPorts {
|
||||||
|
prefix, _ := parseIPSet(dest.IP, nil)
|
||||||
|
|
||||||
|
match.Dests = append(match.Dests, prefix...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Match) SrcsContainsIPs(ips []netip.Addr) bool {
|
||||||
|
for _, prefix := range m.Srcs {
|
||||||
|
for _, ip := range ips {
|
||||||
|
if prefix.Contains(ip) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Match) DestsContainsIP(ips []netip.Addr) bool {
|
||||||
|
for _, prefix := range m.Dests {
|
||||||
|
for _, ip := range ips {
|
||||||
|
if prefix.Contains(ip) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
284
acls_test.go
284
acls_test.go
|
@ -1661,140 +1661,140 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_expandACLPeerAddr(t *testing.T) {
|
// func Test_expandACLPeerAddr(t *testing.T) {
|
||||||
type args struct {
|
// type args struct {
|
||||||
srcIP string
|
// srcIP string
|
||||||
}
|
// }
|
||||||
tests := []struct {
|
// tests := []struct {
|
||||||
name string
|
// name string
|
||||||
args args
|
// args args
|
||||||
want []string
|
// want []string
|
||||||
}{
|
// }{
|
||||||
{
|
// {
|
||||||
name: "asterix",
|
// name: "asterix",
|
||||||
args: args{
|
// args: args{
|
||||||
srcIP: "*",
|
// srcIP: "*",
|
||||||
},
|
// },
|
||||||
want: []string{"*"},
|
// want: []string{"*"},
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
name: "ip",
|
// name: "ip",
|
||||||
args: args{
|
// args: args{
|
||||||
srcIP: "10.0.0.1",
|
// srcIP: "10.0.0.1",
|
||||||
},
|
// },
|
||||||
want: []string{"10.0.0.1"},
|
// want: []string{"10.0.0.1"},
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
name: "ip/32",
|
// name: "ip/32",
|
||||||
args: args{
|
// args: args{
|
||||||
srcIP: "10.0.0.1/32",
|
// srcIP: "10.0.0.1/32",
|
||||||
},
|
// },
|
||||||
want: []string{"10.0.0.1"},
|
// want: []string{"10.0.0.1"},
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
name: "ip/30",
|
// name: "ip/30",
|
||||||
args: args{
|
// args: args{
|
||||||
srcIP: "10.0.0.1/30",
|
// srcIP: "10.0.0.1/30",
|
||||||
},
|
// },
|
||||||
want: []string{
|
// want: []string{
|
||||||
"10.0.0.0",
|
// "10.0.0.0",
|
||||||
"10.0.0.1",
|
// "10.0.0.1",
|
||||||
"10.0.0.2",
|
// "10.0.0.2",
|
||||||
"10.0.0.3",
|
// "10.0.0.3",
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
name: "ip/28",
|
// name: "ip/28",
|
||||||
args: args{
|
// args: args{
|
||||||
srcIP: "192.168.0.128/28",
|
// srcIP: "192.168.0.128/28",
|
||||||
},
|
// },
|
||||||
want: []string{
|
// want: []string{
|
||||||
"192.168.0.128", "192.168.0.129", "192.168.0.130",
|
// "192.168.0.128", "192.168.0.129", "192.168.0.130",
|
||||||
"192.168.0.131", "192.168.0.132", "192.168.0.133",
|
// "192.168.0.131", "192.168.0.132", "192.168.0.133",
|
||||||
"192.168.0.134", "192.168.0.135", "192.168.0.136",
|
// "192.168.0.134", "192.168.0.135", "192.168.0.136",
|
||||||
"192.168.0.137", "192.168.0.138", "192.168.0.139",
|
// "192.168.0.137", "192.168.0.138", "192.168.0.139",
|
||||||
"192.168.0.140", "192.168.0.141", "192.168.0.142",
|
// "192.168.0.140", "192.168.0.141", "192.168.0.142",
|
||||||
"192.168.0.143",
|
// "192.168.0.143",
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
}
|
// }
|
||||||
for _, tt := range tests {
|
// for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
// t.Run(tt.name, func(t *testing.T) {
|
||||||
if got := expandACLPeerAddr(tt.args.srcIP); !reflect.DeepEqual(got, tt.want) {
|
// if got := expandACLPeerAddr(tt.args.srcIP); !reflect.DeepEqual(got, tt.want) {
|
||||||
t.Errorf("expandACLPeerAddr() = %v, want %v", got, tt.want)
|
// t.Errorf("expandACLPeerAddr() = %v, want %v", got, tt.want)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
func Test_expandACLPeerAddrV6(t *testing.T) {
|
// func Test_expandACLPeerAddrV6(t *testing.T) {
|
||||||
type args struct {
|
// type args struct {
|
||||||
srcIP string
|
// srcIP string
|
||||||
}
|
// }
|
||||||
tests := []struct {
|
// tests := []struct {
|
||||||
name string
|
// name string
|
||||||
args args
|
// args args
|
||||||
want []string
|
// want []string
|
||||||
}{
|
// }{
|
||||||
{
|
// {
|
||||||
name: "asterix",
|
// name: "asterix",
|
||||||
args: args{
|
// args: args{
|
||||||
srcIP: "*",
|
// srcIP: "*",
|
||||||
},
|
// },
|
||||||
want: []string{"*"},
|
// want: []string{"*"},
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
name: "ipfull",
|
// name: "ipfull",
|
||||||
args: args{
|
// args: args{
|
||||||
srcIP: "fd7a:115c:a1e0:ab12:4943:cd96:624c:3166",
|
// srcIP: "fd7a:115c:a1e0:ab12:4943:cd96:624c:3166",
|
||||||
},
|
// },
|
||||||
want: []string{"fd7a:115c:a1e0:ab12:4943:cd96:624c:3166"},
|
// want: []string{"fd7a:115c:a1e0:ab12:4943:cd96:624c:3166"},
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
name: "ipzerocompression",
|
// name: "ipzerocompression",
|
||||||
args: args{
|
// args: args{
|
||||||
srcIP: "fd7a:115c:a1e0:ab12:4943:cd96:624c::",
|
// srcIP: "fd7a:115c:a1e0:ab12:4943:cd96:624c::",
|
||||||
},
|
// },
|
||||||
want: []string{"fd7a:115c:a1e0:ab12:4943:cd96:624c:0"},
|
// want: []string{"fd7a:115c:a1e0:ab12:4943:cd96:624c:0"},
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
name: "ip/128",
|
// name: "ip/128",
|
||||||
args: args{
|
// args: args{
|
||||||
srcIP: "fd7a:115c:a1e0:ab12:4943:cd96:624c:3166/128",
|
// srcIP: "fd7a:115c:a1e0:ab12:4943:cd96:624c:3166/128",
|
||||||
},
|
// },
|
||||||
want: []string{"fd7a:115c:a1e0:ab12:4943:cd96:624c:3166"},
|
// want: []string{"fd7a:115c:a1e0:ab12:4943:cd96:624c:3166"},
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
name: "ip/127",
|
// name: "ip/127",
|
||||||
args: args{
|
// args: args{
|
||||||
srcIP: "fd7a:115c:a1e0:ab12:4943:cd96:624c:0000/127",
|
// srcIP: "fd7a:115c:a1e0:ab12:4943:cd96:624c:0000/127",
|
||||||
},
|
// },
|
||||||
want: []string{
|
// want: []string{
|
||||||
"fd7a:115c:a1e0:ab12:4943:cd96:624c:0",
|
// "fd7a:115c:a1e0:ab12:4943:cd96:624c:0",
|
||||||
"fd7a:115c:a1e0:ab12:4943:cd96:624c:1",
|
// "fd7a:115c:a1e0:ab12:4943:cd96:624c:1",
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
name: "ip/126",
|
// name: "ip/126",
|
||||||
args: args{
|
// args: args{
|
||||||
srcIP: "fd7a:115c:a1e0:ab12:4943:cd96:624c:0000/126",
|
// srcIP: "fd7a:115c:a1e0:ab12:4943:cd96:624c:0000/126",
|
||||||
},
|
// },
|
||||||
want: []string{
|
// want: []string{
|
||||||
"fd7a:115c:a1e0:ab12:4943:cd96:624c:0",
|
// "fd7a:115c:a1e0:ab12:4943:cd96:624c:0",
|
||||||
"fd7a:115c:a1e0:ab12:4943:cd96:624c:1",
|
// "fd7a:115c:a1e0:ab12:4943:cd96:624c:1",
|
||||||
"fd7a:115c:a1e0:ab12:4943:cd96:624c:2",
|
// "fd7a:115c:a1e0:ab12:4943:cd96:624c:2",
|
||||||
"fd7a:115c:a1e0:ab12:4943:cd96:624c:3",
|
// "fd7a:115c:a1e0:ab12:4943:cd96:624c:3",
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
}
|
// }
|
||||||
for _, tt := range tests {
|
// for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
// t.Run(tt.name, func(t *testing.T) {
|
||||||
if got := expandACLPeerAddr(tt.args.srcIP); !reflect.DeepEqual(got, tt.want) {
|
// if got := expandACLPeerAddr(tt.args.srcIP); !reflect.DeepEqual(got, tt.want) {
|
||||||
t.Errorf("expandACLPeerAddr() = %v, want %v", got, tt.want)
|
// t.Errorf("expandACLPeerAddr() = %v, want %v", got, tt.want)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
func TestACLPolicy_generateFilterRules(t *testing.T) {
|
func TestACLPolicy_generateFilterRules(t *testing.T) {
|
||||||
type field struct {
|
type field struct {
|
||||||
|
@ -1819,7 +1819,7 @@ func TestACLPolicy_generateFilterRules(t *testing.T) {
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "simple group",
|
name: "allow-all",
|
||||||
field: field{
|
field: field{
|
||||||
pol: ACLPolicy{
|
pol: ACLPolicy{
|
||||||
ACLs: []ACL{
|
ACLs: []ACL{
|
||||||
|
@ -1852,7 +1852,7 @@ func TestACLPolicy_generateFilterRules(t *testing.T) {
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "simple host by ipv4 single dual stack",
|
name: "host1-can-reach-host2",
|
||||||
field: field{
|
field: field{
|
||||||
pol: ACLPolicy{
|
pol: ACLPolicy{
|
||||||
ACLs: []ACL{
|
ACLs: []ACL{
|
||||||
|
@ -1868,14 +1868,14 @@ func TestACLPolicy_generateFilterRules(t *testing.T) {
|
||||||
machines: []Machine{
|
machines: []Machine{
|
||||||
{
|
{
|
||||||
IPAddresses: MachineAddresses{
|
IPAddresses: MachineAddresses{
|
||||||
netip.MustParseAddr("10.0.0.1"),
|
netip.MustParseAddr("100.64.0.1"),
|
||||||
netip.MustParseAddr("fd7a:115c:a1e0:ab12:4843:2222:6273:2221"),
|
netip.MustParseAddr("fd7a:115c:a1e0:ab12:4843:2222:6273:2221"),
|
||||||
},
|
},
|
||||||
User: User{Name: "mickael"},
|
User: User{Name: "mickael"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
IPAddresses: MachineAddresses{
|
IPAddresses: MachineAddresses{
|
||||||
netip.MustParseAddr("10.0.0.2"),
|
netip.MustParseAddr("100.64.0.2"),
|
||||||
netip.MustParseAddr("fd7a:115c:a1e0:ab12:4843:2222:6273:2222"),
|
netip.MustParseAddr("fd7a:115c:a1e0:ab12:4843:2222:6273:2222"),
|
||||||
},
|
},
|
||||||
User: User{Name: "mickael"},
|
User: User{Name: "mickael"},
|
||||||
|
@ -1883,10 +1883,9 @@ func TestACLPolicy_generateFilterRules(t *testing.T) {
|
||||||
},
|
},
|
||||||
stripEmailDomain: true,
|
stripEmailDomain: true,
|
||||||
},
|
},
|
||||||
// [{"SrcIPs":["100.64.0.1"],"DstPorts":[{"IP":"100.64.0.2","Bits":null,"Ports":{"First":0,"Last":65535}}]}]
|
|
||||||
want: []tailcfg.FilterRule{
|
want: []tailcfg.FilterRule{
|
||||||
{
|
{
|
||||||
SrcIPs: []string{"100.64.0.1"},
|
SrcIPs: []string{"100.64.0.1", "fd7a:115c:a1e0:ab12:4843:2222:6273:2221"},
|
||||||
DstPorts: []tailcfg.NetPortRange{
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
{
|
{
|
||||||
IP: "100.64.0.2",
|
IP: "100.64.0.2",
|
||||||
|
@ -1895,6 +1894,13 @@ func TestACLPolicy_generateFilterRules(t *testing.T) {
|
||||||
Last: 65535,
|
Last: 65535,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
IP: "fd7a:115c:a1e0:ab12:4843:2222:6273:2222",
|
||||||
|
Ports: tailcfg.PortRange{
|
||||||
|
First: 0,
|
||||||
|
Last: 65535,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
6
go.mod
6
go.mod
|
@ -4,7 +4,6 @@ go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/AlecAivazis/survey/v2 v2.3.6
|
github.com/AlecAivazis/survey/v2 v2.3.6
|
||||||
github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029
|
|
||||||
github.com/cenkalti/backoff/v4 v4.2.0
|
github.com/cenkalti/backoff/v4 v4.2.0
|
||||||
github.com/coreos/go-oidc/v3 v3.5.0
|
github.com/coreos/go-oidc/v3 v3.5.0
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
|
@ -12,6 +11,7 @@ require (
|
||||||
github.com/efekarakus/termcolor v1.0.1
|
github.com/efekarakus/termcolor v1.0.1
|
||||||
github.com/glebarez/sqlite v1.7.0
|
github.com/glebarez/sqlite v1.7.0
|
||||||
github.com/gofrs/uuid/v5 v5.0.0
|
github.com/gofrs/uuid/v5 v5.0.0
|
||||||
|
github.com/google/go-cmp v0.5.9
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
|
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2
|
||||||
|
@ -20,6 +20,7 @@ require (
|
||||||
github.com/ory/dockertest/v3 v3.9.1
|
github.com/ory/dockertest/v3 v3.9.1
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/philip-bui/grpc-zerolog v1.0.1
|
github.com/philip-bui/grpc-zerolog v1.0.1
|
||||||
|
github.com/pkg/profile v1.7.0
|
||||||
github.com/prometheus/client_golang v1.14.0
|
github.com/prometheus/client_golang v1.14.0
|
||||||
github.com/prometheus/common v0.42.0
|
github.com/prometheus/common v0.42.0
|
||||||
github.com/pterm/pterm v0.12.58
|
github.com/pterm/pterm v0.12.58
|
||||||
|
@ -73,7 +74,6 @@ require (
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/google/go-cmp v0.5.9 // indirect
|
|
||||||
github.com/google/go-github v17.0.0+incompatible // indirect
|
github.com/google/go-github v17.0.0+incompatible // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 // indirect
|
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 // indirect
|
||||||
|
@ -113,7 +113,6 @@ require (
|
||||||
github.com/opencontainers/runc v1.1.4 // indirect
|
github.com/opencontainers/runc v1.1.4 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pkg/profile v1.7.0 // indirect
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_model v0.3.0 // indirect
|
github.com/prometheus/client_model v0.3.0 // indirect
|
||||||
github.com/prometheus/procfs v0.9.0 // indirect
|
github.com/prometheus/procfs v0.9.0 // indirect
|
||||||
|
@ -144,6 +143,7 @@ require (
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
gotest.tools/v3 v3.4.0 // indirect
|
||||||
modernc.org/libc v1.22.2 // indirect
|
modernc.org/libc v1.22.2 // indirect
|
||||||
modernc.org/mathutil v1.5.0 // indirect
|
modernc.org/mathutil v1.5.0 // indirect
|
||||||
modernc.org/memory v1.5.0 // indirect
|
modernc.org/memory v1.5.0 // indirect
|
||||||
|
|
5
go.sum
5
go.sum
|
@ -74,8 +74,6 @@ github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkU
|
||||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029 h1:POmUHfxXdeyM8Aomg4tKDcwATCFuW+cYLkj6pwsw9pc=
|
|
||||||
github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029/go.mod h1:Rpr5n9cGHYdM3S3IK8ROSUUUYjQOu+MSUCZDcJbYWi8=
|
|
||||||
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
|
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
|
||||||
github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
@ -902,7 +900,8 @@ gorm.io/driver/postgres v1.4.8/go.mod h1:O9MruWGNLUBUWVYfWuBClpf3HeGjOoybY0SNmCs
|
||||||
gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||||
gorm.io/gorm v1.24.6 h1:wy98aq9oFEetsc4CAbKD2SoBCdMzsbSIvSUUFJuHi5s=
|
gorm.io/gorm v1.24.6 h1:wy98aq9oFEetsc4CAbKD2SoBCdMzsbSIvSUUFJuHi5s=
|
||||||
gorm.io/gorm v1.24.6/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
gorm.io/gorm v1.24.6/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||||
gotest.tools/v3 v3.2.0 h1:I0DwBVMGAx26dttAj1BtJLAkVGncrkkUXfJLC4Flt/I=
|
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
|
||||||
|
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
|
290
machine.go
290
machine.go
|
@ -4,12 +4,10 @@ import (
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||||
|
@ -162,151 +160,189 @@ func (machine *Machine) isEphemeral() bool {
|
||||||
return machine.AuthKey != nil && machine.AuthKey.Ephemeral
|
return machine.AuthKey != nil && machine.AuthKey.Ephemeral
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (machine *Machine) canAccess(filter []tailcfg.FilterRule, machine2 *Machine) bool {
|
||||||
|
for _, rule := range filter {
|
||||||
|
// TODO(kradalby): Cache or pregen this
|
||||||
|
matcher := MatchFromFilterRule(rule)
|
||||||
|
|
||||||
|
if !matcher.SrcsContainsIPs([]netip.Addr(machine.IPAddresses)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if matcher.DestsContainsIP([]netip.Addr(machine2.IPAddresses)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// filterMachinesByACL wrapper function to not have devs pass around locks and maps
|
// filterMachinesByACL wrapper function to not have devs pass around locks and maps
|
||||||
// related to the application outside of tests.
|
// related to the application outside of tests.
|
||||||
func (h *Headscale) filterMachinesByACL(currentMachine *Machine, peers Machines) Machines {
|
func (h *Headscale) filterMachinesByACL(currentMachine *Machine, peers Machines) Machines {
|
||||||
return filterMachinesByACL(currentMachine, peers, &h.aclPeerCacheMapRW, h.aclPeerCacheMap)
|
return filterMachinesByACL(currentMachine, peers, h.aclRules)
|
||||||
}
|
}
|
||||||
|
|
||||||
// filterMachinesByACL returns the list of peers authorized to be accessed from a given machine.
|
// filterMachinesByACL returns the list of peers authorized to be accessed from a given machine.
|
||||||
func filterMachinesByACL(
|
func filterMachinesByACL(
|
||||||
machine *Machine,
|
machine *Machine,
|
||||||
machines Machines,
|
machines Machines,
|
||||||
lock *sync.RWMutex,
|
filter []tailcfg.FilterRule,
|
||||||
aclPeerCacheMap map[string][]string,
|
|
||||||
) Machines {
|
) Machines {
|
||||||
log.Trace().
|
result := Machines{}
|
||||||
Caller().
|
|
||||||
Str("self", machine.Hostname).
|
|
||||||
Str("input", machines.String()).
|
|
||||||
Msg("Finding peers filtered by ACLs")
|
|
||||||
|
|
||||||
peers := make(map[uint64]Machine)
|
for index, peer := range machines {
|
||||||
// Aclfilter peers here. We are itering through machines in all users and search through the computed aclRules
|
|
||||||
// for match between rule SrcIPs and DstPorts. If the rule is a match we allow the machine to be viewable.
|
|
||||||
machineIPs := machine.IPAddresses.ToStringSlice()
|
|
||||||
|
|
||||||
// TODO(kradalby): Remove this lock, I suspect its not a good idea, and might not be necessary,
|
|
||||||
// we only set this at startup atm (reading ACLs) and it might become a bottleneck.
|
|
||||||
lock.RLock()
|
|
||||||
|
|
||||||
for _, peer := range machines {
|
|
||||||
if peer.ID == machine.ID {
|
if peer.ID == machine.ID {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
peerIPs := peer.IPAddresses.ToStringSlice()
|
|
||||||
|
|
||||||
if dstMap, ok := aclPeerCacheMap["*"]; ok {
|
if machine.canAccess(filter, &machines[index]) || peer.canAccess(filter, machine) {
|
||||||
// match source and all destination
|
result = append(result, peer)
|
||||||
|
|
||||||
for _, dst := range dstMap {
|
|
||||||
if dst == "*" {
|
|
||||||
peers[peer.ID] = peer
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// match source and all destination
|
|
||||||
for _, peerIP := range peerIPs {
|
|
||||||
for _, dst := range dstMap {
|
|
||||||
_, cdr, _ := net.ParseCIDR(dst)
|
|
||||||
ip := net.ParseIP(peerIP)
|
|
||||||
if dst == peerIP || (cdr != nil && ip != nil && cdr.Contains(ip)) {
|
|
||||||
peers[peer.ID] = peer
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// match all sources and source
|
|
||||||
for _, machineIP := range machineIPs {
|
|
||||||
for _, dst := range dstMap {
|
|
||||||
_, cdr, _ := net.ParseCIDR(dst)
|
|
||||||
ip := net.ParseIP(machineIP)
|
|
||||||
if dst == machineIP || (cdr != nil && ip != nil && cdr.Contains(ip)) {
|
|
||||||
peers[peer.ID] = peer
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, machineIP := range machineIPs {
|
|
||||||
if dstMap, ok := aclPeerCacheMap[machineIP]; ok {
|
|
||||||
// match source and all destination
|
|
||||||
for _, dst := range dstMap {
|
|
||||||
if dst == "*" {
|
|
||||||
peers[peer.ID] = peer
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// match source and destination
|
|
||||||
for _, peerIP := range peerIPs {
|
|
||||||
for _, dst := range dstMap {
|
|
||||||
_, cdr, _ := net.ParseCIDR(dst)
|
|
||||||
ip := net.ParseIP(peerIP)
|
|
||||||
if dst == peerIP || (cdr != nil && ip != nil && cdr.Contains(ip)) {
|
|
||||||
peers[peer.ID] = peer
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, peerIP := range peerIPs {
|
|
||||||
if dstMap, ok := aclPeerCacheMap[peerIP]; ok {
|
|
||||||
// match source and all destination
|
|
||||||
for _, dst := range dstMap {
|
|
||||||
if dst == "*" {
|
|
||||||
peers[peer.ID] = peer
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// match return path
|
|
||||||
for _, machineIP := range machineIPs {
|
|
||||||
for _, dst := range dstMap {
|
|
||||||
_, cdr, _ := net.ParseCIDR(dst)
|
|
||||||
ip := net.ParseIP(machineIP)
|
|
||||||
if dst == machineIP || (cdr != nil && ip != nil && cdr.Contains(ip)) {
|
|
||||||
peers[peer.ID] = peer
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lock.RUnlock()
|
return result
|
||||||
|
|
||||||
authorizedPeers := make(Machines, 0, len(peers))
|
|
||||||
for _, m := range peers {
|
|
||||||
authorizedPeers = append(authorizedPeers, m)
|
|
||||||
}
|
|
||||||
sort.Slice(
|
|
||||||
authorizedPeers,
|
|
||||||
func(i, j int) bool { return authorizedPeers[i].ID < authorizedPeers[j].ID },
|
|
||||||
)
|
|
||||||
|
|
||||||
log.Trace().
|
|
||||||
Caller().
|
|
||||||
Str("self", machine.Hostname).
|
|
||||||
Str("peers", authorizedPeers.String()).
|
|
||||||
Msg("Authorized peers")
|
|
||||||
|
|
||||||
return authorizedPeers
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// filterMachinesByACL returns the list of peers authorized to be accessed from a given machine.
|
||||||
|
// func filterMachinesByACL(
|
||||||
|
// machine *Machine,
|
||||||
|
// machines Machines,
|
||||||
|
// lock *sync.RWMutex,
|
||||||
|
// aclPeerCacheMap map[string][]string,
|
||||||
|
// ) Machines {
|
||||||
|
// log.Trace().
|
||||||
|
// Caller().
|
||||||
|
// Str("self", machine.Hostname).
|
||||||
|
// Str("input", machines.String()).
|
||||||
|
// Msg("Finding peers filtered by ACLs")
|
||||||
|
//
|
||||||
|
// peers := make(map[uint64]Machine)
|
||||||
|
// // Aclfilter peers here. We are itering through machines in all users and search through the computed aclRules
|
||||||
|
// // for match between rule SrcIPs and DstPorts. If the rule is a match we allow the machine to be viewable.
|
||||||
|
// machineIPs := machine.IPAddresses.ToStringSlice()
|
||||||
|
//
|
||||||
|
// // TODO(kradalby): Remove this lock, I suspect its not a good idea, and might not be necessary,
|
||||||
|
// // we only set this at startup atm (reading ACLs) and it might become a bottleneck.
|
||||||
|
// lock.RLock()
|
||||||
|
//
|
||||||
|
// for _, peer := range machines {
|
||||||
|
// if peer.ID == machine.ID {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// peerIPs := peer.IPAddresses.ToStringSlice()
|
||||||
|
//
|
||||||
|
// if dstMap, ok := aclPeerCacheMap["*"]; ok {
|
||||||
|
// // match source and all destination
|
||||||
|
//
|
||||||
|
// for _, dst := range dstMap {
|
||||||
|
// if dst == "*" {
|
||||||
|
// peers[peer.ID] = peer
|
||||||
|
//
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // match source and all destination
|
||||||
|
// for _, peerIP := range peerIPs {
|
||||||
|
// for _, dst := range dstMap {
|
||||||
|
// _, cdr, _ := net.ParseCIDR(dst)
|
||||||
|
// ip := net.ParseIP(peerIP)
|
||||||
|
// if dst == peerIP || (cdr != nil && ip != nil && cdr.Contains(ip)) {
|
||||||
|
// peers[peer.ID] = peer
|
||||||
|
//
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // match all sources and source
|
||||||
|
// for _, machineIP := range machineIPs {
|
||||||
|
// for _, dst := range dstMap {
|
||||||
|
// _, cdr, _ := net.ParseCIDR(dst)
|
||||||
|
// ip := net.ParseIP(machineIP)
|
||||||
|
// if dst == machineIP || (cdr != nil && ip != nil && cdr.Contains(ip)) {
|
||||||
|
// peers[peer.ID] = peer
|
||||||
|
//
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for _, machineIP := range machineIPs {
|
||||||
|
// if dstMap, ok := aclPeerCacheMap[machineIP]; ok {
|
||||||
|
// // match source and all destination
|
||||||
|
// for _, dst := range dstMap {
|
||||||
|
// if dst == "*" {
|
||||||
|
// peers[peer.ID] = peer
|
||||||
|
//
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // match source and destination
|
||||||
|
// for _, peerIP := range peerIPs {
|
||||||
|
// for _, dst := range dstMap {
|
||||||
|
// _, cdr, _ := net.ParseCIDR(dst)
|
||||||
|
// ip := net.ParseIP(peerIP)
|
||||||
|
// if dst == peerIP || (cdr != nil && ip != nil && cdr.Contains(ip)) {
|
||||||
|
// peers[peer.ID] = peer
|
||||||
|
//
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for _, peerIP := range peerIPs {
|
||||||
|
// if dstMap, ok := aclPeerCacheMap[peerIP]; ok {
|
||||||
|
// // match source and all destination
|
||||||
|
// for _, dst := range dstMap {
|
||||||
|
// if dst == "*" {
|
||||||
|
// peers[peer.ID] = peer
|
||||||
|
//
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // match return path
|
||||||
|
// for _, machineIP := range machineIPs {
|
||||||
|
// for _, dst := range dstMap {
|
||||||
|
// _, cdr, _ := net.ParseCIDR(dst)
|
||||||
|
// ip := net.ParseIP(machineIP)
|
||||||
|
// if dst == machineIP || (cdr != nil && ip != nil && cdr.Contains(ip)) {
|
||||||
|
// peers[peer.ID] = peer
|
||||||
|
//
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// lock.RUnlock()
|
||||||
|
//
|
||||||
|
// authorizedPeers := make(Machines, 0, len(peers))
|
||||||
|
// for _, m := range peers {
|
||||||
|
// authorizedPeers = append(authorizedPeers, m)
|
||||||
|
// }
|
||||||
|
// sort.Slice(
|
||||||
|
// authorizedPeers,
|
||||||
|
// func(i, j int) bool { return authorizedPeers[i].ID < authorizedPeers[j].ID },
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// log.Trace().
|
||||||
|
// Caller().
|
||||||
|
// Str("self", machine.Hostname).
|
||||||
|
// Str("peers", authorizedPeers.String()).
|
||||||
|
// Msg("Authorized peers")
|
||||||
|
//
|
||||||
|
// return authorizedPeers
|
||||||
|
// }
|
||||||
|
|
||||||
func (h *Headscale) ListPeers(machine *Machine) (Machines, error) {
|
func (h *Headscale) ListPeers(machine *Machine) (Machines, error) {
|
||||||
log.Trace().
|
log.Trace().
|
||||||
Caller().
|
Caller().
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -1041,16 +1040,12 @@ func Test_getFilteredByACLPeers(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
var lock sync.RWMutex
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
aclRulesMap := generateACLPeerCacheMap(tt.args.rules)
|
|
||||||
|
|
||||||
got := filterMachinesByACL(
|
got := filterMachinesByACL(
|
||||||
tt.args.machine,
|
tt.args.machine,
|
||||||
tt.args.machines,
|
tt.args.machines,
|
||||||
&lock,
|
tt.args.rules,
|
||||||
aclRulesMap,
|
|
||||||
)
|
)
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
t.Errorf("filterMachinesByACL() = %v, want %v", got, tt.want)
|
t.Errorf("filterMachinesByACL() = %v, want %v", got, tt.want)
|
||||||
|
|
Loading…
Reference in a new issue