diff --git a/acls.go b/acls.go index 53bb7023..c5e4864a 100644 --- a/acls.go +++ b/acls.go @@ -338,9 +338,8 @@ func (h *Headscale) generateSSHRules() ([]*tailcfg.SSHRule, error) { principals := make([]*tailcfg.SSHPrincipal, 0, len(sshACL.Sources)) for innerIndex, rawSrc := range sshACL.Sources { - expandedSrcs, err := expandAlias( + expandedSrcs, err := h.aclPolicy.expandAlias( machines, - *h.aclPolicy, rawSrc, h.cfg.OIDC.StripEmaildomain, ) @@ -391,16 +390,16 @@ func sshCheckAction(duration string) (*tailcfg.SSHAction, error) { func generateACLPolicySrc( machines []Machine, - aclPolicy ACLPolicy, + pol ACLPolicy, src string, stripEmaildomain bool, ) ([]string, error) { - return expandAlias(machines, aclPolicy, src, stripEmaildomain) + return pol.expandAlias(machines, src, stripEmaildomain) } func generateACLPolicyDest( machines []Machine, - aclPolicy ACLPolicy, + pol ACLPolicy, dest string, needsWildcard bool, stripEmaildomain bool, @@ -448,9 +447,8 @@ func generateACLPolicyDest( alias = fmt.Sprintf("%s:%s", tokens[0], tokens[1]) } - expanded, err := expandAlias( + expanded, err := pol.expandAlias( machines, - aclPolicy, alias, stripEmaildomain, ) @@ -534,13 +532,11 @@ func parseProtocol(protocol string) ([]int, bool, error) { // - an ip // - a cidr // and transform these in IPAddresses. -func expandAlias( +func (pol *ACLPolicy) expandAlias( machines Machines, - aclPolicy ACLPolicy, alias string, stripEmailDomain bool, ) ([]string, error) { - ips := []string{} if alias == "*" { return []string{"*"}, nil } @@ -549,120 +545,48 @@ func expandAlias( Str("alias", alias). Msg("Expanding") + // if alias is a group if strings.HasPrefix(alias, "group:") { - users, err := expandGroup(aclPolicy, alias, stripEmailDomain) - if err != nil { - return ips, err - } - for _, n := range users { - nodes := filterMachinesByUser(machines, n) - for _, node := range nodes { - ips = append(ips, node.IPAddresses.ToStringSlice()...) - } - } - - return ips, nil + return pol.getIPsFromGroup(alias, machines, stripEmailDomain) } + // if alias is a tag if strings.HasPrefix(alias, "tag:") { - // check for forced tags - for _, machine := range machines { - if contains(machine.ForcedTags, alias) { - ips = append(ips, machine.IPAddresses.ToStringSlice()...) - } - } - - // find tag owners - owners, err := expandTagOwners(aclPolicy, alias, stripEmailDomain) - if err != nil { - if errors.Is(err, errInvalidTag) { - if len(ips) == 0 { - return ips, fmt.Errorf( - "%w. %v isn't owned by a TagOwner and no forced tags are defined", - errInvalidTag, - alias, - ) - } - - return ips, nil - } else { - return ips, err - } - } - - // filter out machines per tag owner - for _, user := range owners { - machines := filterMachinesByUser(machines, user) - for _, machine := range machines { - hi := machine.GetHostInfo() - if contains(hi.RequestTags, alias) { - ips = append(ips, machine.IPAddresses.ToStringSlice()...) - } - } - } - - return ips, nil + return pol.getIPsFromTag(alias, machines, stripEmailDomain) } // if alias is a user - nodes := filterMachinesByUser(machines, alias) - nodes = excludeCorrectlyTaggedNodes(aclPolicy, nodes, alias, stripEmailDomain) - - for _, n := range nodes { - ips = append(ips, n.IPAddresses.ToStringSlice()...) - } - if len(ips) > 0 { + if ips := pol.getIPsForUser(alias, machines, stripEmailDomain); len(ips) > 0 { return ips, nil } // if alias is an host - if h, ok := aclPolicy.Hosts[alias]; ok { + if h, ok := pol.Hosts[alias]; ok { log.Trace().Str("host", h.String()).Msg("expandAlias got hosts entry") - return expandAlias(machines, aclPolicy, h.String(), stripEmailDomain) + return pol.expandAlias(machines, h.String(), stripEmailDomain) } // if alias is an IP if ip, err := netip.ParseAddr(alias); err == nil { - log.Trace().Str("ip", ip.String()).Msg("expandAlias got ip") - ips := []string{ip.String()} - matches := machines.FilterByIP(ip) - - for _, machine := range matches { - ips = append(ips, machine.IPAddresses.ToStringSlice()...) - } - - return lo.Uniq(ips), nil + return pol.getIPsFromSingleIP(ip, machines) } - if cidr, err := netip.ParsePrefix(alias); err == nil { - log.Trace().Str("cidr", cidr.String()).Msg("expandAlias got cidr") - val := []string{cidr.String()} - // This is suboptimal and quite expensive, but if we only add the cidr, we will miss all the relevant IPv6 - // addresses for the hosts that belong to tailscale. This doesnt really affect stuff like subnet routers. - for _, machine := range machines { - for _, ip := range machine.IPAddresses { - // log.Trace(). - // Msgf("checking if machine ip (%s) is part of cidr (%s): %v, is single ip cidr (%v), addr: %s", ip.String(), cidr.String(), cidr.Contains(ip), cidr.IsSingleIP(), cidr.Addr().String()) - if cidr.Contains(ip) { - val = append(val, machine.IPAddresses.ToStringSlice()...) - } - } - } - - return lo.Uniq(val), nil + // if alias is an IP Prefix (CIDR) + if prefix, err := netip.ParsePrefix(alias); err == nil { + return pol.getIPsFromIPPrefix(prefix, machines) } log.Warn().Msgf("No IPs found with the alias %v", alias) - return ips, nil + return []string{}, nil } // excludeCorrectlyTaggedNodes will remove from the list of input nodes the ones // that are correctly tagged since they should not be listed as being in the user // we assume in this function that we only have nodes from 1 user. func excludeCorrectlyTaggedNodes( - aclPolicy ACLPolicy, + aclPolicy *ACLPolicy, nodes []Machine, user string, stripEmailDomain bool, @@ -670,7 +594,7 @@ func excludeCorrectlyTaggedNodes( out := []Machine{} tags := []string{} for tag := range aclPolicy.TagOwners { - owners, _ := expandTagOwners(aclPolicy, user, stripEmailDomain) + owners, _ := getTagOwners(aclPolicy, user, stripEmailDomain) ns := append(owners, user) if contains(ns, user) { tags = append(tags, tag) @@ -758,15 +682,15 @@ func filterMachinesByUser(machines []Machine, user string) []Machine { return out } -// expandTagOwners will return a list of user. An owner can be either a user or a group +// getTagOwners will return a list of user. An owner can be either a user or a group // a group cannot be composed of groups. -func expandTagOwners( - aclPolicy ACLPolicy, +func getTagOwners( + pol *ACLPolicy, tag string, stripEmailDomain bool, ) ([]string, error) { var owners []string - ows, ok := aclPolicy.TagOwners[tag] + ows, ok := pol.TagOwners[tag] if !ok { return []string{}, fmt.Errorf( "%w. %v isn't owned by a TagOwner. Please add one first. https://tailscale.com/kb/1018/acls/#tag-owners", @@ -776,7 +700,7 @@ func expandTagOwners( } for _, owner := range ows { if strings.HasPrefix(owner, "group:") { - gs, err := expandGroup(aclPolicy, owner, stripEmailDomain) + gs, err := pol.getUsersInGroup(owner, stripEmailDomain) if err != nil { return []string{}, err } @@ -789,15 +713,15 @@ func expandTagOwners( return owners, nil } -// expandGroup will return the list of user inside the group +// getUsersInGroup will return the list of user inside the group // after some validation. -func expandGroup( - aclPolicy ACLPolicy, +func (pol *ACLPolicy) getUsersInGroup( group string, stripEmailDomain bool, ) ([]string, error) { - outGroups := []string{} - aclGroups, ok := aclPolicy.Groups[group] + users := []string{} + log.Trace().Caller().Interface("pol", pol).Msg("test") + aclGroups, ok := pol.Groups[group] if !ok { return []string{}, fmt.Errorf( "group %v isn't registered. %w", @@ -820,8 +744,129 @@ func expandGroup( errInvalidGroup, ) } - outGroups = append(outGroups, grp) + users = append(users, grp) } - return outGroups, nil + return users, nil +} + +func (pol *ACLPolicy) getIPsFromGroup( + group string, + machines Machines, + stripEmailDomain bool, +) ([]string, error) { + ips := []string{} + + users, err := pol.getUsersInGroup(group, stripEmailDomain) + if err != nil { + return ips, err + } + for _, n := range users { + nodes := filterMachinesByUser(machines, n) + for _, node := range nodes { + ips = append(ips, node.IPAddresses.ToStringSlice()...) + } + } + + return ips, nil +} + +func (pol *ACLPolicy) getIPsFromTag( + alias string, + machines Machines, + stripEmailDomain bool, +) ([]string, error) { + ips := []string{} + + // check for forced tags + for _, machine := range machines { + if contains(machine.ForcedTags, alias) { + ips = append(ips, machine.IPAddresses.ToStringSlice()...) + } + } + + // find tag owners + owners, err := getTagOwners(pol, alias, stripEmailDomain) + if err != nil { + if errors.Is(err, errInvalidTag) { + if len(ips) == 0 { + return ips, fmt.Errorf( + "%w. %v isn't owned by a TagOwner and no forced tags are defined", + errInvalidTag, + alias, + ) + } + + return ips, nil + } else { + return ips, err + } + } + + // filter out machines per tag owner + for _, user := range owners { + machines := filterMachinesByUser(machines, user) + for _, machine := range machines { + hi := machine.GetHostInfo() + if contains(hi.RequestTags, alias) { + ips = append(ips, machine.IPAddresses.ToStringSlice()...) + } + } + } + + return ips, nil +} + +func (pol *ACLPolicy) getIPsForUser( + user string, + machines Machines, + stripEmailDomain bool, +) []string { + ips := []string{} + + nodes := filterMachinesByUser(machines, user) + nodes = excludeCorrectlyTaggedNodes(pol, nodes, user, stripEmailDomain) + + for _, n := range nodes { + ips = append(ips, n.IPAddresses.ToStringSlice()...) + } + + return ips +} + +func (pol *ACLPolicy) getIPsFromSingleIP( + ip netip.Addr, + machines Machines, +) ([]string, error) { + log.Trace().Str("ip", ip.String()).Msg("expandAlias got ip") + + ips := []string{ip.String()} + matches := machines.FilterByIP(ip) + + for _, machine := range matches { + ips = append(ips, machine.IPAddresses.ToStringSlice()...) + } + + return lo.Uniq(ips), nil +} + +func (pol *ACLPolicy) getIPsFromIPPrefix( + prefix netip.Prefix, + machines Machines, +) ([]string, error) { + log.Trace().Str("prefix", prefix.String()).Msg("expandAlias got prefix") + val := []string{prefix.String()} + // This is suboptimal and quite expensive, but if we only add the prefix, we will miss all the relevant IPv6 + // addresses for the hosts that belong to tailscale. This doesnt really affect stuff like subnet routers. + for _, machine := range machines { + for _, ip := range machine.IPAddresses { + // log.Trace(). + // Msgf("checking if machine ip (%s) is part of prefix (%s): %v, is single ip prefix (%v), addr: %s", ip.String(), prefix.String(), prefix.Contains(ip), prefix.IsSingleIP(), prefix.Addr().String()) + if prefix.Contains(ip) { + val = append(val, machine.IPAddresses.ToStringSlice()...) + } + } + } + + return lo.Uniq(val), nil } diff --git a/acls_test.go b/acls_test.go index eaac2a7c..5df19082 100644 --- a/acls_test.go +++ b/acls_test.go @@ -556,26 +556,31 @@ func (s *Suite) TestPortGroup(c *check.C) { } func Test_expandGroup(t *testing.T) { + type field struct { + pol ACLPolicy + } type args struct { - aclPolicy ACLPolicy group string stripEmailDomain bool } tests := []struct { name string + field field args args want []string wantErr bool }{ { name: "simple test", - args: args{ - aclPolicy: ACLPolicy{ + field: field{ + pol: ACLPolicy{ Groups: Groups{ "group:test": []string{"user1", "user2", "user3"}, "group:foo": []string{"user2", "user3"}, }, }, + }, + args: args{ group: "group:test", stripEmailDomain: true, }, @@ -584,13 +589,15 @@ func Test_expandGroup(t *testing.T) { }, { name: "InexistantGroup", - args: args{ - aclPolicy: ACLPolicy{ + field: field{ + pol: ACLPolicy{ Groups: Groups{ "group:test": []string{"user1", "user2", "user3"}, "group:foo": []string{"user2", "user3"}, }, }, + }, + args: args{ group: "group:undefined", stripEmailDomain: true, }, @@ -599,8 +606,8 @@ func Test_expandGroup(t *testing.T) { }, { name: "Expand emails in group", - args: args{ - aclPolicy: ACLPolicy{ + field: field{ + pol: ACLPolicy{ Groups: Groups{ "group:admin": []string{ "joe.bar@gmail.com", @@ -608,6 +615,8 @@ func Test_expandGroup(t *testing.T) { }, }, }, + }, + args: args{ group: "group:admin", stripEmailDomain: true, }, @@ -616,8 +625,8 @@ func Test_expandGroup(t *testing.T) { }, { name: "Expand emails in group", - args: args{ - aclPolicy: ACLPolicy{ + field: field{ + pol: ACLPolicy{ Groups: Groups{ "group:admin": []string{ "joe.bar@gmail.com", @@ -625,6 +634,8 @@ func Test_expandGroup(t *testing.T) { }, }, }, + }, + args: args{ group: "group:admin", stripEmailDomain: false, }, @@ -634,8 +645,7 @@ func Test_expandGroup(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got, err := expandGroup( - test.args.aclPolicy, + got, err := test.field.pol.getUsersInGroup( test.args.group, test.args.stripEmailDomain, ) @@ -653,7 +663,7 @@ func Test_expandGroup(t *testing.T) { func Test_expandTagOwners(t *testing.T) { type args struct { - aclPolicy ACLPolicy + aclPolicy *ACLPolicy tag string stripEmailDomain bool } @@ -666,7 +676,7 @@ func Test_expandTagOwners(t *testing.T) { { name: "simple tag expansion", args: args{ - aclPolicy: ACLPolicy{ + aclPolicy: &ACLPolicy{ TagOwners: TagOwners{"tag:test": []string{"user1"}}, }, tag: "tag:test", @@ -678,7 +688,7 @@ func Test_expandTagOwners(t *testing.T) { { name: "expand with tag and group", args: args{ - aclPolicy: ACLPolicy{ + aclPolicy: &ACLPolicy{ Groups: Groups{"group:foo": []string{"user1", "user2"}}, TagOwners: TagOwners{"tag:test": []string{"group:foo"}}, }, @@ -691,7 +701,7 @@ func Test_expandTagOwners(t *testing.T) { { name: "expand with user and group", args: args{ - aclPolicy: ACLPolicy{ + aclPolicy: &ACLPolicy{ Groups: Groups{"group:foo": []string{"user1", "user2"}}, TagOwners: TagOwners{"tag:test": []string{"group:foo", "user3"}}, }, @@ -704,7 +714,7 @@ func Test_expandTagOwners(t *testing.T) { { name: "invalid tag", args: args{ - aclPolicy: ACLPolicy{ + aclPolicy: &ACLPolicy{ TagOwners: TagOwners{"tag:foo": []string{"group:foo", "user1"}}, }, tag: "tag:test", @@ -716,7 +726,7 @@ func Test_expandTagOwners(t *testing.T) { { name: "invalid group", args: args{ - aclPolicy: ACLPolicy{ + aclPolicy: &ACLPolicy{ Groups: Groups{"group:bar": []string{"user1", "user2"}}, TagOwners: TagOwners{"tag:test": []string{"group:foo", "user2"}}, }, @@ -729,7 +739,7 @@ func Test_expandTagOwners(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got, err := expandTagOwners( + got, err := getTagOwners( test.args.aclPolicy, test.args.tag, test.args.stripEmailDomain, @@ -908,6 +918,9 @@ func Test_listMachinesInUser(t *testing.T) { } func Test_expandAlias(t *testing.T) { + type field struct { + pol ACLPolicy + } type args struct { machines []Machine aclPolicy ACLPolicy @@ -916,12 +929,16 @@ func Test_expandAlias(t *testing.T) { } tests := []struct { name string + field field args args want []string wantErr bool }{ { name: "wildcard", + field: field{ + pol: ACLPolicy{}, + }, args: args{ alias: "*", machines: []Machine{ @@ -932,7 +949,6 @@ func Test_expandAlias(t *testing.T) { }, }, }, - aclPolicy: ACLPolicy{}, stripEmailDomain: true, }, want: []string{"*"}, @@ -940,6 +956,11 @@ func Test_expandAlias(t *testing.T) { }, { name: "simple group", + field: field{ + pol: ACLPolicy{ + Groups: Groups{"group:accountant": []string{"joe", "marc"}}, + }, + }, args: args{ alias: "group:accountant", machines: []Machine{ @@ -968,9 +989,6 @@ func Test_expandAlias(t *testing.T) { User: User{Name: "mickael"}, }, }, - aclPolicy: ACLPolicy{ - Groups: Groups{"group:accountant": []string{"joe", "marc"}}, - }, stripEmailDomain: true, }, want: []string{"100.64.0.1", "100.64.0.2", "100.64.0.3"}, @@ -978,6 +996,11 @@ func Test_expandAlias(t *testing.T) { }, { name: "wrong group", + field: field{ + pol: ACLPolicy{ + Groups: Groups{"group:accountant": []string{"joe", "marc"}}, + }, + }, args: args{ alias: "group:hr", machines: []Machine{ @@ -1006,9 +1029,6 @@ func Test_expandAlias(t *testing.T) { User: User{Name: "mickael"}, }, }, - aclPolicy: ACLPolicy{ - Groups: Groups{"group:accountant": []string{"joe", "marc"}}, - }, stripEmailDomain: true, }, want: []string{}, @@ -1016,10 +1036,12 @@ func Test_expandAlias(t *testing.T) { }, { name: "simple ipaddress", + field: field{ + pol: ACLPolicy{}, + }, args: args{ alias: "10.0.0.3", machines: []Machine{}, - aclPolicy: ACLPolicy{}, stripEmailDomain: true, }, want: []string{"10.0.0.3"}, @@ -1027,10 +1049,12 @@ func Test_expandAlias(t *testing.T) { }, { name: "simple host by ip passed through", + field: field{ + pol: ACLPolicy{}, + }, args: args{ alias: "10.0.0.1", machines: []Machine{}, - aclPolicy: ACLPolicy{}, stripEmailDomain: true, }, want: []string{"10.0.0.1"}, @@ -1038,6 +1062,9 @@ func Test_expandAlias(t *testing.T) { }, { name: "simple host by ipv4 single ipv4", + field: field{ + pol: ACLPolicy{}, + }, args: args{ alias: "10.0.0.1", machines: []Machine{ @@ -1048,7 +1075,6 @@ func Test_expandAlias(t *testing.T) { User: User{Name: "mickael"}, }, }, - aclPolicy: ACLPolicy{}, stripEmailDomain: true, }, want: []string{"10.0.0.1"}, @@ -1056,6 +1082,9 @@ func Test_expandAlias(t *testing.T) { }, { name: "simple host by ipv4 single dual stack", + field: field{ + pol: ACLPolicy{}, + }, args: args{ alias: "10.0.0.1", machines: []Machine{ @@ -1067,7 +1096,6 @@ func Test_expandAlias(t *testing.T) { User: User{Name: "mickael"}, }, }, - aclPolicy: ACLPolicy{}, stripEmailDomain: true, }, want: []string{"10.0.0.1", "fd7a:115c:a1e0:ab12:4843:2222:6273:2222"}, @@ -1075,6 +1103,9 @@ func Test_expandAlias(t *testing.T) { }, { name: "simple host by ipv6 single dual stack", + field: field{ + pol: ACLPolicy{}, + }, args: args{ alias: "fd7a:115c:a1e0:ab12:4843:2222:6273:2222", machines: []Machine{ @@ -1086,7 +1117,6 @@ func Test_expandAlias(t *testing.T) { User: User{Name: "mickael"}, }, }, - aclPolicy: ACLPolicy{}, stripEmailDomain: true, }, want: []string{"fd7a:115c:a1e0:ab12:4843:2222:6273:2222", "10.0.0.1"}, @@ -1094,14 +1124,16 @@ func Test_expandAlias(t *testing.T) { }, { name: "simple host by hostname alias", - args: args{ - alias: "testy", - machines: []Machine{}, - aclPolicy: ACLPolicy{ + field: field{ + pol: ACLPolicy{ Hosts: Hosts{ "testy": netip.MustParsePrefix("10.0.0.132/32"), }, }, + }, + args: args{ + alias: "testy", + machines: []Machine{}, stripEmailDomain: true, }, want: []string{"10.0.0.132/32"}, @@ -1109,14 +1141,16 @@ func Test_expandAlias(t *testing.T) { }, { name: "private network", - args: args{ - alias: "homeNetwork", - machines: []Machine{}, - aclPolicy: ACLPolicy{ + field: field{ + pol: ACLPolicy{ Hosts: Hosts{ "homeNetwork": netip.MustParsePrefix("192.168.1.0/24"), }, }, + }, + args: args{ + alias: "homeNetwork", + machines: []Machine{}, stripEmailDomain: true, }, want: []string{"192.168.1.0/24"}, @@ -1124,6 +1158,9 @@ func Test_expandAlias(t *testing.T) { }, { name: "simple CIDR", + field: field{ + pol: ACLPolicy{}, + }, args: args{ alias: "10.0.0.0/16", machines: []Machine{}, @@ -1135,6 +1172,11 @@ func Test_expandAlias(t *testing.T) { }, { name: "simple tag", + field: field{ + pol: ACLPolicy{ + TagOwners: TagOwners{"tag:hr-webserver": []string{"joe"}}, + }, + }, args: args{ alias: "tag:hr-webserver", machines: []Machine{ @@ -1173,9 +1215,6 @@ func Test_expandAlias(t *testing.T) { User: User{Name: "joe"}, }, }, - aclPolicy: ACLPolicy{ - TagOwners: TagOwners{"tag:hr-webserver": []string{"joe"}}, - }, stripEmailDomain: true, }, want: []string{"100.64.0.1", "100.64.0.2"}, @@ -1183,6 +1222,14 @@ func Test_expandAlias(t *testing.T) { }, { name: "No tag defined", + field: field{ + pol: ACLPolicy{ + Groups: Groups{"group:accountant": []string{"joe", "marc"}}, + TagOwners: TagOwners{ + "tag:accountant-webserver": []string{"group:accountant"}, + }, + }, + }, args: args{ alias: "tag:hr-webserver", machines: []Machine{ @@ -1211,12 +1258,6 @@ func Test_expandAlias(t *testing.T) { User: User{Name: "mickael"}, }, }, - aclPolicy: ACLPolicy{ - Groups: Groups{"group:accountant": []string{"joe", "marc"}}, - TagOwners: TagOwners{ - "tag:accountant-webserver": []string{"group:accountant"}, - }, - }, stripEmailDomain: true, }, want: []string{}, @@ -1224,6 +1265,9 @@ func Test_expandAlias(t *testing.T) { }, { name: "Forced tag defined", + field: field{ + pol: ACLPolicy{}, + }, args: args{ alias: "tag:hr-webserver", machines: []Machine{ @@ -1254,7 +1298,6 @@ func Test_expandAlias(t *testing.T) { User: User{Name: "mickael"}, }, }, - aclPolicy: ACLPolicy{}, stripEmailDomain: true, }, want: []string{"100.64.0.1", "100.64.0.2"}, @@ -1262,6 +1305,13 @@ func Test_expandAlias(t *testing.T) { }, { name: "Forced tag with legitimate tagOwner", + field: field{ + pol: ACLPolicy{ + TagOwners: TagOwners{ + "tag:hr-webserver": []string{"joe"}, + }, + }, + }, args: args{ alias: "tag:hr-webserver", machines: []Machine{ @@ -1296,11 +1346,6 @@ func Test_expandAlias(t *testing.T) { User: User{Name: "mickael"}, }, }, - aclPolicy: ACLPolicy{ - TagOwners: TagOwners{ - "tag:hr-webserver": []string{"joe"}, - }, - }, stripEmailDomain: true, }, want: []string{"100.64.0.1", "100.64.0.2"}, @@ -1308,6 +1353,11 @@ func Test_expandAlias(t *testing.T) { }, { name: "list host in user without correctly tagged servers", + field: field{ + pol: ACLPolicy{ + TagOwners: TagOwners{"tag:accountant-webserver": []string{"joe"}}, + }, + }, args: args{ alias: "joe", machines: []Machine{ @@ -1346,9 +1396,6 @@ func Test_expandAlias(t *testing.T) { User: User{Name: "joe"}, }, }, - aclPolicy: ACLPolicy{ - TagOwners: TagOwners{"tag:accountant-webserver": []string{"joe"}}, - }, stripEmailDomain: true, }, want: []string{"100.64.0.4"}, @@ -1357,9 +1404,8 @@ func Test_expandAlias(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got, err := expandAlias( + got, err := test.field.pol.expandAlias( test.args.machines, - test.args.aclPolicy, test.args.alias, test.args.stripEmailDomain, ) @@ -1377,7 +1423,7 @@ func Test_expandAlias(t *testing.T) { func Test_excludeCorrectlyTaggedNodes(t *testing.T) { type args struct { - aclPolicy ACLPolicy + aclPolicy *ACLPolicy nodes []Machine user string stripEmailDomain bool @@ -1391,7 +1437,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { { name: "exclude nodes with valid tags", args: args{ - aclPolicy: ACLPolicy{ + aclPolicy: &ACLPolicy{ TagOwners: TagOwners{"tag:accountant-webserver": []string{"joe"}}, }, nodes: []Machine{ @@ -1437,7 +1483,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { { name: "exclude nodes with valid tags, and owner is in a group", args: args{ - aclPolicy: ACLPolicy{ + aclPolicy: &ACLPolicy{ Groups: Groups{ "group:accountant": []string{"joe", "bar"}, }, @@ -1488,7 +1534,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { { name: "exclude nodes with valid tags and with forced tags", args: args{ - aclPolicy: ACLPolicy{ + aclPolicy: &ACLPolicy{ TagOwners: TagOwners{"tag:accountant-webserver": []string{"joe"}}, }, nodes: []Machine{ @@ -1530,7 +1576,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { { name: "all nodes have invalid tags, don't exclude them", args: args{ - aclPolicy: ACLPolicy{ + aclPolicy: &ACLPolicy{ TagOwners: TagOwners{"tag:accountant-webserver": []string{"joe"}}, }, nodes: []Machine{ diff --git a/acls_types.go b/acls_types.go index 4f318ddc..b98b2d2a 100644 --- a/acls_types.go +++ b/acls_types.go @@ -111,8 +111,8 @@ func (hosts *Hosts) UnmarshalYAML(data []byte) error { } // IsZero is perhaps a bit naive here. -func (policy ACLPolicy) IsZero() bool { - if len(policy.Groups) == 0 && len(policy.Hosts) == 0 && len(policy.ACLs) == 0 { +func (pol ACLPolicy) IsZero() bool { + if len(pol.Groups) == 0 && len(pol.Hosts) == 0 && len(pol.ACLs) == 0 { return true } diff --git a/machine.go b/machine.go index 1b70b1e2..1d132755 100644 --- a/machine.go +++ b/machine.go @@ -893,7 +893,7 @@ func getTags( validTagMap := make(map[string]bool) invalidTagMap := make(map[string]bool) for _, tag := range machine.HostInfo.RequestTags { - owners, err := expandTagOwners(*aclPolicy, tag, stripEmailDomain) + owners, err := getTagOwners(aclPolicy, tag, stripEmailDomain) if errors.Is(err, errInvalidTag) { invalidTagMap[tag] = true @@ -1207,7 +1207,7 @@ func (h *Headscale) EnableAutoApprovedRoutes(machine *Machine) error { if approvedAlias == machine.User.Name { approvedRoutes = append(approvedRoutes, advertisedRoute) } else { - approvedIps, err := expandAlias([]Machine{*machine}, *h.aclPolicy, approvedAlias, h.cfg.OIDC.StripEmaildomain) + approvedIps, err := h.aclPolicy.expandAlias([]Machine{*machine}, approvedAlias, h.cfg.OIDC.StripEmaildomain) if err != nil { log.Err(err). Str("alias", approvedAlias).