add autogroup:internet, fix reduce filter rules (#1917)
This commit is contained in:
parent
ff427ccb78
commit
87e2ae4d52
3 changed files with 619 additions and 7 deletions
|
@ -56,6 +56,7 @@ after improving the test harness as part of adopting [#1460](https://github.com/
|
||||||
- Add support for deleting api keys [#1702](https://github.com/juanfont/headscale/pull/1702)
|
- Add support for deleting api keys [#1702](https://github.com/juanfont/headscale/pull/1702)
|
||||||
- Add command to backfill IP addresses for nodes missing IPs from configured prefixes. [#1869](https://github.com/juanfont/headscale/pull/1869)
|
- Add command to backfill IP addresses for nodes missing IPs from configured prefixes. [#1869](https://github.com/juanfont/headscale/pull/1869)
|
||||||
- Log available update as warning [#1877](https://github.com/juanfont/headscale/pull/1877)
|
- Log available update as warning [#1877](https://github.com/juanfont/headscale/pull/1877)
|
||||||
|
- Add `autogroup:internet` to Policy [#1917](https://github.com/juanfont/headscale/pull/1917)
|
||||||
|
|
||||||
## 0.22.3 (2023-05-12)
|
## 0.22.3 (2023-05-12)
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,38 @@ const (
|
||||||
expectedTokenItems = 2
|
expectedTokenItems = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var theInternetSet *netipx.IPSet
|
||||||
|
|
||||||
|
// theInternet returns the IPSet for the Internet.
|
||||||
|
// https://www.youtube.com/watch?v=iDbyYGrswtg
|
||||||
|
func theInternet() *netipx.IPSet {
|
||||||
|
if theInternetSet != nil {
|
||||||
|
return theInternetSet
|
||||||
|
}
|
||||||
|
|
||||||
|
var internetBuilder netipx.IPSetBuilder
|
||||||
|
internetBuilder.AddPrefix(netip.MustParsePrefix("2000::/3"))
|
||||||
|
internetBuilder.AddPrefix(netip.MustParsePrefix("0.0.0.0/0"))
|
||||||
|
|
||||||
|
// Delete Private network addresses
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc1918
|
||||||
|
internetBuilder.RemovePrefix(netip.MustParsePrefix("fc00::/7"))
|
||||||
|
internetBuilder.RemovePrefix(netip.MustParsePrefix("10.0.0.0/8"))
|
||||||
|
internetBuilder.RemovePrefix(netip.MustParsePrefix("172.16.0.0/12"))
|
||||||
|
internetBuilder.RemovePrefix(netip.MustParsePrefix("192.168.0.0/16"))
|
||||||
|
|
||||||
|
// Delete Tailscale networks
|
||||||
|
internetBuilder.RemovePrefix(netip.MustParsePrefix("fd7a:115c:a1e0::/48"))
|
||||||
|
internetBuilder.RemovePrefix(netip.MustParsePrefix("100.64.0.0/10"))
|
||||||
|
|
||||||
|
// Delete "cant find DHCP networks"
|
||||||
|
internetBuilder.RemovePrefix(netip.MustParsePrefix("fe80::/10")) // link-loca
|
||||||
|
internetBuilder.RemovePrefix(netip.MustParsePrefix("169.254.0.0/16"))
|
||||||
|
|
||||||
|
theInternetSet, _ := internetBuilder.IPSet()
|
||||||
|
return theInternetSet
|
||||||
|
}
|
||||||
|
|
||||||
// For some reason golang.org/x/net/internal/iana is an internal package.
|
// For some reason golang.org/x/net/internal/iana is an internal package.
|
||||||
const (
|
const (
|
||||||
protocolICMP = 1 // Internet Control Message
|
protocolICMP = 1 // Internet Control Message
|
||||||
|
@ -221,28 +253,28 @@ func ReduceFilterRules(node *types.Node, rules []tailcfg.FilterRule) []tailcfg.F
|
||||||
// record if the rule is actually relevant for the given node.
|
// record if the rule is actually relevant for the given node.
|
||||||
dests := []tailcfg.NetPortRange{}
|
dests := []tailcfg.NetPortRange{}
|
||||||
|
|
||||||
|
DEST_LOOP:
|
||||||
for _, dest := range rule.DstPorts {
|
for _, dest := range rule.DstPorts {
|
||||||
expanded, err := util.ParseIPSet(dest.IP, nil)
|
expanded, err := util.ParseIPSet(dest.IP, nil)
|
||||||
// Fail closed, if we cant parse it, then we should not allow
|
// Fail closed, if we cant parse it, then we should not allow
|
||||||
// access.
|
// access.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue DEST_LOOP
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.InIPSet(expanded) {
|
if node.InIPSet(expanded) {
|
||||||
dests = append(dests, dest)
|
dests = append(dests, dest)
|
||||||
|
continue DEST_LOOP
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the node exposes routes, ensure they are note removed
|
// If the node exposes routes, ensure they are note removed
|
||||||
// when the filters are reduced.
|
// when the filters are reduced.
|
||||||
if node.Hostinfo != nil {
|
if node.Hostinfo != nil {
|
||||||
// TODO(kradalby): Evaluate if we should only keep
|
|
||||||
// the routes if the route is enabled. This will
|
|
||||||
// require database access in this part of the code.
|
|
||||||
if len(node.Hostinfo.RoutableIPs) > 0 {
|
if len(node.Hostinfo.RoutableIPs) > 0 {
|
||||||
for _, routableIP := range node.Hostinfo.RoutableIPs {
|
for _, routableIP := range node.Hostinfo.RoutableIPs {
|
||||||
if expanded.ContainsPrefix(routableIP) {
|
if expanded.OverlapsPrefix(routableIP) {
|
||||||
dests = append(dests, dest)
|
dests = append(dests, dest)
|
||||||
|
continue DEST_LOOP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -517,6 +549,7 @@ func (pol *ACLPolicy) expandSource(
|
||||||
// - a host
|
// - a host
|
||||||
// - an ip
|
// - an ip
|
||||||
// - a cidr
|
// - a cidr
|
||||||
|
// - an autogroup
|
||||||
// and transform these in IPAddresses.
|
// and transform these in IPAddresses.
|
||||||
func (pol *ACLPolicy) ExpandAlias(
|
func (pol *ACLPolicy) ExpandAlias(
|
||||||
nodes types.Nodes,
|
nodes types.Nodes,
|
||||||
|
@ -542,6 +575,10 @@ func (pol *ACLPolicy) ExpandAlias(
|
||||||
return pol.expandIPsFromTag(alias, nodes)
|
return pol.expandIPsFromTag(alias, nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isAutoGroup(alias) {
|
||||||
|
return expandAutoGroup(alias)
|
||||||
|
}
|
||||||
|
|
||||||
// if alias is a user
|
// if alias is a user
|
||||||
if ips, err := pol.expandIPsFromUser(alias, nodes); ips != nil {
|
if ips, err := pol.expandIPsFromUser(alias, nodes); ips != nil {
|
||||||
return ips, err
|
return ips, err
|
||||||
|
@ -862,6 +899,16 @@ func (pol *ACLPolicy) expandIPsFromIPPrefix(
|
||||||
return build.IPSet()
|
return build.IPSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func expandAutoGroup(alias string) (*netipx.IPSet, error) {
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(alias, "autogroup:internet"):
|
||||||
|
return theInternet(), nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown autogroup %q", alias)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func isWildcard(str string) bool {
|
func isWildcard(str string) bool {
|
||||||
return str == "*"
|
return str == "*"
|
||||||
}
|
}
|
||||||
|
@ -874,6 +921,10 @@ func isTag(str string) bool {
|
||||||
return strings.HasPrefix(str, "tag:")
|
return strings.HasPrefix(str, "tag:")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isAutoGroup(str string) bool {
|
||||||
|
return strings.HasPrefix(str, "autogroup:")
|
||||||
|
}
|
||||||
|
|
||||||
// TagsOfNode will return the tags of the current node.
|
// TagsOfNode will return the tags of the current node.
|
||||||
// Invalid tags are tags added by a user on a node, and that user doesn't have authority to add this tag.
|
// Invalid tags are tags added by a user on a node, and that user doesn't have authority to add this tag.
|
||||||
// Valid tags are tags added by a user that is allowed in the ACL policy to add this tag.
|
// Valid tags are tags added by a user that is allowed in the ACL policy to add this tag.
|
||||||
|
|
|
@ -1765,6 +1765,108 @@ func TestACLPolicy_generateFilterRules(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tsExitNodeDest is the list of destination IP ranges that are allowed when
|
||||||
|
// you dump the filter list from a Tailscale node connected to Tailscale SaaS.
|
||||||
|
var tsExitNodeDest = []tailcfg.NetPortRange{
|
||||||
|
{
|
||||||
|
IP: "0.0.0.0-9.255.255.255",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IP: "11.0.0.0-100.63.255.255",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IP: "100.128.0.0-169.253.255.255",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IP: "169.255.0.0-172.15.255.255",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IP: "172.32.0.0-192.167.255.255",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IP: "192.169.0.0-255.255.255.255",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IP: "2000::-3fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// hsExitNodeDest is the list of destination IP ranges that are allowed when
|
||||||
|
// we use headscale "autogroup:internet"
|
||||||
|
var hsExitNodeDest = []tailcfg.NetPortRange{
|
||||||
|
{IP: "0.0.0.0/5", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "8.0.0.0/7", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "11.0.0.0/8", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "12.0.0.0/6", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "16.0.0.0/4", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "32.0.0.0/3", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "64.0.0.0/3", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "96.0.0.0/6", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "100.0.0.0/10", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "100.128.0.0/9", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "101.0.0.0/8", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "102.0.0.0/7", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "104.0.0.0/5", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "112.0.0.0/4", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "128.0.0.0/3", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "160.0.0.0/5", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "168.0.0.0/8", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "169.0.0.0/9", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "169.128.0.0/10", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "169.192.0.0/11", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "169.224.0.0/12", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "169.240.0.0/13", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "169.248.0.0/14", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "169.252.0.0/15", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "169.255.0.0/16", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "170.0.0.0/7", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "172.0.0.0/12", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "172.32.0.0/11", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "172.64.0.0/10", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "172.128.0.0/9", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "173.0.0.0/8", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "174.0.0.0/7", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "176.0.0.0/4", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "192.0.0.0/9", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "192.128.0.0/11", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "192.160.0.0/13", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "192.169.0.0/16", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "192.170.0.0/15", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "192.172.0.0/14", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "192.176.0.0/12", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "192.192.0.0/10", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "193.0.0.0/8", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "194.0.0.0/7", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "196.0.0.0/6", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "200.0.0.0/5", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "208.0.0.0/4", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "224.0.0.0/3", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "2000::/3", Ports: tailcfg.PortRangeAny},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTheInternet(t *testing.T) {
|
||||||
|
internetSet := theInternet()
|
||||||
|
|
||||||
|
internetPrefs := internetSet.Prefixes()
|
||||||
|
|
||||||
|
for i, _ := range internetPrefs {
|
||||||
|
if internetPrefs[i].String() != hsExitNodeDest[i].IP {
|
||||||
|
t.Errorf("prefix from internet set %q != hsExit list %q", internetPrefs[i].String(), hsExitNodeDest[i].IP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(internetPrefs) != len(hsExitNodeDest) {
|
||||||
|
t.Fatalf("expected same length of prefixes, internet: %d, hsExit: %d", len(internetPrefs), len(hsExitNodeDest))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestReduceFilterRules(t *testing.T) {
|
func TestReduceFilterRules(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -1869,15 +1971,473 @@ func TestReduceFilterRules(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "1786-reducing-breaks-exit-nodes-the-client",
|
||||||
|
pol: ACLPolicy{
|
||||||
|
Hosts: Hosts{
|
||||||
|
// Exit node
|
||||||
|
"internal": netip.MustParsePrefix("100.64.0.100/32"),
|
||||||
|
},
|
||||||
|
Groups: Groups{
|
||||||
|
"group:team": {"user3", "user2", "user1"},
|
||||||
|
},
|
||||||
|
ACLs: []ACL{
|
||||||
|
{
|
||||||
|
Action: "accept",
|
||||||
|
Sources: []string{"group:team"},
|
||||||
|
Destinations: []string{
|
||||||
|
"internal:*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Action: "accept",
|
||||||
|
Sources: []string{"group:team"},
|
||||||
|
Destinations: []string{
|
||||||
|
"autogroup:internet:*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
node: &types.Node{
|
||||||
|
IPv4: iap("100.64.0.1"),
|
||||||
|
IPv6: iap("fd7a:115c:a1e0::1"),
|
||||||
|
User: types.User{Name: "user1"},
|
||||||
|
},
|
||||||
|
peers: types.Nodes{
|
||||||
|
&types.Node{
|
||||||
|
IPv4: iap("100.64.0.2"),
|
||||||
|
IPv6: iap("fd7a:115c:a1e0::2"),
|
||||||
|
User: types.User{Name: "user2"},
|
||||||
|
},
|
||||||
|
// "internal" exit node
|
||||||
|
&types.Node{
|
||||||
|
IPv4: iap("100.64.0.100"),
|
||||||
|
IPv6: iap("fd7a:115c:a1e0::100"),
|
||||||
|
User: types.User{Name: "user100"},
|
||||||
|
Hostinfo: &tailcfg.Hostinfo{
|
||||||
|
RoutableIPs: []netip.Prefix{types.ExitRouteV4, types.ExitRouteV6},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []tailcfg.FilterRule{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "1786-reducing-breaks-exit-nodes-the-exit",
|
||||||
|
pol: ACLPolicy{
|
||||||
|
Hosts: Hosts{
|
||||||
|
// Exit node
|
||||||
|
"internal": netip.MustParsePrefix("100.64.0.100/32"),
|
||||||
|
},
|
||||||
|
Groups: Groups{
|
||||||
|
"group:team": {"user3", "user2", "user1"},
|
||||||
|
},
|
||||||
|
ACLs: []ACL{
|
||||||
|
{
|
||||||
|
Action: "accept",
|
||||||
|
Sources: []string{"group:team"},
|
||||||
|
Destinations: []string{
|
||||||
|
"internal:*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Action: "accept",
|
||||||
|
Sources: []string{"group:team"},
|
||||||
|
Destinations: []string{
|
||||||
|
"autogroup:internet:*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
node: &types.Node{
|
||||||
|
IPv4: iap("100.64.0.100"),
|
||||||
|
IPv6: iap("fd7a:115c:a1e0::100"),
|
||||||
|
User: types.User{Name: "user100"},
|
||||||
|
Hostinfo: &tailcfg.Hostinfo{
|
||||||
|
RoutableIPs: []netip.Prefix{types.ExitRouteV4, types.ExitRouteV6},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
peers: types.Nodes{
|
||||||
|
&types.Node{
|
||||||
|
IPv4: iap("100.64.0.2"),
|
||||||
|
IPv6: iap("fd7a:115c:a1e0::2"),
|
||||||
|
User: types.User{Name: "user2"},
|
||||||
|
},
|
||||||
|
&types.Node{
|
||||||
|
IPv4: iap("100.64.0.1"),
|
||||||
|
IPv6: iap("fd7a:115c:a1e0::1"),
|
||||||
|
User: types.User{Name: "user1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []tailcfg.FilterRule{
|
||||||
|
{
|
||||||
|
SrcIPs: []string{"100.64.0.1/32", "100.64.0.2/32", "fd7a:115c:a1e0::1/128", "fd7a:115c:a1e0::2/128"},
|
||||||
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{
|
||||||
|
IP: "100.64.0.100/32",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IP: "fd7a:115c:a1e0::100/128",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SrcIPs: []string{"100.64.0.1/32", "100.64.0.2/32", "fd7a:115c:a1e0::1/128", "fd7a:115c:a1e0::2/128"},
|
||||||
|
DstPorts: hsExitNodeDest,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "1786-reducing-breaks-exit-nodes-the-example-from-issue",
|
||||||
|
pol: ACLPolicy{
|
||||||
|
Hosts: Hosts{
|
||||||
|
// Exit node
|
||||||
|
"internal": netip.MustParsePrefix("100.64.0.100/32"),
|
||||||
|
},
|
||||||
|
Groups: Groups{
|
||||||
|
"group:team": {"user3", "user2", "user1"},
|
||||||
|
},
|
||||||
|
ACLs: []ACL{
|
||||||
|
{
|
||||||
|
Action: "accept",
|
||||||
|
Sources: []string{"group:team"},
|
||||||
|
Destinations: []string{
|
||||||
|
"internal:*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Action: "accept",
|
||||||
|
Sources: []string{"group:team"},
|
||||||
|
Destinations: []string{
|
||||||
|
"0.0.0.0/5:*",
|
||||||
|
"8.0.0.0/7:*",
|
||||||
|
"11.0.0.0/8:*",
|
||||||
|
"12.0.0.0/6:*",
|
||||||
|
"16.0.0.0/4:*",
|
||||||
|
"32.0.0.0/3:*",
|
||||||
|
"64.0.0.0/2:*",
|
||||||
|
"128.0.0.0/3:*",
|
||||||
|
"160.0.0.0/5:*",
|
||||||
|
"168.0.0.0/6:*",
|
||||||
|
"172.0.0.0/12:*",
|
||||||
|
"172.32.0.0/11:*",
|
||||||
|
"172.64.0.0/10:*",
|
||||||
|
"172.128.0.0/9:*",
|
||||||
|
"173.0.0.0/8:*",
|
||||||
|
"174.0.0.0/7:*",
|
||||||
|
"176.0.0.0/4:*",
|
||||||
|
"192.0.0.0/9:*",
|
||||||
|
"192.128.0.0/11:*",
|
||||||
|
"192.160.0.0/13:*",
|
||||||
|
"192.169.0.0/16:*",
|
||||||
|
"192.170.0.0/15:*",
|
||||||
|
"192.172.0.0/14:*",
|
||||||
|
"192.176.0.0/12:*",
|
||||||
|
"192.192.0.0/10:*",
|
||||||
|
"193.0.0.0/8:*",
|
||||||
|
"194.0.0.0/7:*",
|
||||||
|
"196.0.0.0/6:*",
|
||||||
|
"200.0.0.0/5:*",
|
||||||
|
"208.0.0.0/4:*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
node: &types.Node{
|
||||||
|
IPv4: iap("100.64.0.100"),
|
||||||
|
IPv6: iap("fd7a:115c:a1e0::100"),
|
||||||
|
User: types.User{Name: "user100"},
|
||||||
|
Hostinfo: &tailcfg.Hostinfo{
|
||||||
|
RoutableIPs: []netip.Prefix{types.ExitRouteV4, types.ExitRouteV6},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
peers: types.Nodes{
|
||||||
|
&types.Node{
|
||||||
|
IPv4: iap("100.64.0.2"),
|
||||||
|
IPv6: iap("fd7a:115c:a1e0::2"),
|
||||||
|
User: types.User{Name: "user2"},
|
||||||
|
},
|
||||||
|
&types.Node{
|
||||||
|
IPv4: iap("100.64.0.1"),
|
||||||
|
IPv6: iap("fd7a:115c:a1e0::1"),
|
||||||
|
User: types.User{Name: "user1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []tailcfg.FilterRule{
|
||||||
|
{
|
||||||
|
SrcIPs: []string{"100.64.0.1/32", "100.64.0.2/32", "fd7a:115c:a1e0::1/128", "fd7a:115c:a1e0::2/128"},
|
||||||
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{
|
||||||
|
IP: "100.64.0.100/32",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IP: "fd7a:115c:a1e0::100/128",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SrcIPs: []string{"100.64.0.1/32", "100.64.0.2/32", "fd7a:115c:a1e0::1/128", "fd7a:115c:a1e0::2/128"},
|
||||||
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{IP: "0.0.0.0/5", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "8.0.0.0/7", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "11.0.0.0/8", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "12.0.0.0/6", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "16.0.0.0/4", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "32.0.0.0/3", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "64.0.0.0/2", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "fd7a:115c:a1e0::1/128", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "fd7a:115c:a1e0::2/128", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "fd7a:115c:a1e0::100/128", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "128.0.0.0/3", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "160.0.0.0/5", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "168.0.0.0/6", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "172.0.0.0/12", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "172.32.0.0/11", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "172.64.0.0/10", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "172.128.0.0/9", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "173.0.0.0/8", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "174.0.0.0/7", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "176.0.0.0/4", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "192.0.0.0/9", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "192.128.0.0/11", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "192.160.0.0/13", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "192.169.0.0/16", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "192.170.0.0/15", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "192.172.0.0/14", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "192.176.0.0/12", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "192.192.0.0/10", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "193.0.0.0/8", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "194.0.0.0/7", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "196.0.0.0/6", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "200.0.0.0/5", Ports: tailcfg.PortRangeAny},
|
||||||
|
{IP: "208.0.0.0/4", Ports: tailcfg.PortRangeAny},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "1786-reducing-breaks-exit-nodes-app-connector-like",
|
||||||
|
pol: ACLPolicy{
|
||||||
|
Hosts: Hosts{
|
||||||
|
// Exit node
|
||||||
|
"internal": netip.MustParsePrefix("100.64.0.100/32"),
|
||||||
|
},
|
||||||
|
Groups: Groups{
|
||||||
|
"group:team": {"user3", "user2", "user1"},
|
||||||
|
},
|
||||||
|
ACLs: []ACL{
|
||||||
|
{
|
||||||
|
Action: "accept",
|
||||||
|
Sources: []string{"group:team"},
|
||||||
|
Destinations: []string{
|
||||||
|
"internal:*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Action: "accept",
|
||||||
|
Sources: []string{"group:team"},
|
||||||
|
Destinations: []string{
|
||||||
|
"8.0.0.0/8:*",
|
||||||
|
"16.0.0.0/8:*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
node: &types.Node{
|
||||||
|
IPv4: iap("100.64.0.100"),
|
||||||
|
IPv6: iap("fd7a:115c:a1e0::100"),
|
||||||
|
User: types.User{Name: "user100"},
|
||||||
|
Hostinfo: &tailcfg.Hostinfo{
|
||||||
|
RoutableIPs: []netip.Prefix{netip.MustParsePrefix("8.0.0.0/16"), netip.MustParsePrefix("16.0.0.0/16")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
peers: types.Nodes{
|
||||||
|
&types.Node{
|
||||||
|
IPv4: iap("100.64.0.2"),
|
||||||
|
IPv6: iap("fd7a:115c:a1e0::2"),
|
||||||
|
User: types.User{Name: "user2"},
|
||||||
|
},
|
||||||
|
&types.Node{
|
||||||
|
IPv4: iap("100.64.0.1"),
|
||||||
|
IPv6: iap("fd7a:115c:a1e0::1"),
|
||||||
|
User: types.User{Name: "user1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []tailcfg.FilterRule{
|
||||||
|
{
|
||||||
|
SrcIPs: []string{"100.64.0.1/32", "100.64.0.2/32", "fd7a:115c:a1e0::1/128", "fd7a:115c:a1e0::2/128"},
|
||||||
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{
|
||||||
|
IP: "100.64.0.100/32",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IP: "fd7a:115c:a1e0::100/128",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SrcIPs: []string{"100.64.0.1/32", "100.64.0.2/32", "fd7a:115c:a1e0::1/128", "fd7a:115c:a1e0::2/128"},
|
||||||
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{
|
||||||
|
IP: "8.0.0.0/8",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IP: "16.0.0.0/8",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "1786-reducing-breaks-exit-nodes-app-connector-like2",
|
||||||
|
pol: ACLPolicy{
|
||||||
|
Hosts: Hosts{
|
||||||
|
// Exit node
|
||||||
|
"internal": netip.MustParsePrefix("100.64.0.100/32"),
|
||||||
|
},
|
||||||
|
Groups: Groups{
|
||||||
|
"group:team": {"user3", "user2", "user1"},
|
||||||
|
},
|
||||||
|
ACLs: []ACL{
|
||||||
|
{
|
||||||
|
Action: "accept",
|
||||||
|
Sources: []string{"group:team"},
|
||||||
|
Destinations: []string{
|
||||||
|
"internal:*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Action: "accept",
|
||||||
|
Sources: []string{"group:team"},
|
||||||
|
Destinations: []string{
|
||||||
|
"8.0.0.0/16:*",
|
||||||
|
"16.0.0.0/16:*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
node: &types.Node{
|
||||||
|
IPv4: iap("100.64.0.100"),
|
||||||
|
IPv6: iap("fd7a:115c:a1e0::100"),
|
||||||
|
User: types.User{Name: "user100"},
|
||||||
|
Hostinfo: &tailcfg.Hostinfo{
|
||||||
|
RoutableIPs: []netip.Prefix{netip.MustParsePrefix("8.0.0.0/8"), netip.MustParsePrefix("16.0.0.0/8")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
peers: types.Nodes{
|
||||||
|
&types.Node{
|
||||||
|
IPv4: iap("100.64.0.2"),
|
||||||
|
IPv6: iap("fd7a:115c:a1e0::2"),
|
||||||
|
User: types.User{Name: "user2"},
|
||||||
|
},
|
||||||
|
&types.Node{
|
||||||
|
IPv4: iap("100.64.0.1"),
|
||||||
|
IPv6: iap("fd7a:115c:a1e0::1"),
|
||||||
|
User: types.User{Name: "user1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []tailcfg.FilterRule{
|
||||||
|
{
|
||||||
|
SrcIPs: []string{"100.64.0.1/32", "100.64.0.2/32", "fd7a:115c:a1e0::1/128", "fd7a:115c:a1e0::2/128"},
|
||||||
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{
|
||||||
|
IP: "100.64.0.100/32",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IP: "fd7a:115c:a1e0::100/128",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SrcIPs: []string{"100.64.0.1/32", "100.64.0.2/32", "fd7a:115c:a1e0::1/128", "fd7a:115c:a1e0::2/128"},
|
||||||
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{
|
||||||
|
IP: "8.0.0.0/16",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IP: "16.0.0.0/16",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "1817-reduce-breaks-32-mask",
|
||||||
|
pol: ACLPolicy{
|
||||||
|
Hosts: Hosts{
|
||||||
|
"vlan1": netip.MustParsePrefix("172.16.0.0/24"),
|
||||||
|
"dns1": netip.MustParsePrefix("172.16.0.21/32"),
|
||||||
|
},
|
||||||
|
Groups: Groups{
|
||||||
|
"group:access": {"user1"},
|
||||||
|
},
|
||||||
|
ACLs: []ACL{
|
||||||
|
{
|
||||||
|
Action: "accept",
|
||||||
|
Sources: []string{"group:access"},
|
||||||
|
Destinations: []string{
|
||||||
|
"tag:access-servers:*",
|
||||||
|
"dns1:*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
node: &types.Node{
|
||||||
|
IPv4: iap("100.64.0.100"),
|
||||||
|
IPv6: iap("fd7a:115c:a1e0::100"),
|
||||||
|
User: types.User{Name: "user100"},
|
||||||
|
Hostinfo: &tailcfg.Hostinfo{
|
||||||
|
RoutableIPs: []netip.Prefix{netip.MustParsePrefix("172.16.0.0/24")},
|
||||||
|
},
|
||||||
|
ForcedTags: types.StringList{"tag:access-servers"},
|
||||||
|
},
|
||||||
|
peers: types.Nodes{
|
||||||
|
&types.Node{
|
||||||
|
IPv4: iap("100.64.0.1"),
|
||||||
|
IPv6: iap("fd7a:115c:a1e0::1"),
|
||||||
|
User: types.User{Name: "user1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []tailcfg.FilterRule{
|
||||||
|
{
|
||||||
|
SrcIPs: []string{"100.64.0.1/32", "fd7a:115c:a1e0::1/128"},
|
||||||
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{
|
||||||
|
IP: "100.64.0.100/32",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IP: "fd7a:115c:a1e0::100/128",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IP: "172.16.0.21/32",
|
||||||
|
Ports: tailcfg.PortRangeAny,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
rules, _ := tt.pol.CompileFilterRules(
|
got, _ := tt.pol.CompileFilterRules(
|
||||||
append(tt.peers, tt.node),
|
append(tt.peers, tt.node),
|
||||||
)
|
)
|
||||||
|
|
||||||
got := ReduceFilterRules(tt.node, rules)
|
got = ReduceFilterRules(tt.node, got)
|
||||||
|
|
||||||
if diff := cmp.Diff(tt.want, got); diff != "" {
|
if diff := cmp.Diff(tt.want, got); diff != "" {
|
||||||
log.Trace().Interface("got", got).Msg("result")
|
log.Trace().Interface("got", got).Msg("result")
|
||||||
|
|
Loading…
Reference in a new issue