Skip to content

Commit 1afbd65

Browse files
committed
test: add coverage for ACL source range sanitization
1 parent 1d0ef3a commit 1afbd65

1 file changed

Lines changed: 176 additions & 0 deletions

File tree

scaleway/loadbalancers_test.go

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package scaleway
1818

1919
import (
2020
"encoding/json"
21+
"fmt"
2122
"reflect"
2223
"strings"
2324
"testing"
@@ -1530,6 +1531,181 @@ func TestMakeACLPrefix(t *testing.T) {
15301531
}
15311532
}
15321533

1534+
func TestMakeACLSpecs(t *testing.T) {
1535+
allowACL := &scwlb.ACLAction{Type: scwlb.ACLActionTypeAllow}
1536+
denyACL := &scwlb.ACLAction{Type: scwlb.ACLActionTypeDeny}
1537+
denyAllMatch := &scwlb.ACLMatch{IPSubnet: scw.StringSlicePtr([]string{"0.0.0.0/0", "::/0"})}
1538+
1539+
t.Run("no source ranges returns empty acls", func(t *testing.T) {
1540+
got := makeACLSpecs(&v1.Service{}, nil, nil)
1541+
want := []*scwlb.ACLSpec{}
1542+
if !reflect.DeepEqual(got, want) {
1543+
t.Errorf("want: %v, got: %v", want, got)
1544+
}
1545+
})
1546+
1547+
matrix := []struct {
1548+
name string
1549+
sourceRanges []string
1550+
nodes []*v1.Node
1551+
frontend *scwlb.Frontend
1552+
want []*scwlb.ACLSpec
1553+
}{
1554+
{
1555+
name: "ipv4 /32 mask is stripped",
1556+
sourceRanges: []string{"1.2.3.4/32"},
1557+
want: []*scwlb.ACLSpec{
1558+
{
1559+
Name: "lb-source-range-0",
1560+
Action: allowACL,
1561+
Index: 0,
1562+
Match: &scwlb.ACLMatch{IPSubnet: scw.StringSlicePtr([]string{"1.2.3.4"})},
1563+
},
1564+
{
1565+
Name: "lb-source-range-end",
1566+
Action: denyACL,
1567+
Index: 1,
1568+
Match: denyAllMatch,
1569+
},
1570+
},
1571+
},
1572+
{
1573+
name: "ipv4 non-/32 mask is kept",
1574+
sourceRanges: []string{"10.0.0.0/24"},
1575+
want: []*scwlb.ACLSpec{
1576+
{
1577+
Name: "lb-source-range-0",
1578+
Action: allowACL,
1579+
Index: 0,
1580+
Match: &scwlb.ACLMatch{IPSubnet: scw.StringSlicePtr([]string{"10.0.0.0/24"})},
1581+
},
1582+
{
1583+
Name: "lb-source-range-end",
1584+
Action: denyACL,
1585+
Index: 1,
1586+
Match: denyAllMatch,
1587+
},
1588+
},
1589+
},
1590+
{
1591+
name: "ipv6 /128 mask is stripped",
1592+
sourceRanges: []string{"2001:db8::1/128"},
1593+
want: []*scwlb.ACLSpec{
1594+
{
1595+
Name: "lb-source-range-0",
1596+
Action: allowACL,
1597+
Index: 0,
1598+
Match: &scwlb.ACLMatch{IPSubnet: scw.StringSlicePtr([]string{"2001:db8::1"})},
1599+
},
1600+
{
1601+
Name: "lb-source-range-end",
1602+
Action: denyACL,
1603+
Index: 1,
1604+
Match: denyAllMatch,
1605+
},
1606+
},
1607+
},
1608+
{
1609+
name: "ipv6 non-/128 mask is kept",
1610+
sourceRanges: []string{"2001:db8::/32"},
1611+
want: []*scwlb.ACLSpec{
1612+
{
1613+
Name: "lb-source-range-0",
1614+
Action: allowACL,
1615+
Index: 0,
1616+
Match: &scwlb.ACLMatch{IPSubnet: scw.StringSlicePtr([]string{"2001:db8::/32"})},
1617+
},
1618+
{
1619+
Name: "lb-source-range-end",
1620+
Action: denyACL,
1621+
Index: 1,
1622+
Match: denyAllMatch,
1623+
},
1624+
},
1625+
},
1626+
{
1627+
name: "invalid CIDR is ignored",
1628+
sourceRanges: []string{"not-a-cidr", "1.2.3.4/32"},
1629+
want: []*scwlb.ACLSpec{
1630+
{
1631+
Name: "lb-source-range-0",
1632+
Action: allowACL,
1633+
Index: 0,
1634+
Match: &scwlb.ACLMatch{IPSubnet: scw.StringSlicePtr([]string{"1.2.3.4"})},
1635+
},
1636+
{
1637+
Name: "lb-source-range-end",
1638+
Action: denyACL,
1639+
Index: 1,
1640+
Match: denyAllMatch,
1641+
},
1642+
},
1643+
},
1644+
{
1645+
name: "source ranges are combined with node ips, sorted, and use the frontend prefix",
1646+
sourceRanges: []string{"1.2.3.4/32"},
1647+
nodes: []*v1.Node{
1648+
{Status: v1.NodeStatus{Addresses: []v1.NodeAddress{
1649+
{Type: v1.NodeInternalIP, Address: "10.0.0.1"},
1650+
{Type: v1.NodeExternalIP, Address: "51.15.0.1"},
1651+
}}},
1652+
},
1653+
frontend: &scwlb.Frontend{ID: "uid"},
1654+
want: []*scwlb.ACLSpec{
1655+
{
1656+
Name: "uid-lb-source-range-0",
1657+
Action: allowACL,
1658+
Index: 0,
1659+
Match: &scwlb.ACLMatch{IPSubnet: scw.StringSlicePtr([]string{"1.2.3.4", "10.0.0.1", "51.15.0.1"})},
1660+
},
1661+
{
1662+
Name: "uid-lb-source-range-end",
1663+
Action: denyACL,
1664+
Index: 1,
1665+
Match: denyAllMatch,
1666+
},
1667+
},
1668+
},
1669+
}
1670+
1671+
for _, tt := range matrix {
1672+
t.Run(tt.name, func(t *testing.T) {
1673+
svc := &v1.Service{
1674+
Spec: v1.ServiceSpec{LoadBalancerSourceRanges: tt.sourceRanges},
1675+
}
1676+
got := makeACLSpecs(svc, tt.nodes, tt.frontend)
1677+
if !reflect.DeepEqual(got, tt.want) {
1678+
t.Errorf("want: %v, got: %v", tt.want, got)
1679+
}
1680+
})
1681+
}
1682+
1683+
t.Run("whitelist is split into multiple ACL chunks", func(t *testing.T) {
1684+
sourceRanges := make([]string, 0, MaxEntriesPerACL+1)
1685+
for i := 0; i <= MaxEntriesPerACL; i++ {
1686+
sourceRanges = append(sourceRanges, fmt.Sprintf("10.0.%d.1/32", i))
1687+
}
1688+
1689+
svc := &v1.Service{
1690+
Spec: v1.ServiceSpec{LoadBalancerSourceRanges: sourceRanges},
1691+
}
1692+
got := makeACLSpecs(svc, nil, nil)
1693+
1694+
if len(got) != 3 {
1695+
t.Fatalf("expected 3 acls, got %d", len(got))
1696+
}
1697+
if got[0].Name != "lb-source-range-0" || got[0].Index != 0 || len(got[0].Match.IPSubnet) != MaxEntriesPerACL {
1698+
t.Errorf("unexpected first chunk: %+v", got[0])
1699+
}
1700+
if got[1].Name != "lb-source-range-1" || got[1].Index != 1 || len(got[1].Match.IPSubnet) != 1 {
1701+
t.Errorf("unexpected second chunk: %+v", got[1])
1702+
}
1703+
if got[2].Name != "lb-source-range-end" || got[2].Index != 2 || got[2].Action.Type != scwlb.ACLActionTypeDeny {
1704+
t.Errorf("unexpected last chunk: %+v", got[2])
1705+
}
1706+
})
1707+
}
1708+
15331709
func TestGetHTTPHealthCheck(t *testing.T) {
15341710
matrix := []struct {
15351711
name string

0 commit comments

Comments
 (0)