Skip to content

Commit

Permalink
Verify that the EIP within the IP address ranges configured in the ip…
Browse files Browse the repository at this point in the history
…pools of the egressgateway

Signed-off-by: bzsuni <[email protected]>
  • Loading branch information
bzsuni committed Nov 10, 2023
1 parent 2fa66c9 commit eda6834
Show file tree
Hide file tree
Showing 5 changed files with 304 additions and 6 deletions.
51 changes: 51 additions & 0 deletions pkg/controller/webhook/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,14 @@ func validateEgressPolicy(ctx context.Context, client client.Client, req webhook
}
}
}

// denied when the `Spec.EgressIP.IPv4` or `Spec.EgressIP.IPv6` are not within the ip ranges defined in the ippools of the egressgateway
if ok, err := checkEIPIncluded(client, ctx, egp.Spec.EgressIP.IPv4, egp.Spec.EgressIP.IPv6, egp.Spec.EgressGatewayName); !ok {
if err != nil {
return webhook.Denied(err.Error())
}
return webhook.Denied("the Spec.EgressIP.IPv4 or Spec.EgressIP.IPv6 is not within the ip ranges defined in the ippools of the egressgateway")
}
}

return validateSubnet(egp.Spec.DestSubnet)
Expand Down Expand Up @@ -220,6 +228,14 @@ func validateEgressClusterPolicy(ctx context.Context, client client.Client, req

}
}

// denied when the `Spec.EgressIP.IPv4` or `Spec.EgressIP.IPv6` are not within the ip ranges defined in the ippools of the egressgateway
if ok, err := checkEIPIncluded(client, ctx, policy.Spec.EgressIP.IPv4, policy.Spec.EgressIP.IPv6, policy.Spec.EgressGatewayName); !ok {
if err != nil {
return webhook.Denied(err.Error())
}
return webhook.Denied("the Spec.EgressIP.IPv4 or Spec.EgressIP.IPv6 is not within the ip ranges defined in the ippools of the egressgateway")
}
}

return validateSubnet(policy.Spec.DestSubnet)
Expand Down Expand Up @@ -298,6 +314,41 @@ func checkEIP(client client.Client, ctx context.Context, ipv4, ipv6, egwName str
return true, nil
}

// checkEIPIncluded check if the `Spec.EgressIP.IPv4` or `Spec.EgressIP.IPv6` are within the ip ranges defined in the ippools of the egressgateway
func checkEIPIncluded(client client.Client, ctx context.Context, ipv4, ipv6, egwName string) (bool, error) {
eipIPV4 := ipv4
eipIPV6 := ipv6

if len(eipIPV4) == 0 && len(eipIPV6) == 0 {
return true, nil
}

egw := new(egressv1.EgressGateway)
err := client.Get(ctx, types.NamespacedName{Name: egwName}, egw)
if err != nil {
if !errors.IsNotFound(err) {
return false, fmt.Errorf("failed to get the EgressGateway: %v", err)
}
}

if len(egw.Spec.Ippools.IPv4) != 0 || len(egw.Spec.Ippools.IPv6) != 0 {
ips := append(egw.Spec.Ippools.IPv4, egw.Spec.Ippools.IPv6...)
if len(ipv4) != 0 {
ok, err := ip.CheckIPIncluded(ipv4, ips)
if !ok {
return ok, err
}
}
if len(ipv6) != 0 {
ok, err := ip.CheckIPIncluded(ipv6, ips)
if !ok {
return ok, err
}
}
}
return true, nil
}

func countGatewayAvailableIP(egw *egressv1.EgressGateway) (int, int, error) {
// check has free egress ip
ipv4Ranges, err := ip.MergeIPRanges(constant.IPv4, egw.Spec.Ippools.IPv4)
Expand Down
66 changes: 66 additions & 0 deletions pkg/controller/webhook/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,39 @@ func TestValidateEgressPolicy(t *testing.T) {
},
expAllow: false,
},
"case7 the policy's eip is not within the ip ranges defined in the ippool of the gateway": {
existingResources: []client.Object{
&v1beta1.EgressGateway{
ObjectMeta: metav1.ObjectMeta{Name: "test"},
Spec: v1beta1.EgressGatewaySpec{
Ippools: v1beta1.Ippools{
IPv4: []string{"172.18.1.2-172.18.1.5"},
IPv6: []string{"fc00:f853:ccd:e793:a::3-fc00:f853:ccd:e793:a::6"},
},
},
},
},
spec: v1beta1.EgressPolicySpec{
EgressGatewayName: "test",
EgressIP: v1beta1.EgressIP{
UseNodeIP: false,
IPv4: "172.19.1.2",
IPv6: "fddd:feee::2",
},
AppliedTo: v1beta1.AppliedTo{
PodSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"app": "test"},
},
},
DestSubnet: []string{
"192.168.1.1/24",
"1.1.1.1/32",
"10.0.6.1/16",
"fd00::21/112",
},
},
expAllow: false,
},
}
for name, c := range cases {
t.Run(name, func(t *testing.T) {
Expand Down Expand Up @@ -659,6 +692,39 @@ func TestValidateEgressClusterPolicy(t *testing.T) {
},
expAllow: false,
},
"case6 the policy's eip is not within the ip ranges defined in the ippool of the gateway": {
existingResources: []client.Object{
&v1beta1.EgressGateway{
ObjectMeta: metav1.ObjectMeta{Name: "test"},
Spec: v1beta1.EgressGatewaySpec{
Ippools: v1beta1.Ippools{
IPv4: []string{"172.18.1.2-172.18.1.5"},
IPv6: []string{"fc00:f853:ccd:e793:a::3-fc00:f853:ccd:e793:a::6"},
},
},
},
},
spec: v1beta1.EgressClusterPolicySpec{
EgressGatewayName: "test",
EgressIP: v1beta1.EgressIP{
UseNodeIP: false,
IPv4: "10.10.10.1",
IPv6: "fddd:10::1",
},
AppliedTo: v1beta1.ClusterAppliedTo{
PodSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"app": "test"},
},
},
DestSubnet: []string{
"192.168.1.1/24",
"1.1.1.1/32",
"10.0.6.1/16",
"fd00::21/112",
},
},
expAllow: false,
},
}
for name, c := range cases {
t.Run(name, func(t *testing.T) {
Expand Down
74 changes: 74 additions & 0 deletions pkg/utils/ip/ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -474,3 +474,77 @@ func IsIPv6IPRange(ipRange string) bool {

return true
}

// CheckIPIncluded checks if the 'ips' contain the 'ip'. The 'ips' can include three formats: single IP, IP range, and IP CIDR, such as ["172.30.0.2", "172.30.0.3-172.30.0-5", "172.30.1.0/24"]
func CheckIPIncluded(ip string, ips []string) (bool, error) {
if net.ParseIP(ip) == nil {
return false, ErrInvalidIP
}
var version constant.IPVersion
// check ip
if ok, _ := IsIPv4(ip); ok {
version = constant.IPv4
} else {
version = constant.IPv6
}

cidrs := make([]string, 0)
ipV4Ranges := make([]string, 0)
ipV6Ranges := make([]string, 0)

for _, item := range ips {
_, _, err := net.ParseCIDR(item)
if err != nil {
if IsIPv4IPRange(item) {
ipV4Ranges = append(ipV4Ranges, item)
} else if IsIPv6IPRange(item) {
ipV6Ranges = append(ipV6Ranges, item)
} else {
return false, ErrInvalidIPRangeFormat
}
} else {
// cidr
cidrs = append(cidrs, item)
}
}

// check if the ip is within the cidr
for _, item := range cidrs {
include, err := IsIPIncludedCidr(ip, item)
if err != nil {
return false, err
}
if include {
return true, nil
}
}

// check if the ip is within the ipRange
var ipRanges []string
if version == constant.IPv4 {
ipRanges = ipV4Ranges
} else {
ipRanges = ipV6Ranges
}
include, err := IsIPIncludedRange(version, ip, ipRanges)
if err != nil {
return false, err
}
if include {
return true, nil
}
return false, nil
}

// IsIPIncludedCidr checks if the "cidr" contains the "ip"
func IsIPIncludedCidr(ip, cidr string) (bool, error) {
IPip := net.ParseIP(ip)
if IPip == nil {
return false, ErrInvalidIP
}
_, IPnet, err := net.ParseCIDR(cidr)
if err != nil {
return false, err
}
return IPnet.Contains(IPip), nil
}
113 changes: 111 additions & 2 deletions pkg/utils/ip/ip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
package ip_test

import (
"github.com/spidernet-io/egressgateway/pkg/constant"
"github.com/spidernet-io/egressgateway/pkg/utils/ip"
"net"
"reflect"
"testing"

"github.com/spidernet-io/egressgateway/pkg/constant"
"github.com/spidernet-io/egressgateway/pkg/utils/ip"
)

func TestCmp(t *testing.T) {
Expand Down Expand Up @@ -692,3 +693,111 @@ func TestGetIPV4V6Cidr(t *testing.T) {
})
}
}

func TestCheckIPIncluded(t *testing.T) {
type args struct {
ip string
ips []string
}
tests := []struct {
name string
args args
wantErr bool
wantOk bool
}{
{
name: "invalid ip",
args: args{
ip: "bad-ip",
ips: []string{"10.6.1.0/24", "10.6.1.1-10.6.1.10", "10.6.1.0"},
},
wantOk: false,
wantErr: true,
},
{
name: "ipv4 not in range",
args: args{
ip: "10.7.1.0",
ips: []string{"10.6.1.0/24", "10.6.1.1-10.6.1.10", "10.6.1.0"},
},
wantOk: false,
wantErr: false,
},
{
name: "ipv6 not in range",
args: args{
ip: "fddd:10:7::1",
ips: []string{"10.6.1.0/24", "10.6.1.1-10.6.1.10", "10.6.1.0"},
},
wantOk: false,
wantErr: false,
},
{
name: "ipv4 in single ip",
args: args{
ip: "10.6.1.0",
ips: []string{"10.6.1.0"},
},
wantOk: true,
wantErr: false,
},
{
name: "ipv4 in cidr",
args: args{
ip: "10.6.1.0",
ips: []string{"10.6.1.0/24"},
},
wantOk: true,
wantErr: false,
},
{
name: "ipv4 in ip range",
args: args{
ip: "10.6.1.9",
ips: []string{"10.6.1.1-10.6.1.10"},
},
wantOk: true,
wantErr: false,
},
{
name: "ipv6 in single ip",
args: args{
ip: "fddd:10::0",
ips: []string{"fddd:10::0"},
},
wantOk: true,
wantErr: false,
},
{
name: "ipv6 in cidr",
args: args{
ip: "fddd:10::0",
ips: []string{"fdde:10::1-fdde:10::a", "fddd:10::/120"},
},
wantOk: true,
wantErr: false,
},
{
name: "ipv6 in ipRange",
args: args{
ip: "fdde:10::2",
ips: []string{"fddd:10::/120", "fdde:10::1-fdde:10::a"},
},
wantOk: true,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ok, err := ip.CheckIPIncluded(tt.args.ip, tt.args.ips)
if (err != nil) != tt.wantErr {
t.Errorf("CheckIPIncluded() error = %v, wantErr %v", err, tt.wantErr)
return
}
if ok != tt.wantOk {
t.Errorf("CheckIPIncluded() isIncluded = %v, wantOk %v", ok, tt.wantOk)
return
}
})
}
}
6 changes: 2 additions & 4 deletions test/e2e/egresspolicy/egresspolicy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,7 @@ var _ = Describe("EgressPolicy", Ordered, func() {
egp.Spec.EgressIP.IPv6 = "10.10.10.2"
}
}),
// todo @bzsuni waiting for the bug be fixed
PEntry("should fail when the `Spec.EgressIP` of the policy is not within the IP range of the ippools in the gateway used by the policy", Label("P00004"), true,
Entry("should fail when the `Spec.EgressIP` of the policy is not within the IP range of the ippools in the gateway used by the policy", Label("P00004"), true,
func(egp *egressv1.EgressPolicy) {
egp.Spec.EgressGatewayName = egw.Name
egp.Spec.AppliedTo.PodSubnet = []string{"10.10.0.0/16"}
Expand Down Expand Up @@ -310,8 +309,7 @@ var _ = Describe("EgressPolicy", Ordered, func() {
egcp.Spec.EgressIP.IPv6 = "10.10.10.2"
}
}),
// todo @bzsuni waiting for the bug be fixed
PEntry("should fail when the `Spec.EgressIP` of the cluster-policy is not within the IP range of the ippools in the gateway used by the policy", Label("P00004"), true,
Entry("should fail when the `Spec.EgressIP` of the cluster-policy is not within the IP range of the ippools in the gateway used by the policy", Label("P00004"), true,
func(egcp *egressv1.EgressClusterPolicy) {
egcp.Spec.EgressGatewayName = egw.Name
egcp.Spec.AppliedTo.PodSubnet = &[]string{"10.10.0.0/16"}
Expand Down

0 comments on commit eda6834

Please sign in to comment.