From 735b185e7f669360dc666211ee8501874a61a932 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 28 Apr 2023 16:11:02 +0200 Subject: [PATCH] use IPSet in acls instead of string slice Signed-off-by: Kristoffer Dalby --- CHANGELOG.md | 1 + acls.go | 186 ++++++++++++++++++++++++++-------------- acls_test.go | 113 ++++++++++++++++-------- integration/ssh_test.go | 2 +- machine.go | 11 ++- 5 files changed, 209 insertions(+), 104 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f21e931b..0314088d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Profiles are continously generated in our integration tests. - Fix systemd service file location in `.deb` packages [#1391](https://github.com/juanfont/headscale/pull/1391) - Improvements on Noise implementation [#1379](https://github.com/juanfont/headscale/pull/1379) +- Replace node filter logic, ensuring nodes with access can see eachother [#1381](https://github.com/juanfont/headscale/pull/1381) ## 0.22.1 (2023-04-20) diff --git a/acls.go b/acls.go index f405acbf..a771df50 100644 --- a/acls.go +++ b/acls.go @@ -13,8 +13,8 @@ import ( "time" "github.com/rs/zerolog/log" - "github.com/samber/lo" "github.com/tailscale/hujson" + "go4.org/netipx" "gopkg.in/yaml.v3" "tailscale.com/envknob" "tailscale.com/tailcfg" @@ -272,21 +272,41 @@ func (h *Headscale) generateSSHRules() ([]*tailcfg.SSHRule, error) { principals := make([]*tailcfg.SSHPrincipal, 0, len(sshACL.Sources)) for innerIndex, rawSrc := range sshACL.Sources { - expandedSrcs, err := h.aclPolicy.expandAlias( - machines, - rawSrc, - h.cfg.OIDC.StripEmaildomain, - ) - if err != nil { - log.Error(). - Msgf("Error parsing SSH %d, Source %d", index, innerIndex) - - return nil, err - } - for _, expandedSrc := range expandedSrcs { + if isWildcard(rawSrc) { principals = append(principals, &tailcfg.SSHPrincipal{ - NodeIP: expandedSrc, + Any: true, }) + } else if isGroup(rawSrc) { + users, err := h.aclPolicy.getUsersInGroup(rawSrc, h.cfg.OIDC.StripEmaildomain) + if err != nil { + log.Error(). + Msgf("Error parsing SSH %d, Source %d", index, innerIndex) + + return nil, err + } + + for _, user := range users { + principals = append(principals, &tailcfg.SSHPrincipal{ + UserLogin: user, + }) + } + } else { + expandedSrcs, err := h.aclPolicy.expandAlias( + machines, + rawSrc, + h.cfg.OIDC.StripEmaildomain, + ) + if err != nil { + log.Error(). + Msgf("Error parsing SSH %d, Source %d", index, innerIndex) + + return nil, err + } + for _, expandedSrc := range expandedSrcs.Prefixes() { + principals = append(principals, &tailcfg.SSHPrincipal{ + NodeIP: expandedSrc.Addr().String(), + }) + } } } @@ -295,10 +315,9 @@ func (h *Headscale) generateSSHRules() ([]*tailcfg.SSHRule, error) { userMap[user] = "=" } rules = append(rules, &tailcfg.SSHRule{ - RuleExpires: nil, - Principals: principals, - SSHUsers: userMap, - Action: &action, + Principals: principals, + SSHUsers: userMap, + Action: &action, }) } @@ -329,7 +348,18 @@ func (pol *ACLPolicy) getIPsFromSource( machines []Machine, stripEmaildomain bool, ) ([]string, error) { - return pol.expandAlias(machines, src, stripEmaildomain) + ipSet, err := pol.expandAlias(machines, src, stripEmaildomain) + if err != nil { + return []string{}, err + } + + prefixes := []string{} + + for _, prefix := range ipSet.Prefixes() { + prefixes = append(prefixes, prefix.String()) + } + + return prefixes, nil } // getNetPortRangeFromDestination returns a set of tailcfg.NetPortRange @@ -397,11 +427,11 @@ func (pol *ACLPolicy) getNetPortRangeFromDestination( } dests := []tailcfg.NetPortRange{} - for _, d := range expanded { - for _, p := range *ports { + for _, dest := range expanded.Prefixes() { + for _, port := range *ports { pr := tailcfg.NetPortRange{ - IP: d, - Ports: p, + IP: dest.String(), + Ports: port, } dests = append(dests, pr) } @@ -472,28 +502,30 @@ func (pol *ACLPolicy) expandAlias( machines Machines, alias string, stripEmailDomain bool, -) ([]string, error) { - if alias == "*" { - return []string{"*"}, nil +) (*netipx.IPSet, error) { + if isWildcard(alias) { + return parseIPSet("*", nil) } + build := netipx.IPSetBuilder{} + log.Debug(). Str("alias", alias). Msg("Expanding") // if alias is a group - if strings.HasPrefix(alias, "group:") { + if isGroup(alias) { return pol.getIPsFromGroup(alias, machines, stripEmailDomain) } // if alias is a tag - if strings.HasPrefix(alias, "tag:") { + if isTag(alias) { return pol.getIPsFromTag(alias, machines, stripEmailDomain) } // if alias is a user - if ips := pol.getIPsForUser(alias, machines, stripEmailDomain); len(ips) > 0 { - return ips, nil + if ips, err := pol.getIPsForUser(alias, machines, stripEmailDomain); ips != nil { + return ips, err } // if alias is an host @@ -516,7 +548,7 @@ func (pol *ACLPolicy) expandAlias( log.Warn().Msgf("No IPs found with the alias %v", alias) - return []string{}, nil + return build.IPSet() } // excludeCorrectlyTaggedNodes will remove from the list of input nodes the ones @@ -561,7 +593,7 @@ func excludeCorrectlyTaggedNodes( } func expandPorts(portsStr string, needsWildcard bool) (*[]tailcfg.PortRange, error) { - if portsStr == "*" { + if isWildcard(portsStr) { return &[]tailcfg.PortRange{ {First: portRangeBegin, Last: portRangeEnd}, }, nil @@ -636,7 +668,7 @@ func getTagOwners( ) } for _, owner := range ows { - if strings.HasPrefix(owner, "group:") { + if isGroup(owner) { gs, err := pol.getUsersInGroup(owner, stripEmailDomain) if err != nil { return []string{}, err @@ -667,7 +699,7 @@ func (pol *ACLPolicy) getUsersInGroup( ) } for _, group := range aclGroups { - if strings.HasPrefix(group, "group:") { + if isGroup(group) { return []string{}, fmt.Errorf( "%w. A group cannot be composed of groups. https://tailscale.com/kb/1018/acls/#groups", errInvalidGroup, @@ -691,34 +723,34 @@ func (pol *ACLPolicy) getIPsFromGroup( group string, machines Machines, stripEmailDomain bool, -) ([]string, error) { - ips := []string{} +) (*netipx.IPSet, error) { + build := netipx.IPSetBuilder{} users, err := pol.getUsersInGroup(group, stripEmailDomain) if err != nil { - return ips, err + return &netipx.IPSet{}, err } - for _, n := range users { - nodes := filterMachinesByUser(machines, n) - for _, node := range nodes { - ips = append(ips, node.IPAddresses.ToStringSlice()...) + for _, user := range users { + filteredMachines := filterMachinesByUser(machines, user) + for _, machine := range filteredMachines { + machine.IPAddresses.AppendToIPSet(&build) } } - return ips, nil + return build.IPSet() } func (pol *ACLPolicy) getIPsFromTag( alias string, machines Machines, stripEmailDomain bool, -) ([]string, error) { - ips := []string{} +) (*netipx.IPSet, error) { + build := netipx.IPSetBuilder{} // check for forced tags for _, machine := range machines { if contains(machine.ForcedTags, alias) { - ips = append(ips, machine.IPAddresses.ToStringSlice()...) + machine.IPAddresses.AppendToIPSet(&build) } } @@ -726,17 +758,18 @@ func (pol *ACLPolicy) getIPsFromTag( owners, err := getTagOwners(pol, alias, stripEmailDomain) if err != nil { if errors.Is(err, errInvalidTag) { - if len(ips) == 0 { - return ips, fmt.Errorf( + ipSet, _ := build.IPSet() + if len(ipSet.Prefixes()) == 0 { + return ipSet, fmt.Errorf( "%w. %v isn't owned by a TagOwner and no forced tags are defined", errInvalidTag, alias, ) } - return ips, nil + return build.IPSet() } else { - return ips, err + return nil, err } } @@ -746,53 +779,62 @@ func (pol *ACLPolicy) getIPsFromTag( for _, machine := range machines { hi := machine.GetHostInfo() if contains(hi.RequestTags, alias) { - ips = append(ips, machine.IPAddresses.ToStringSlice()...) + machine.IPAddresses.AppendToIPSet(&build) } } } - return ips, nil + return build.IPSet() } func (pol *ACLPolicy) getIPsForUser( user string, machines Machines, stripEmailDomain bool, -) []string { - ips := []string{} +) (*netipx.IPSet, error) { + build := netipx.IPSetBuilder{} - nodes := filterMachinesByUser(machines, user) - nodes = excludeCorrectlyTaggedNodes(pol, nodes, user, stripEmailDomain) + filteredMachines := filterMachinesByUser(machines, user) + filteredMachines = excludeCorrectlyTaggedNodes(pol, filteredMachines, user, stripEmailDomain) - for _, n := range nodes { - ips = append(ips, n.IPAddresses.ToStringSlice()...) + // shortcurcuit if we have no machines to get ips from. + if len(filteredMachines) == 0 { + return nil, nil //nolint } - return ips + for _, machine := range filteredMachines { + machine.IPAddresses.AppendToIPSet(&build) + } + + return build.IPSet() } func (pol *ACLPolicy) getIPsFromSingleIP( ip netip.Addr, machines Machines, -) ([]string, error) { +) (*netipx.IPSet, error) { log.Trace().Str("ip", ip.String()).Msg("expandAlias got ip") - ips := []string{ip.String()} matches := machines.FilterByIP(ip) + build := netipx.IPSetBuilder{} + build.Add(ip) + for _, machine := range matches { - ips = append(ips, machine.IPAddresses.ToStringSlice()...) + machine.IPAddresses.AppendToIPSet(&build) } - return lo.Uniq(ips), nil + return build.IPSet() } func (pol *ACLPolicy) getIPsFromIPPrefix( prefix netip.Prefix, machines Machines, -) ([]string, error) { +) (*netipx.IPSet, error) { log.Trace().Str("prefix", prefix.String()).Msg("expandAlias got prefix") - val := []string{prefix.String()} + build := netipx.IPSetBuilder{} + build.AddPrefix(prefix) + // 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 { @@ -800,10 +842,22 @@ func (pol *ACLPolicy) getIPsFromIPPrefix( // 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()...) + machine.IPAddresses.AppendToIPSet(&build) } } } - return lo.Uniq(val), nil + return build.IPSet() +} + +func isWildcard(str string) bool { + return str == "*" +} + +func isGroup(str string) bool { + return strings.HasPrefix(str, "group:") +} + +func isTag(str string) bool { + return strings.HasPrefix(str, "tag:") } diff --git a/acls_test.go b/acls_test.go index 30dfbe31..d6e10a73 100644 --- a/acls_test.go +++ b/acls_test.go @@ -8,6 +8,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/rs/zerolog/log" + "go4.org/netipx" "gopkg.in/check.v1" "tailscale.com/envknob" "tailscale.com/tailcfg" @@ -144,7 +145,7 @@ func (s *Suite) TestSshRules(c *check.C) { c.Assert(app.sshPolicy.Rules, check.HasLen, 2) c.Assert(app.sshPolicy.Rules[0].SSHUsers, check.HasLen, 1) c.Assert(app.sshPolicy.Rules[0].Principals, check.HasLen, 1) - c.Assert(app.sshPolicy.Rules[0].Principals[0].NodeIP, check.Matches, "100.64.0.1") + c.Assert(app.sshPolicy.Rules[0].Principals[0].UserLogin, check.Matches, "user1") c.Assert(app.sshPolicy.Rules[1].SSHUsers, check.HasLen, 1) c.Assert(app.sshPolicy.Rules[1].Principals, check.HasLen, 1) @@ -232,7 +233,7 @@ func (s *Suite) TestValidExpandTagOwnersInSources(c *check.C) { c.Assert(err, check.IsNil) c.Assert(app.aclRules, check.HasLen, 1) c.Assert(app.aclRules[0].SrcIPs, check.HasLen, 1) - c.Assert(app.aclRules[0].SrcIPs[0], check.Equals, "100.64.0.1") + c.Assert(app.aclRules[0].SrcIPs[0], check.Equals, "100.64.0.1/32") } // this test should validate that we can expand a group in a TagOWner section and @@ -282,7 +283,7 @@ func (s *Suite) TestValidExpandTagOwnersInDestinations(c *check.C) { c.Assert(err, check.IsNil) c.Assert(app.aclRules, check.HasLen, 1) c.Assert(app.aclRules[0].DstPorts, check.HasLen, 1) - c.Assert(app.aclRules[0].DstPorts[0].IP, check.Equals, "100.64.0.1") + c.Assert(app.aclRules[0].DstPorts[0].IP, check.Equals, "100.64.0.1/32") } // need a test with: @@ -331,7 +332,7 @@ func (s *Suite) TestInvalidTagValidUser(c *check.C) { c.Assert(err, check.IsNil) c.Assert(app.aclRules, check.HasLen, 1) c.Assert(app.aclRules[0].SrcIPs, check.HasLen, 1) - c.Assert(app.aclRules[0].SrcIPs[0], check.Equals, "100.64.0.1") + c.Assert(app.aclRules[0].SrcIPs[0], check.Equals, "100.64.0.1/32") } // tag on a host is owned by a tag owner, the tag is valid. @@ -399,14 +400,14 @@ func (s *Suite) TestValidTagInvalidUser(c *check.C) { c.Assert(err, check.IsNil) c.Assert(app.aclRules, check.HasLen, 1) c.Assert(app.aclRules[0].SrcIPs, check.HasLen, 1) - c.Assert(app.aclRules[0].SrcIPs[0], check.Equals, "100.64.0.2") + c.Assert(app.aclRules[0].SrcIPs[0], check.Equals, "100.64.0.2/32") c.Assert(app.aclRules[0].DstPorts, check.HasLen, 2) c.Assert(app.aclRules[0].DstPorts[0].Ports.First, check.Equals, uint16(80)) c.Assert(app.aclRules[0].DstPorts[0].Ports.Last, check.Equals, uint16(80)) - c.Assert(app.aclRules[0].DstPorts[0].IP, check.Equals, "100.64.0.1") + c.Assert(app.aclRules[0].DstPorts[0].IP, check.Equals, "100.64.0.1/32") c.Assert(app.aclRules[0].DstPorts[1].Ports.First, check.Equals, uint16(443)) c.Assert(app.aclRules[0].DstPorts[1].Ports.Last, check.Equals, uint16(443)) - c.Assert(app.aclRules[0].DstPorts[1].IP, check.Equals, "100.64.0.1") + c.Assert(app.aclRules[0].DstPorts[1].IP, check.Equals, "100.64.0.1/32") } func (s *Suite) TestPortRange(c *check.C) { @@ -449,8 +450,8 @@ func (s *Suite) TestPortWildcard(c *check.C) { c.Assert(rules[0].DstPorts, check.HasLen, 1) c.Assert(rules[0].DstPorts[0].Ports.First, check.Equals, uint16(0)) c.Assert(rules[0].DstPorts[0].Ports.Last, check.Equals, uint16(65535)) - c.Assert(rules[0].SrcIPs, check.HasLen, 1) - c.Assert(rules[0].SrcIPs[0], check.Equals, "*") + c.Assert(rules[0].SrcIPs, check.HasLen, 2) + c.Assert(rules[0].SrcIPs[0], check.Equals, "0.0.0.0/0") } func (s *Suite) TestPortWildcardYAML(c *check.C) { @@ -465,8 +466,8 @@ func (s *Suite) TestPortWildcardYAML(c *check.C) { c.Assert(rules[0].DstPorts, check.HasLen, 1) c.Assert(rules[0].DstPorts[0].Ports.First, check.Equals, uint16(0)) c.Assert(rules[0].DstPorts[0].Ports.Last, check.Equals, uint16(65535)) - c.Assert(rules[0].SrcIPs, check.HasLen, 1) - c.Assert(rules[0].SrcIPs[0], check.Equals, "*") + c.Assert(rules[0].SrcIPs, check.HasLen, 2) + c.Assert(rules[0].SrcIPs[0], check.Equals, "0.0.0.0/0") } func (s *Suite) TestPortUser(c *check.C) { @@ -511,7 +512,7 @@ func (s *Suite) TestPortUser(c *check.C) { c.Assert(rules[0].SrcIPs, check.HasLen, 1) c.Assert(rules[0].SrcIPs[0], check.Not(check.Equals), "not an ip") c.Assert(len(ips), check.Equals, 1) - c.Assert(rules[0].SrcIPs[0], check.Equals, ips[0].String()) + c.Assert(rules[0].SrcIPs[0], check.Equals, ips[0].String()+"/32") } func (s *Suite) TestPortGroup(c *check.C) { @@ -554,7 +555,7 @@ func (s *Suite) TestPortGroup(c *check.C) { c.Assert(rules[0].SrcIPs, check.HasLen, 1) c.Assert(rules[0].SrcIPs[0], check.Not(check.Equals), "not an ip") c.Assert(len(ips), check.Equals, 1) - c.Assert(rules[0].SrcIPs[0], check.Equals, ips[0].String()) + c.Assert(rules[0].SrcIPs[0], check.Equals, ips[0].String()+"/32") } func Test_expandGroup(t *testing.T) { @@ -920,6 +921,22 @@ func Test_listMachinesInUser(t *testing.T) { } func Test_expandAlias(t *testing.T) { + set := func(ips []string, prefixes []string) *netipx.IPSet { + var builder netipx.IPSetBuilder + + for _, ip := range ips { + builder.Add(netip.MustParseAddr(ip)) + } + + for _, pre := range prefixes { + builder.AddPrefix(netip.MustParsePrefix(pre)) + } + + s, _ := builder.IPSet() + + return s + } + type field struct { pol ACLPolicy } @@ -933,7 +950,7 @@ func Test_expandAlias(t *testing.T) { name string field field args args - want []string + want *netipx.IPSet wantErr bool }{ { @@ -953,7 +970,10 @@ func Test_expandAlias(t *testing.T) { }, stripEmailDomain: true, }, - want: []string{"*"}, + want: set([]string{}, []string{ + "0.0.0.0/0", + "::/0", + }), wantErr: false, }, { @@ -993,7 +1013,9 @@ func Test_expandAlias(t *testing.T) { }, stripEmailDomain: true, }, - want: []string{"100.64.0.1", "100.64.0.2", "100.64.0.3"}, + want: set([]string{ + "100.64.0.1", "100.64.0.2", "100.64.0.3", + }, []string{}), wantErr: false, }, { @@ -1033,7 +1055,7 @@ func Test_expandAlias(t *testing.T) { }, stripEmailDomain: true, }, - want: []string{}, + want: set([]string{}, []string{}), wantErr: true, }, { @@ -1046,7 +1068,9 @@ func Test_expandAlias(t *testing.T) { machines: []Machine{}, stripEmailDomain: true, }, - want: []string{"10.0.0.3"}, + want: set([]string{ + "10.0.0.3", + }, []string{}), wantErr: false, }, { @@ -1059,7 +1083,9 @@ func Test_expandAlias(t *testing.T) { machines: []Machine{}, stripEmailDomain: true, }, - want: []string{"10.0.0.1"}, + want: set([]string{ + "10.0.0.1", + }, []string{}), wantErr: false, }, { @@ -1079,7 +1105,9 @@ func Test_expandAlias(t *testing.T) { }, stripEmailDomain: true, }, - want: []string{"10.0.0.1"}, + want: set([]string{ + "10.0.0.1", + }, []string{}), wantErr: false, }, { @@ -1100,7 +1128,9 @@ func Test_expandAlias(t *testing.T) { }, stripEmailDomain: true, }, - want: []string{"10.0.0.1", "fd7a:115c:a1e0:ab12:4843:2222:6273:2222"}, + want: set([]string{ + "10.0.0.1", "fd7a:115c:a1e0:ab12:4843:2222:6273:2222", + }, []string{}), wantErr: false, }, { @@ -1121,7 +1151,9 @@ func Test_expandAlias(t *testing.T) { }, stripEmailDomain: true, }, - want: []string{"fd7a:115c:a1e0:ab12:4843:2222:6273:2222", "10.0.0.1"}, + want: set([]string{ + "fd7a:115c:a1e0:ab12:4843:2222:6273:2222", "10.0.0.1", + }, []string{}), wantErr: false, }, { @@ -1138,7 +1170,7 @@ func Test_expandAlias(t *testing.T) { machines: []Machine{}, stripEmailDomain: true, }, - want: []string{"10.0.0.132/32"}, + want: set([]string{}, []string{"10.0.0.132/32"}), wantErr: false, }, { @@ -1155,7 +1187,7 @@ func Test_expandAlias(t *testing.T) { machines: []Machine{}, stripEmailDomain: true, }, - want: []string{"192.168.1.0/24"}, + want: set([]string{}, []string{"192.168.1.0/24"}), wantErr: false, }, { @@ -1169,7 +1201,7 @@ func Test_expandAlias(t *testing.T) { aclPolicy: ACLPolicy{}, stripEmailDomain: true, }, - want: []string{"10.0.0.0/16"}, + want: set([]string{}, []string{"10.0.0.0/16"}), wantErr: false, }, { @@ -1219,7 +1251,9 @@ func Test_expandAlias(t *testing.T) { }, stripEmailDomain: true, }, - want: []string{"100.64.0.1", "100.64.0.2"}, + want: set([]string{ + "100.64.0.1", "100.64.0.2", + }, []string{}), wantErr: false, }, { @@ -1262,7 +1296,7 @@ func Test_expandAlias(t *testing.T) { }, stripEmailDomain: true, }, - want: []string{}, + want: set([]string{}, []string{}), wantErr: true, }, { @@ -1302,7 +1336,7 @@ func Test_expandAlias(t *testing.T) { }, stripEmailDomain: true, }, - want: []string{"100.64.0.1", "100.64.0.2"}, + want: set([]string{"100.64.0.1", "100.64.0.2"}, []string{}), wantErr: false, }, { @@ -1350,7 +1384,7 @@ func Test_expandAlias(t *testing.T) { }, stripEmailDomain: true, }, - want: []string{"100.64.0.1", "100.64.0.2"}, + want: set([]string{"100.64.0.1", "100.64.0.2"}, []string{}), wantErr: false, }, { @@ -1400,7 +1434,7 @@ func Test_expandAlias(t *testing.T) { }, stripEmailDomain: true, }, - want: []string{"100.64.0.4"}, + want: set([]string{"100.64.0.4"}, []string{}), wantErr: false, }, } @@ -1416,7 +1450,7 @@ func Test_expandAlias(t *testing.T) { return } - if !reflect.DeepEqual(got, test.want) { + if diff := cmp.Diff(test.want, got); diff != "" { t.Errorf("expandAlias() = %v, want %v", got, test.want) } }) @@ -1702,10 +1736,17 @@ func TestACLPolicy_generateFilterRules(t *testing.T) { }, want: []tailcfg.FilterRule{ { - SrcIPs: []string{"*"}, + SrcIPs: []string{"0.0.0.0/0", "::/0"}, DstPorts: []tailcfg.NetPortRange{ { - IP: "*", + IP: "0.0.0.0/0", + Ports: tailcfg.PortRange{ + First: 0, + Last: 65535, + }, + }, + { + IP: "::/0", Ports: tailcfg.PortRange{ First: 0, Last: 65535, @@ -1750,17 +1791,17 @@ func TestACLPolicy_generateFilterRules(t *testing.T) { }, want: []tailcfg.FilterRule{ { - SrcIPs: []string{"100.64.0.1", "fd7a:115c:a1e0:ab12:4843:2222:6273:2221"}, + SrcIPs: []string{"100.64.0.1/32", "fd7a:115c:a1e0:ab12:4843:2222:6273:2221/128"}, DstPorts: []tailcfg.NetPortRange{ { - IP: "100.64.0.2", + IP: "100.64.0.2/32", Ports: tailcfg.PortRange{ First: 0, Last: 65535, }, }, { - IP: "fd7a:115c:a1e0:ab12:4843:2222:6273:2222", + IP: "fd7a:115c:a1e0:ab12:4843:2222:6273:2222/128", Ports: tailcfg.PortRange{ First: 0, Last: 65535, diff --git a/integration/ssh_test.go b/integration/ssh_test.go index 66f9f815..aaebf3b9 100644 --- a/integration/ssh_test.go +++ b/integration/ssh_test.go @@ -424,7 +424,7 @@ func TestSSUserOnlyIsolation(t *testing.T) { // TODO(kradalby,evenh): ACLs do currently not cover reject // cases properly, and currently will accept all incomming connections // as long as a rule is present. - // + // for _, client := range ssh1Clients { // for _, peer := range ssh2Clients { // if client.Hostname() == peer.Hostname() { diff --git a/machine.go b/machine.go index 982ed78a..9b775a47 100644 --- a/machine.go +++ b/machine.go @@ -13,6 +13,7 @@ import ( v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/rs/zerolog/log" "github.com/samber/lo" + "go4.org/netipx" "google.golang.org/protobuf/types/known/timestamppb" "gorm.io/gorm" "tailscale.com/tailcfg" @@ -97,6 +98,14 @@ func (ma MachineAddresses) ToStringSlice() []string { return strSlice } +// AppendToIPSet adds the individual ips in MachineAddresses to a +// given netipx.IPSetBuilder. +func (ma MachineAddresses) AppendToIPSet(build *netipx.IPSetBuilder) { + for _, ip := range ma { + build.Add(ip) + } +} + func (ma *MachineAddresses) Scan(destination interface{}) error { switch value := destination.(type) { case string: @@ -1114,7 +1123,7 @@ func (h *Headscale) EnableAutoApprovedRoutes(machine *Machine) error { } // approvedIPs should contain all of machine's IPs if it matches the rule, so check for first - if contains(approvedIps, machine.IPAddresses[0].String()) { + if approvedIps.Contains(machine.IPAddresses[0]) { approvedRoutes = append(approvedRoutes, advertisedRoute) } }