Add IPBlock to NetworkPolicy

Signed-off-by: Christopher M. Luciano <cmluciano@us.ibm.com>
This commit is contained in:
Christopher M. Luciano
2017-08-19 14:25:27 -04:00
parent c61468f29b
commit b75dc93dd4
6 changed files with 227 additions and 1 deletions

View File

@@ -60,6 +60,8 @@ func addConversionFuncs(scheme *runtime.Scheme) error {
Convert_v1beta1_NetworkPolicySpec_To_networking_NetworkPolicySpec,
Convert_networking_NetworkPolicySpec_To_v1beta1_NetworkPolicySpec,
Convert_extensions_PodSecurityPolicySpec_To_v1beta1_PodSecurityPolicySpec,
Convert_v1beta1_IPBlock_To_networking_IPBlock,
Convert_networking_IPBlock_To_v1beta1_IPBlock,
)
if err != nil {
return err
@@ -364,6 +366,14 @@ func Convert_v1beta1_NetworkPolicyPeer_To_networking_NetworkPolicyPeer(in *exten
} else {
out.NamespaceSelector = nil
}
if in.IPBlock != nil {
out.IPBlock = new(networking.IPBlock)
if err := s.Convert(in.IPBlock, out.IPBlock, 0); err != nil {
return err
}
} else {
out.IPBlock = nil
}
return nil
}
@@ -384,6 +394,30 @@ func Convert_networking_NetworkPolicyPeer_To_v1beta1_NetworkPolicyPeer(in *netwo
} else {
out.NamespaceSelector = nil
}
if in.IPBlock != nil {
out.IPBlock = new(extensionsv1beta1.IPBlock)
if err := s.Convert(in.IPBlock, out.IPBlock, 0); err != nil {
return err
}
} else {
out.IPBlock = nil
}
return nil
}
func Convert_v1beta1_IPBlock_To_networking_IPBlock(in *extensionsv1beta1.IPBlock, out *networking.IPBlock, s conversion.Scope) error {
out.CIDR = in.CIDR
out.Except = make([]string, len(in.Except))
copy(out.Except, in.Except)
return nil
}
func Convert_networking_IPBlock_To_v1beta1_IPBlock(in *networking.IPBlock, out *extensionsv1beta1.IPBlock, s conversion.Scope) error {
out.CIDR = in.CIDR
out.Except = make([]string, len(in.Except))
copy(out.Except, in.Except)
return nil
}

View File

@@ -90,6 +90,20 @@ type NetworkPolicyPort struct {
Port *intstr.IntOrString
}
// IPBlock describes a particular CIDR (Ex. "192.168.1.1/24") that is allowed to the pods
// matched by a NetworkPolicySpec's podSelector. The except entry describes CIDRs that should
// not be included within this rule.
type IPBlock struct {
// CIDR is a string representing the IP Block
// Valid examples are "192.168.1.1/24"
CIDR string
// Except is a slice of CIDRs that should not be included within an IP Block
// Valid examples are "192.168.1.1/24"
// Except values will be rejected if they are outside the CIDR range
// +optional
Except []string
}
// NetworkPolicyPeer describes a peer to allow traffic from. Exactly one of its fields
// must be specified.
type NetworkPolicyPeer struct {
@@ -104,6 +118,10 @@ type NetworkPolicyPeer struct {
// selector semantics. If present but empty, this selector selects all namespaces.
// +optional
NamespaceSelector *metav1.LabelSelector
// IPBlock defines policy on a particular IPBlock
// +optional
IPBlock *IPBlock
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

View File

@@ -17,6 +17,8 @@ limitations under the License.
package validation
import (
"net"
unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/validation"
@@ -68,7 +70,10 @@ func ValidateNetworkPolicySpec(spec *networking.NetworkPolicySpec, fldPath *fiel
numFroms++
allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(from.NamespaceSelector, fromPath.Child("namespaceSelector"))...)
}
if from.IPBlock != nil {
numFroms++
allErrs = append(allErrs, ValidateIPBlock(from.IPBlock, fromPath.Child("ipBlock"))...)
}
if numFroms == 0 {
allErrs = append(allErrs, field.Required(fromPath, "must specify a from type"))
} else if numFroms > 1 {
@@ -93,3 +98,39 @@ func ValidateNetworkPolicyUpdate(update, old *networking.NetworkPolicy) field.Er
allErrs = append(allErrs, ValidateNetworkPolicySpec(&update.Spec, field.NewPath("spec"))...)
return allErrs
}
// ValidateIPBlock validates a cidr and the except fields of an IpBlock NetworkPolicyPeer
func ValidateIPBlock(ipb *networking.IPBlock, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(ipb.CIDR) == 0 || ipb.CIDR == "" {
allErrs = append(allErrs, field.Required(fldPath.Child("cidr"), ""))
return allErrs
}
cidrIPNet, err := validateCIDR(ipb.CIDR)
if err != nil {
allErrs = append(allErrs, field.Invalid(fldPath.Child("cidr"), ipb.CIDR, "not a valid CIDR"))
return allErrs
}
exceptCIDR := ipb.Except
for i, exceptIP := range exceptCIDR {
exceptPath := fldPath.Child("except").Index(i)
exceptCIDR, err := validateCIDR(exceptIP)
if err != nil {
allErrs = append(allErrs, field.Invalid(exceptPath, exceptIP, "not a valid CIDR"))
return allErrs
}
if !cidrIPNet.Contains(exceptCIDR.IP) {
allErrs = append(allErrs, field.Invalid(exceptPath, exceptCIDR.IP, "not within CIDR range"))
}
}
return allErrs
}
// validateCIDR validates whether a CIDR matches the conventions expected by net.ParseCIDR
func validateCIDR(cidr string) (*net.IPNet, error) {
_, net, err := net.ParseCIDR(cidr)
if err != nil {
return nil, err
}
return net, nil
}

View File

@@ -122,6 +122,26 @@ func TestValidateNetworkPolicy(t *testing.T) {
},
},
},
{
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
Spec: networking.NetworkPolicySpec{
PodSelector: metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
Ingress: []networking.NetworkPolicyIngressRule{
{
From: []networking.NetworkPolicyPeer{
{
IPBlock: &networking.IPBlock{
CIDR: "192.168.0.0/16",
Except: []string{"192.168.3.0/24", "192.168.4.0/24"},
},
},
},
},
},
},
},
}
// Success cases are expected to pass validation.
@@ -256,6 +276,83 @@ func TestValidateNetworkPolicy(t *testing.T) {
},
},
},
"missing cidr field": {
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
Spec: networking.NetworkPolicySpec{
PodSelector: metav1.LabelSelector{},
Ingress: []networking.NetworkPolicyIngressRule{
{
From: []networking.NetworkPolicyPeer{
{
IPBlock: &networking.IPBlock{
Except: []string{"192.168.8.0/24", "192.168.9.0/24"},
},
},
},
},
},
},
},
"invalid cidr format": {
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
Spec: networking.NetworkPolicySpec{
PodSelector: metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
Ingress: []networking.NetworkPolicyIngressRule{
{
From: []networking.NetworkPolicyPeer{
{
IPBlock: &networking.IPBlock{
CIDR: "192.168.5.6",
Except: []string{"192.168.1.0/24", "192.168.2.0/24"},
},
},
},
},
},
},
},
"except field is an empty string": {
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
Spec: networking.NetworkPolicySpec{
PodSelector: metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
Ingress: []networking.NetworkPolicyIngressRule{
{
From: []networking.NetworkPolicyPeer{
{
IPBlock: &networking.IPBlock{
CIDR: "192.168.8.0/24",
Except: []string{"", " "},
},
},
},
},
},
},
},
"except IP is outside of CIDR range": {
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
Spec: networking.NetworkPolicySpec{
PodSelector: metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
Ingress: []networking.NetworkPolicyIngressRule{
{
From: []networking.NetworkPolicyPeer{
{
IPBlock: &networking.IPBlock{
CIDR: "192.168.8.0/24",
Except: []string{"192.168.9.1/24"},
},
},
},
},
},
},
},
}
// Error cases are not expected to pass validation.

View File

@@ -1181,6 +1181,20 @@ type NetworkPolicyPort struct {
Port *intstr.IntOrString `json:"port,omitempty" protobuf:"bytes,2,opt,name=port"`
}
// IPBlock describes a particular CIDR (Ex. "192.168.1.1/24") that is allowed to the pods
// matched by a NetworkPolicySpec's podSelector. The except entry describes CIDRs that should
// not be included within this rule.
type IPBlock struct {
// CIDR is a string representing the IP Block
// Valid examples are "192.168.1.1/24"
CIDR string `json:"cidr" protobuf:"bytes,1,name=cidr"`
// Except is a slice of CIDRs that should not be included within an IP Block
// Valid examples are "192.168.1.1/24"
// Except values will be rejected if they are outside the CIDR range
// +optional
Except []string `json:"except,omitempty" protobuf:"bytes,2,rep,name=except"`
}
type NetworkPolicyPeer struct {
// Exactly one of the following must be specified.
@@ -1196,6 +1210,10 @@ type NetworkPolicyPeer struct {
// If present but empty, this selector selects all namespaces.
// +optional
NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty" protobuf:"bytes,2,opt,name=namespaceSelector"`
// IPBlock defines policy on a particular IPBlock
// +optional
IPBlock *IPBlock `json:"ipBlock,omitempty" protobuf:"bytes,3,rep,name=ipBlock"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

View File

@@ -92,6 +92,20 @@ type NetworkPolicyPort struct {
Port *intstr.IntOrString `json:"port,omitempty" protobuf:"bytes,2,opt,name=port"`
}
// IPBlock describes a particular CIDR (Ex. "192.168.1.1/24") that is allowed to the pods
// matched by a NetworkPolicySpec's podSelector. The except entry describes CIDRs that should
// not be included within this rule.
type IPBlock struct {
// CIDR is a string representing the IP Block
// Valid examples are "192.168.1.1/24"
CIDR string `json:"cidr" protobuf:"bytes,1,name=cidr"`
// Except is a slice of CIDRs that should not be included within an IP Block
// Valid examples are "192.168.1.1/24"
// Except values will be rejected if they are outside the CIDR range
// +optional
Except []string `json:"except,omitempty" protobuf:"bytes,2,rep,name=except"`
}
// NetworkPolicyPeer describes a peer to allow traffic from. Exactly one of its fields
// must be specified.
type NetworkPolicyPeer struct {
@@ -106,6 +120,10 @@ type NetworkPolicyPeer struct {
// selector semantics. If present but empty, this selector selects all namespaces.
// +optional
NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty" protobuf:"bytes,2,opt,name=namespaceSelector"`
// IPBlock defines policy on a particular IPBlock
// +optional
IPBlock *IPBlock `json:"ipBlock,omitempty" protobuf:"bytes,3,rep,name=ipBlock"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object