only send relevant filterrules to nodes
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
parent
2675ff4b94
commit
88ca2501d1
4 changed files with 489 additions and 454 deletions
|
@ -125,122 +125,6 @@ func TestInvalidTagValidUser(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPortGroup(t *testing.T) {
|
|
||||||
machine := types.Machine{
|
|
||||||
ID: 0,
|
|
||||||
MachineKey: "foo",
|
|
||||||
NodeKey: "bar",
|
|
||||||
DiscoKey: "faa",
|
|
||||||
Hostname: "testmachine",
|
|
||||||
UserID: 0,
|
|
||||||
User: types.User{
|
|
||||||
Name: "testuser",
|
|
||||||
},
|
|
||||||
RegisterMethod: util.RegisterMethodAuthKey,
|
|
||||||
IPAddresses: types.MachineAddresses{netip.MustParseAddr("100.64.0.5")},
|
|
||||||
}
|
|
||||||
|
|
||||||
acl := []byte(`
|
|
||||||
{
|
|
||||||
"groups": {
|
|
||||||
"group:example": [
|
|
||||||
"testuser",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
"hosts": {
|
|
||||||
"host-1": "100.100.100.100",
|
|
||||||
"subnet-1": "100.100.101.100/24",
|
|
||||||
},
|
|
||||||
|
|
||||||
"acls": [
|
|
||||||
{
|
|
||||||
"action": "accept",
|
|
||||||
"src": [
|
|
||||||
"group:example",
|
|
||||||
],
|
|
||||||
"dst": [
|
|
||||||
"host-1:*",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
pol, err := policy.LoadACLPolicyFromBytes(acl, "hujson")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
got, _, err := policy.GenerateFilterRules(pol, &machine, types.Machines{})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
want := []tailcfg.FilterRule{
|
|
||||||
{
|
|
||||||
SrcIPs: []string{"100.64.0.5/32"},
|
|
||||||
DstPorts: []tailcfg.NetPortRange{
|
|
||||||
{IP: "100.100.100.100/32", Ports: tailcfg.PortRange{Last: 65535}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if diff := cmp.Diff(want, got); diff != "" {
|
|
||||||
t.Errorf("TestPortGroup() unexpected result (-want +got):\n%s", diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPortUser(t *testing.T) {
|
|
||||||
machine := types.Machine{
|
|
||||||
ID: 0,
|
|
||||||
MachineKey: "12345",
|
|
||||||
NodeKey: "bar",
|
|
||||||
DiscoKey: "faa",
|
|
||||||
Hostname: "testmachine",
|
|
||||||
UserID: 0,
|
|
||||||
User: types.User{
|
|
||||||
Name: "testuser",
|
|
||||||
},
|
|
||||||
RegisterMethod: util.RegisterMethodAuthKey,
|
|
||||||
IPAddresses: types.MachineAddresses{netip.MustParseAddr("100.64.0.9")},
|
|
||||||
}
|
|
||||||
|
|
||||||
acl := []byte(`
|
|
||||||
{
|
|
||||||
"hosts": {
|
|
||||||
"host-1": "100.100.100.100",
|
|
||||||
"subnet-1": "100.100.101.100/24",
|
|
||||||
},
|
|
||||||
|
|
||||||
"acls": [
|
|
||||||
{
|
|
||||||
"action": "accept",
|
|
||||||
"src": [
|
|
||||||
"testuser",
|
|
||||||
],
|
|
||||||
"dst": [
|
|
||||||
"host-1:*",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
pol, err := policy.LoadACLPolicyFromBytes(acl, "hujson")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
got, _, err := policy.GenerateFilterRules(pol, &machine, types.Machines{})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
want := []tailcfg.FilterRule{
|
|
||||||
{
|
|
||||||
SrcIPs: []string{"100.64.0.9/32"},
|
|
||||||
DstPorts: []tailcfg.NetPortRange{
|
|
||||||
{IP: "100.100.100.100/32", Ports: tailcfg.PortRange{Last: 65535}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if diff := cmp.Diff(want, got); diff != "" {
|
|
||||||
t.Errorf("TestPortUser() unexpected result (-want +got):\n%s", diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this test should validate that we can expand a group in a TagOWner section and
|
// this test should validate that we can expand a group in a TagOWner section and
|
||||||
// match properly the IP's of the related hosts. The owner is valid and the tag is also valid.
|
// match properly the IP's of the related hosts. The owner is valid and the tag is also valid.
|
||||||
// the tag is matched in the Destinations section.
|
// the tag is matched in the Destinations section.
|
||||||
|
|
|
@ -403,8 +403,8 @@ func Test_fullMapResponse(t *testing.T) {
|
||||||
ACLs: []policy.ACL{
|
ACLs: []policy.ACL{
|
||||||
{
|
{
|
||||||
Action: "accept",
|
Action: "accept",
|
||||||
Sources: []string{"mini"},
|
Sources: []string{"100.64.0.2"},
|
||||||
Destinations: []string{"100.64.0.2:*"},
|
Destinations: []string{"mini:*"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -430,8 +430,9 @@ func Test_fullMapResponse(t *testing.T) {
|
||||||
CollectServices: "false",
|
CollectServices: "false",
|
||||||
PacketFilter: []tailcfg.FilterRule{
|
PacketFilter: []tailcfg.FilterRule{
|
||||||
{
|
{
|
||||||
SrcIPs: []string{"100.64.0.1/32", "100.64.0.2/32"},
|
SrcIPs: []string{"100.64.0.2/32"},
|
||||||
DstPorts: []tailcfg.NetPortRange{
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{IP: "100.64.0.1/32", Ports: tailcfg.PortRangeAny},
|
||||||
{IP: "100.64.0.2/32", Ports: tailcfg.PortRangeAny},
|
{IP: "100.64.0.2/32", Ports: tailcfg.PortRangeAny},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -177,7 +177,7 @@ func (pol *ACLPolicy) generateFilterRules(
|
||||||
srcIPs = append(srcIPs, srcs...)
|
srcIPs = append(srcIPs, srcs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
protocols, needsWildcard, err := parseProtocol(acl.Protocol)
|
protocols, isWildcard, err := parseProtocol(acl.Protocol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
Msgf("Error parsing ACL %d. protocol unknown %s", index, acl.Protocol)
|
Msgf("Error parsing ACL %d. protocol unknown %s", index, acl.Protocol)
|
||||||
|
@ -185,25 +185,52 @@ func (pol *ACLPolicy) generateFilterRules(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
destPorts := []tailcfg.NetPortRange{}
|
// record if the rule is actually relevant for the given machine.
|
||||||
for destIndex, dest := range acl.Destinations {
|
isRelevant := false
|
||||||
dests, err := pol.getNetPortRangeFromDestination(
|
|
||||||
dest,
|
|
||||||
machines,
|
|
||||||
needsWildcard,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Error().
|
|
||||||
Interface("dest", dest).
|
|
||||||
Int("ACL index", index).
|
|
||||||
Int("dest index", destIndex).
|
|
||||||
Msgf("Error parsing ACL")
|
|
||||||
|
|
||||||
|
destPorts := []tailcfg.NetPortRange{}
|
||||||
|
for _, dest := range acl.Destinations {
|
||||||
|
alias, port, err := parseDestination(dest)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expanded, err := pol.ExpandAlias(
|
||||||
|
machines,
|
||||||
|
alias,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if machine.IPAddresses.InIPSet(expanded) {
|
||||||
|
isRelevant = true
|
||||||
|
}
|
||||||
|
|
||||||
|
ports, err := expandPorts(port, isWildcard)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dests := []tailcfg.NetPortRange{}
|
||||||
|
for _, dest := range expanded.Prefixes() {
|
||||||
|
for _, port := range *ports {
|
||||||
|
pr := tailcfg.NetPortRange{
|
||||||
|
IP: dest.String(),
|
||||||
|
Ports: port,
|
||||||
|
}
|
||||||
|
dests = append(dests, pr)
|
||||||
|
}
|
||||||
|
}
|
||||||
destPorts = append(destPorts, dests...)
|
destPorts = append(destPorts, dests...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if the rule does not apply to the machine we are evaluating,
|
||||||
|
// do not add it to the list and continue.
|
||||||
|
if !isRelevant {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
rules = append(rules, tailcfg.FilterRule{
|
rules = append(rules, tailcfg.FilterRule{
|
||||||
SrcIPs: srcIPs,
|
SrcIPs: srcIPs,
|
||||||
DstPorts: destPorts,
|
DstPorts: destPorts,
|
||||||
|
@ -368,44 +395,6 @@ func (pol *ACLPolicy) getIPsFromSource(
|
||||||
return prefixes, nil
|
return prefixes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getNetPortRangeFromDestination returns a set of tailcfg.NetPortRange
|
|
||||||
// which are associated with the dest alias.
|
|
||||||
func (pol *ACLPolicy) getNetPortRangeFromDestination(
|
|
||||||
dest string,
|
|
||||||
machines types.Machines,
|
|
||||||
needsWildcard bool,
|
|
||||||
) ([]tailcfg.NetPortRange, error) {
|
|
||||||
alias, port, err := parseDestination(dest)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
expanded, err := pol.ExpandAlias(
|
|
||||||
machines,
|
|
||||||
alias,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ports, err := expandPorts(port, needsWildcard)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
dests := []tailcfg.NetPortRange{}
|
|
||||||
for _, dest := range expanded.Prefixes() {
|
|
||||||
for _, port := range *ports {
|
|
||||||
pr := tailcfg.NetPortRange{
|
|
||||||
IP: dest.String(),
|
|
||||||
Ports: port,
|
|
||||||
}
|
|
||||||
dests = append(dests, pr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dests, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseDestination(dest string) (string, string, error) {
|
func parseDestination(dest string) (string, string, error) {
|
||||||
var tokens []string
|
var tokens []string
|
||||||
|
|
||||||
|
@ -605,14 +594,14 @@ func excludeCorrectlyTaggedNodes(
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func expandPorts(portsStr string, needsWildcard bool) (*[]tailcfg.PortRange, error) {
|
func expandPorts(portsStr string, isWild bool) (*[]tailcfg.PortRange, error) {
|
||||||
if isWildcard(portsStr) {
|
if isWildcard(portsStr) {
|
||||||
return &[]tailcfg.PortRange{
|
return &[]tailcfg.PortRange{
|
||||||
{First: portRangeBegin, Last: portRangeEnd},
|
{First: portRangeBegin, Last: portRangeEnd},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if needsWildcard {
|
if isWild {
|
||||||
return nil, ErrWildcardIsNeeded
|
return nil, ErrWildcardIsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,42 +30,388 @@ func (s *Suite) TestWrongPath(c *check.C) {
|
||||||
c.Assert(err, check.NotNil)
|
c.Assert(err, check.NotNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestBrokenHuJson(c *check.C) {
|
func TestParsing(t *testing.T) {
|
||||||
acl := []byte(`
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
format string
|
||||||
|
acl string
|
||||||
|
want []tailcfg.FilterRule
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "invalid-hujson",
|
||||||
|
format: "hujson",
|
||||||
|
acl: `
|
||||||
{
|
{
|
||||||
`)
|
`,
|
||||||
_, err := LoadACLPolicyFromBytes(acl, "hujson")
|
want: []tailcfg.FilterRule{},
|
||||||
c.Assert(err, check.NotNil)
|
wantErr: true,
|
||||||
}
|
},
|
||||||
|
{
|
||||||
func (s *Suite) TestInvalidPolicyHuson(c *check.C) {
|
name: "valid-hujson-invalid-content",
|
||||||
acl := []byte(`
|
format: "hujson",
|
||||||
|
acl: `
|
||||||
{
|
{
|
||||||
"valid_json": true,
|
"valid_json": true,
|
||||||
"but_a_policy_though": false
|
"but_a_policy_though": false
|
||||||
}
|
|
||||||
`)
|
|
||||||
_, err := LoadACLPolicyFromBytes(acl, "hujson")
|
|
||||||
c.Assert(err, check.NotNil)
|
|
||||||
c.Assert(err, check.Equals, ErrEmptyPolicy)
|
|
||||||
}
|
}
|
||||||
|
`,
|
||||||
|
want: []tailcfg.FilterRule{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid-cidr",
|
||||||
|
format: "hujson",
|
||||||
|
acl: `
|
||||||
|
{"example-host-1": "100.100.100.100/42"}
|
||||||
|
`,
|
||||||
|
want: []tailcfg.FilterRule{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "basic-rule",
|
||||||
|
format: "hujson",
|
||||||
|
acl: `
|
||||||
|
{
|
||||||
|
"hosts": {
|
||||||
|
"host-1": "100.100.100.100",
|
||||||
|
"subnet-1": "100.100.101.100/24",
|
||||||
|
},
|
||||||
|
|
||||||
func (s *Suite) TestParseHosts(c *check.C) {
|
"acls": [
|
||||||
var hosts Hosts
|
{
|
||||||
err := hosts.UnmarshalJSON(
|
"action": "accept",
|
||||||
[]byte(
|
"src": [
|
||||||
`{"example-host-1": "100.100.100.100","example-host-2": "100.100.101.100/24"}`,
|
"subnet-1",
|
||||||
),
|
"192.168.1.0/24"
|
||||||
)
|
],
|
||||||
c.Assert(hosts, check.NotNil)
|
"dst": [
|
||||||
c.Assert(err, check.IsNil)
|
"*:22,3389",
|
||||||
|
"host-1:*",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
`,
|
||||||
|
want: []tailcfg.FilterRule{
|
||||||
|
{
|
||||||
|
SrcIPs: []string{"100.100.101.0/24", "192.168.1.0/24"},
|
||||||
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{IP: "0.0.0.0/0", Ports: tailcfg.PortRange{First: 22, Last: 22}},
|
||||||
|
{IP: "0.0.0.0/0", Ports: tailcfg.PortRange{First: 3389, Last: 3389}},
|
||||||
|
{IP: "::/0", Ports: tailcfg.PortRange{First: 22, Last: 22}},
|
||||||
|
{IP: "::/0", Ports: tailcfg.PortRange{First: 3389, Last: 3389}},
|
||||||
|
{IP: "100.100.100.100/32", Ports: tailcfg.PortRangeAny},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "parse-protocol",
|
||||||
|
format: "hujson",
|
||||||
|
acl: `
|
||||||
|
{
|
||||||
|
"hosts": {
|
||||||
|
"host-1": "100.100.100.100",
|
||||||
|
"subnet-1": "100.100.101.100/24",
|
||||||
|
},
|
||||||
|
|
||||||
func (s *Suite) TestParseInvalidCIDR(c *check.C) {
|
"acls": [
|
||||||
var hosts Hosts
|
{
|
||||||
err := hosts.UnmarshalJSON([]byte(`{"example-host-1": "100.100.100.100/42"}`))
|
"Action": "accept",
|
||||||
c.Assert(hosts, check.IsNil)
|
"src": [
|
||||||
c.Assert(err, check.NotNil)
|
"*",
|
||||||
|
],
|
||||||
|
"proto": "tcp",
|
||||||
|
"dst": [
|
||||||
|
"host-1:*",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Action": "accept",
|
||||||
|
"src": [
|
||||||
|
"*",
|
||||||
|
],
|
||||||
|
"proto": "udp",
|
||||||
|
"dst": [
|
||||||
|
"host-1:53",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Action": "accept",
|
||||||
|
"src": [
|
||||||
|
"*",
|
||||||
|
],
|
||||||
|
"proto": "icmp",
|
||||||
|
"dst": [
|
||||||
|
"host-1:*",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}`,
|
||||||
|
want: []tailcfg.FilterRule{
|
||||||
|
{
|
||||||
|
SrcIPs: []string{"0.0.0.0/0", "::/0"},
|
||||||
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{IP: "100.100.100.100/32", Ports: tailcfg.PortRangeAny},
|
||||||
|
},
|
||||||
|
IPProto: []int{protocolTCP},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SrcIPs: []string{"0.0.0.0/0", "::/0"},
|
||||||
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{IP: "100.100.100.100/32", Ports: tailcfg.PortRange{First: 53, Last: 53}},
|
||||||
|
},
|
||||||
|
IPProto: []int{protocolUDP},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SrcIPs: []string{"0.0.0.0/0", "::/0"},
|
||||||
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{IP: "100.100.100.100/32", Ports: tailcfg.PortRangeAny},
|
||||||
|
},
|
||||||
|
IPProto: []int{protocolICMP, protocolIPv6ICMP},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "port-wildcard",
|
||||||
|
format: "hujson",
|
||||||
|
acl: `
|
||||||
|
{
|
||||||
|
"hosts": {
|
||||||
|
"host-1": "100.100.100.100",
|
||||||
|
"subnet-1": "100.100.101.100/24",
|
||||||
|
},
|
||||||
|
|
||||||
|
"acls": [
|
||||||
|
{
|
||||||
|
"Action": "accept",
|
||||||
|
"src": [
|
||||||
|
"*",
|
||||||
|
],
|
||||||
|
"dst": [
|
||||||
|
"host-1:*",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
want: []tailcfg.FilterRule{
|
||||||
|
{
|
||||||
|
SrcIPs: []string{"0.0.0.0/0", "::/0"},
|
||||||
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{IP: "100.100.100.100/32", Ports: tailcfg.PortRangeAny},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "port-range",
|
||||||
|
format: "hujson",
|
||||||
|
acl: `
|
||||||
|
{
|
||||||
|
"hosts": {
|
||||||
|
"host-1": "100.100.100.100",
|
||||||
|
"subnet-1": "100.100.101.100/24",
|
||||||
|
},
|
||||||
|
|
||||||
|
"acls": [
|
||||||
|
{
|
||||||
|
"action": "accept",
|
||||||
|
"src": [
|
||||||
|
"subnet-1",
|
||||||
|
],
|
||||||
|
"dst": [
|
||||||
|
"host-1:5400-5500",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
want: []tailcfg.FilterRule{
|
||||||
|
{
|
||||||
|
SrcIPs: []string{"100.100.101.0/24"},
|
||||||
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{
|
||||||
|
IP: "100.100.100.100/32",
|
||||||
|
Ports: tailcfg.PortRange{First: 5400, Last: 5500},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "port-group",
|
||||||
|
format: "hujson",
|
||||||
|
acl: `
|
||||||
|
{
|
||||||
|
"groups": {
|
||||||
|
"group:example": [
|
||||||
|
"testuser",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
"hosts": {
|
||||||
|
"host-1": "100.100.100.100",
|
||||||
|
"subnet-1": "100.100.101.100/24",
|
||||||
|
},
|
||||||
|
|
||||||
|
"acls": [
|
||||||
|
{
|
||||||
|
"action": "accept",
|
||||||
|
"src": [
|
||||||
|
"group:example",
|
||||||
|
],
|
||||||
|
"dst": [
|
||||||
|
"host-1:*",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
want: []tailcfg.FilterRule{
|
||||||
|
{
|
||||||
|
SrcIPs: []string{"200.200.200.200/32"},
|
||||||
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{IP: "100.100.100.100/32", Ports: tailcfg.PortRangeAny},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "port-user",
|
||||||
|
format: "hujson",
|
||||||
|
acl: `
|
||||||
|
{
|
||||||
|
"hosts": {
|
||||||
|
"host-1": "100.100.100.100",
|
||||||
|
"subnet-1": "100.100.101.100/24",
|
||||||
|
},
|
||||||
|
|
||||||
|
"acls": [
|
||||||
|
{
|
||||||
|
"action": "accept",
|
||||||
|
"src": [
|
||||||
|
"testuser",
|
||||||
|
],
|
||||||
|
"dst": [
|
||||||
|
"host-1:*",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
want: []tailcfg.FilterRule{
|
||||||
|
{
|
||||||
|
SrcIPs: []string{"200.200.200.200/32"},
|
||||||
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{IP: "100.100.100.100/32", Ports: tailcfg.PortRangeAny},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "port-wildcard-yaml",
|
||||||
|
format: "yaml",
|
||||||
|
acl: `
|
||||||
|
---
|
||||||
|
hosts:
|
||||||
|
host-1: 100.100.100.100/32
|
||||||
|
subnet-1: 100.100.101.100/24
|
||||||
|
acls:
|
||||||
|
- action: accept
|
||||||
|
src:
|
||||||
|
- "*"
|
||||||
|
dst:
|
||||||
|
- host-1:*
|
||||||
|
`,
|
||||||
|
want: []tailcfg.FilterRule{
|
||||||
|
{
|
||||||
|
SrcIPs: []string{"0.0.0.0/0", "::/0"},
|
||||||
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{IP: "100.100.100.100/32", Ports: tailcfg.PortRangeAny},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ipv6-yaml",
|
||||||
|
format: "yaml",
|
||||||
|
acl: `
|
||||||
|
---
|
||||||
|
hosts:
|
||||||
|
host-1: 100.100.100.100/32
|
||||||
|
subnet-1: 100.100.101.100/24
|
||||||
|
acls:
|
||||||
|
- action: accept
|
||||||
|
src:
|
||||||
|
- "*"
|
||||||
|
dst:
|
||||||
|
- host-1:*
|
||||||
|
`,
|
||||||
|
want: []tailcfg.FilterRule{
|
||||||
|
{
|
||||||
|
SrcIPs: []string{"0.0.0.0/0", "::/0"},
|
||||||
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{IP: "100.100.100.100/32", Ports: tailcfg.PortRangeAny},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
pol, err := LoadACLPolicyFromBytes([]byte(tt.acl), tt.format)
|
||||||
|
|
||||||
|
if tt.wantErr && err == nil {
|
||||||
|
t.Errorf("parsing() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
|
||||||
|
return
|
||||||
|
} else if !tt.wantErr && err != nil {
|
||||||
|
t.Errorf("parsing() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rules, err := pol.generateFilterRules(&types.Machine{
|
||||||
|
IPAddresses: types.MachineAddresses{
|
||||||
|
netip.MustParseAddr("100.100.100.100"),
|
||||||
|
},
|
||||||
|
}, types.Machines{
|
||||||
|
types.Machine{
|
||||||
|
IPAddresses: types.MachineAddresses{
|
||||||
|
netip.MustParseAddr("200.200.200.200"),
|
||||||
|
},
|
||||||
|
User: types.User{
|
||||||
|
Name: "testuser",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("parsing() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if diff := cmp.Diff(tt.want, rules); diff != "" {
|
||||||
|
t.Errorf("parsing() unexpected result (-want +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestRuleInvalidGeneration(c *check.C) {
|
func (s *Suite) TestRuleInvalidGeneration(c *check.C) {
|
||||||
|
@ -205,37 +551,6 @@ func (s *Suite) TestRuleInvalidGeneration(c *check.C) {
|
||||||
c.Assert(rules, check.IsNil)
|
c.Assert(rules, check.IsNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestBasicRule(c *check.C) {
|
|
||||||
acl := []byte(`
|
|
||||||
{
|
|
||||||
"hosts": {
|
|
||||||
"host-1": "100.100.100.100",
|
|
||||||
"subnet-1": "100.100.101.100/24",
|
|
||||||
},
|
|
||||||
|
|
||||||
"acls": [
|
|
||||||
{
|
|
||||||
"action": "accept",
|
|
||||||
"src": [
|
|
||||||
"subnet-1",
|
|
||||||
"192.168.1.0/24"
|
|
||||||
],
|
|
||||||
"dst": [
|
|
||||||
"*:22,3389",
|
|
||||||
"host-1:*",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
pol, err := LoadACLPolicyFromBytes(acl, "hujson")
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
|
|
||||||
rules, err := pol.generateFilterRules(&types.Machine{}, types.Machines{})
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
c.Assert(rules, check.NotNil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(kradalby): Make tests values safe, independent and descriptive.
|
// TODO(kradalby): Make tests values safe, independent and descriptive.
|
||||||
func (s *Suite) TestInvalidAction(c *check.C) {
|
func (s *Suite) TestInvalidAction(c *check.C) {
|
||||||
pol := &ACLPolicy{
|
pol := &ACLPolicy{
|
||||||
|
@ -286,199 +601,6 @@ func (s *Suite) TestInvalidTagOwners(c *check.C) {
|
||||||
c.Assert(errors.Is(err, ErrInvalidTag), check.Equals, true)
|
c.Assert(errors.Is(err, ErrInvalidTag), check.Equals, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestPortRange(c *check.C) {
|
|
||||||
acl := []byte(`
|
|
||||||
{
|
|
||||||
"hosts": {
|
|
||||||
"host-1": "100.100.100.100",
|
|
||||||
"subnet-1": "100.100.101.100/24",
|
|
||||||
},
|
|
||||||
|
|
||||||
"acls": [
|
|
||||||
{
|
|
||||||
"action": "accept",
|
|
||||||
"src": [
|
|
||||||
"subnet-1",
|
|
||||||
],
|
|
||||||
"dst": [
|
|
||||||
"host-1:5400-5500",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
pol, err := LoadACLPolicyFromBytes(acl, "hujson")
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
c.Assert(pol, check.NotNil)
|
|
||||||
|
|
||||||
rules, err := pol.generateFilterRules(&types.Machine{}, types.Machines{})
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
c.Assert(rules, check.NotNil)
|
|
||||||
|
|
||||||
c.Assert(rules, check.HasLen, 1)
|
|
||||||
c.Assert(rules[0].DstPorts, check.HasLen, 1)
|
|
||||||
c.Assert(rules[0].DstPorts[0].Ports.First, check.Equals, uint16(5400))
|
|
||||||
c.Assert(rules[0].DstPorts[0].Ports.Last, check.Equals, uint16(5500))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Suite) TestProtocolParsing(c *check.C) {
|
|
||||||
acl := []byte(`
|
|
||||||
{
|
|
||||||
"hosts": {
|
|
||||||
"host-1": "100.100.100.100",
|
|
||||||
"subnet-1": "100.100.101.100/24",
|
|
||||||
},
|
|
||||||
|
|
||||||
"acls": [
|
|
||||||
{
|
|
||||||
"Action": "accept",
|
|
||||||
"src": [
|
|
||||||
"*",
|
|
||||||
],
|
|
||||||
"proto": "tcp",
|
|
||||||
"dst": [
|
|
||||||
"host-1:*",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Action": "accept",
|
|
||||||
"src": [
|
|
||||||
"*",
|
|
||||||
],
|
|
||||||
"proto": "udp",
|
|
||||||
"dst": [
|
|
||||||
"host-1:53",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Action": "accept",
|
|
||||||
"src": [
|
|
||||||
"*",
|
|
||||||
],
|
|
||||||
"proto": "icmp",
|
|
||||||
"dst": [
|
|
||||||
"host-1:*",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
pol, err := LoadACLPolicyFromBytes(acl, "hujson")
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
c.Assert(pol, check.NotNil)
|
|
||||||
|
|
||||||
rules, err := pol.generateFilterRules(&types.Machine{}, types.Machines{})
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
c.Assert(rules, check.NotNil)
|
|
||||||
|
|
||||||
c.Assert(rules, check.HasLen, 3)
|
|
||||||
c.Assert(rules[0].IPProto[0], check.Equals, protocolTCP)
|
|
||||||
c.Assert(rules[1].IPProto[0], check.Equals, protocolUDP)
|
|
||||||
c.Assert(rules[2].IPProto[1], check.Equals, protocolIPv6ICMP)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Suite) TestPortWildcard(c *check.C) {
|
|
||||||
acl := []byte(`
|
|
||||||
{
|
|
||||||
"hosts": {
|
|
||||||
"host-1": "100.100.100.100",
|
|
||||||
"subnet-1": "100.100.101.100/24",
|
|
||||||
},
|
|
||||||
|
|
||||||
"acls": [
|
|
||||||
{
|
|
||||||
"Action": "accept",
|
|
||||||
"src": [
|
|
||||||
"*",
|
|
||||||
],
|
|
||||||
"dst": [
|
|
||||||
"host-1:*",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
pol, err := LoadACLPolicyFromBytes(acl, "hujson")
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
c.Assert(pol, check.NotNil)
|
|
||||||
|
|
||||||
rules, err := pol.generateFilterRules(&types.Machine{}, types.Machines{})
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
c.Assert(rules, check.NotNil)
|
|
||||||
|
|
||||||
c.Assert(rules, check.HasLen, 1)
|
|
||||||
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, 2)
|
|
||||||
c.Assert(rules[0].SrcIPs[0], check.Equals, "0.0.0.0/0")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Suite) TestPortWildcardYAML(c *check.C) {
|
|
||||||
acl := []byte(`---
|
|
||||||
hosts:
|
|
||||||
host-1: 100.100.100.100/32
|
|
||||||
subnet-1: 100.100.101.100/24
|
|
||||||
acls:
|
|
||||||
- action: accept
|
|
||||||
src:
|
|
||||||
- "*"
|
|
||||||
dst:
|
|
||||||
- host-1:*`)
|
|
||||||
pol, err := LoadACLPolicyFromBytes(acl, "yaml")
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
c.Assert(pol, check.NotNil)
|
|
||||||
|
|
||||||
rules, err := pol.generateFilterRules(&types.Machine{}, types.Machines{})
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
c.Assert(rules, check.NotNil)
|
|
||||||
|
|
||||||
c.Assert(rules, check.HasLen, 1)
|
|
||||||
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, 2)
|
|
||||||
c.Assert(rules[0].SrcIPs[0], check.Equals, "0.0.0.0/0")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Suite) TestBasicIpv6YAML(c *check.C) {
|
|
||||||
acl := []byte(`
|
|
||||||
---
|
|
||||||
hosts:
|
|
||||||
host-1: 100.100.100.100/32
|
|
||||||
subnet-1: 100.100.101.100/24
|
|
||||||
acls:
|
|
||||||
- action: accept
|
|
||||||
src:
|
|
||||||
- "*"
|
|
||||||
dst:
|
|
||||||
- 0.0.0.0/0:*
|
|
||||||
- ::/0:*
|
|
||||||
- fd7a:115c:a1e0::2:22
|
|
||||||
`)
|
|
||||||
pol, err := LoadACLPolicyFromBytes(acl, "yaml")
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
c.Assert(pol, check.NotNil)
|
|
||||||
|
|
||||||
rules, err := pol.generateFilterRules(&types.Machine{}, types.Machines{})
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
c.Assert(rules, check.NotNil)
|
|
||||||
|
|
||||||
c.Assert(rules, check.HasLen, 1)
|
|
||||||
c.Assert(rules[0].DstPorts, check.HasLen, 3)
|
|
||||||
c.Assert(rules[0].DstPorts[0].IP, check.Equals, "0.0.0.0/0")
|
|
||||||
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].DstPorts[1].IP, check.Equals, "::/0")
|
|
||||||
c.Assert(rules[0].DstPorts[1].Ports.First, check.Equals, uint16(0))
|
|
||||||
c.Assert(rules[0].DstPorts[1].Ports.Last, check.Equals, uint16(65535))
|
|
||||||
c.Assert(rules[0].DstPorts[2].IP, check.Equals, "fd7a:115c:a1e0::2/128")
|
|
||||||
c.Assert(rules[0].DstPorts[2].Ports.First, check.Equals, uint16(22))
|
|
||||||
c.Assert(rules[0].DstPorts[2].Ports.Last, check.Equals, uint16(22))
|
|
||||||
c.Assert(rules[0].SrcIPs, check.HasLen, 2)
|
|
||||||
c.Assert(rules[0].SrcIPs[0], check.Equals, "0.0.0.0/0")
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_expandGroup(t *testing.T) {
|
func Test_expandGroup(t *testing.T) {
|
||||||
type field struct {
|
type field struct {
|
||||||
pol ACLPolicy
|
pol ACLPolicy
|
||||||
|
@ -1621,8 +1743,13 @@ func TestACLPolicy_generateFilterRules(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
machine: types.Machine{},
|
machine: types.Machine{
|
||||||
peers: types.Machines{},
|
IPAddresses: types.MachineAddresses{
|
||||||
|
netip.MustParseAddr("100.64.0.1"),
|
||||||
|
netip.MustParseAddr("fd7a:115c:a1e0:ab12:4843:2222:6273:2221"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
peers: types.Machines{},
|
||||||
},
|
},
|
||||||
want: []tailcfg.FilterRule{
|
want: []tailcfg.FilterRule{
|
||||||
{
|
{
|
||||||
|
@ -1648,7 +1775,64 @@ func TestACLPolicy_generateFilterRules(t *testing.T) {
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "host1-can-reach-host2",
|
name: "host1-can-reach-host2-no-rules",
|
||||||
|
field: field{
|
||||||
|
pol: ACLPolicy{
|
||||||
|
ACLs: []ACL{
|
||||||
|
{
|
||||||
|
Action: "accept",
|
||||||
|
Sources: []string{"100.64.0.2"},
|
||||||
|
Destinations: []string{"100.64.0.1:*"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
machine: types.Machine{
|
||||||
|
IPAddresses: types.MachineAddresses{
|
||||||
|
netip.MustParseAddr("100.64.0.1"),
|
||||||
|
netip.MustParseAddr("fd7a:115c:a1e0:ab12:4843:2222:6273:2221"),
|
||||||
|
},
|
||||||
|
User: types.User{Name: "mickael"},
|
||||||
|
},
|
||||||
|
peers: types.Machines{
|
||||||
|
{
|
||||||
|
IPAddresses: types.MachineAddresses{
|
||||||
|
netip.MustParseAddr("100.64.0.2"),
|
||||||
|
netip.MustParseAddr("fd7a:115c:a1e0:ab12:4843:2222:6273:2222"),
|
||||||
|
},
|
||||||
|
User: types.User{Name: "mickael"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []tailcfg.FilterRule{
|
||||||
|
{
|
||||||
|
SrcIPs: []string{
|
||||||
|
"100.64.0.2/32",
|
||||||
|
"fd7a:115c:a1e0:ab12:4843:2222:6273:2222/128",
|
||||||
|
},
|
||||||
|
DstPorts: []tailcfg.NetPortRange{
|
||||||
|
{
|
||||||
|
IP: "100.64.0.1/32",
|
||||||
|
Ports: tailcfg.PortRange{
|
||||||
|
First: 0,
|
||||||
|
Last: 65535,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IP: "fd7a:115c:a1e0:ab12:4843:2222:6273:2221/128",
|
||||||
|
Ports: tailcfg.PortRange{
|
||||||
|
First: 0,
|
||||||
|
Last: 65535,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "host1-can-reach-host2-no-rules",
|
||||||
field: field{
|
field: field{
|
||||||
pol: ACLPolicy{
|
pol: ACLPolicy{
|
||||||
ACLs: []ACL{
|
ACLs: []ACL{
|
||||||
|
@ -1678,30 +1862,7 @@ func TestACLPolicy_generateFilterRules(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: []tailcfg.FilterRule{
|
want: []tailcfg.FilterRule{},
|
||||||
{
|
|
||||||
SrcIPs: []string{
|
|
||||||
"100.64.0.1/32",
|
|
||||||
"fd7a:115c:a1e0:ab12:4843:2222:6273:2221/128",
|
|
||||||
},
|
|
||||||
DstPorts: []tailcfg.NetPortRange{
|
|
||||||
{
|
|
||||||
IP: "100.64.0.2/32",
|
|
||||||
Ports: tailcfg.PortRange{
|
|
||||||
First: 0,
|
|
||||||
Last: 65535,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
IP: "fd7a:115c:a1e0:ab12:4843:2222:6273:2222/128",
|
|
||||||
Ports: tailcfg.PortRange{
|
|
||||||
First: 0,
|
|
||||||
Last: 65535,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue