Skip to content

Commit

Permalink
fix ACL issue allowing ipv4 and ipv6
Browse files Browse the repository at this point in the history
There was an issue which would not pass the opposite of the IPvX as
filter rules to the tailscale client.

This caused a rule referring to for example ipv4 to block ipv6 traffic
for the client.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
  • Loading branch information
kradalby committed Apr 14, 2023
1 parent 7af39e4 commit ee2c5e7
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 20 deletions.
42 changes: 33 additions & 9 deletions acls.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"time"

"github.com/rs/zerolog/log"
"github.com/samber/lo"
"github.com/tailscale/hujson"
"go4.org/netipx"
"gopkg.in/yaml.v3"
Expand Down Expand Up @@ -533,9 +534,11 @@ func parseProtocol(protocol string) ([]int, bool, error) {
// - a group
// - a tag
// - a host
// - an ip
// - a cidr
// and transform these in IPAddresses.
func expandAlias(
machines []Machine,
machines Machines,
aclPolicy ACLPolicy,
alias string,
stripEmailDomain bool,
Expand Down Expand Up @@ -617,19 +620,40 @@ func expandAlias(

// if alias is an host
if h, ok := aclPolicy.Hosts[alias]; ok {
return []string{h.String()}, nil
log.Trace().Str("host", h.String()).Msg("expandAlias got hosts entry")

return expandAlias(machines, aclPolicy, h.String(), stripEmailDomain)
}

// if alias is an IP
ip, err := netip.ParseAddr(alias)
if err == nil {
return []string{ip.String()}, nil
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
}

// if alias is an CIDR
cidr, err := netip.ParsePrefix(alias)
if err == nil {
return []string{cidr.String()}, nil
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
}

log.Warn().Msgf("No IPs found with the alias %v", alias)
Expand Down
78 changes: 67 additions & 11 deletions acls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1026,29 +1026,70 @@ func Test_expandAlias(t *testing.T) {
wantErr: false,
},
{
name: "private network",
name: "simple host by ip passed through",
args: args{
alias: "homeNetwork",
machines: []Machine{},
aclPolicy: ACLPolicy{
Hosts: Hosts{
"homeNetwork": netip.MustParsePrefix("192.168.1.0/24"),
alias: "10.0.0.1",
machines: []Machine{},
aclPolicy: ACLPolicy{},
stripEmailDomain: true,
},
want: []string{"10.0.0.1"},
wantErr: false,
},
{
name: "simple host by ipv4 single ipv4",
args: args{
alias: "10.0.0.1",
machines: []Machine{
{
IPAddresses: MachineAddresses{
netip.MustParseAddr("10.0.0.1"),
},
User: User{Name: "mickael"},
},
},
aclPolicy: ACLPolicy{},
stripEmailDomain: true,
},
want: []string{"192.168.1.0/24"},
want: []string{"10.0.0.1"},
wantErr: false,
},
{
name: "simple host by ip",
name: "simple host by ipv4 single dual stack",
args: args{
alias: "10.0.0.1",
machines: []Machine{},
alias: "10.0.0.1",
machines: []Machine{
{
IPAddresses: MachineAddresses{
netip.MustParseAddr("10.0.0.1"),
netip.MustParseAddr("fd7a:115c:a1e0:ab12:4843:2222:6273:2222"),
},
User: User{Name: "mickael"},
},
},
aclPolicy: ACLPolicy{},
stripEmailDomain: true,
},
want: []string{"10.0.0.1"},
want: []string{"10.0.0.1", "fd7a:115c:a1e0:ab12:4843:2222:6273:2222"},
wantErr: false,
},
{
name: "simple host by ipv6 single dual stack",
args: args{
alias: "fd7a:115c:a1e0:ab12:4843:2222:6273:2222",
machines: []Machine{
{
IPAddresses: MachineAddresses{
netip.MustParseAddr("10.0.0.1"),
netip.MustParseAddr("fd7a:115c:a1e0:ab12:4843:2222:6273:2222"),
},
User: User{Name: "mickael"},
},
},
aclPolicy: ACLPolicy{},
stripEmailDomain: true,
},
want: []string{"fd7a:115c:a1e0:ab12:4843:2222:6273:2222", "10.0.0.1"},
wantErr: false,
},
{
Expand All @@ -1066,6 +1107,21 @@ func Test_expandAlias(t *testing.T) {
want: []string{"10.0.0.132/32"},
wantErr: false,
},
{
name: "private network",
args: args{
alias: "homeNetwork",
machines: []Machine{},
aclPolicy: ACLPolicy{
Hosts: Hosts{
"homeNetwork": netip.MustParsePrefix("192.168.1.0/24"),
},
},
stripEmailDomain: true,
},
want: []string{"192.168.1.0/24"},
wantErr: false,
},
{
name: "simple CIDR",
args: args{
Expand Down
14 changes: 14 additions & 0 deletions machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -1267,3 +1267,17 @@ func (h *Headscale) GenerateGivenName(machineKey string, suppliedName string) (s

return givenName, nil
}

func (machines Machines) FilterByIP(ip netip.Addr) Machines {
found := make(Machines, 0)

for _, machine := range machines {
for _, mIP := range machine.IPAddresses {
if ip == mIP {
found = append(found, machine)
}
}
}

return found
}

0 comments on commit ee2c5e7

Please sign in to comment.