diff --git a/hack/.golint_failures b/hack/.golint_failures index 4f5a61cac89..9c5e704aff5 100644 --- a/hack/.golint_failures +++ b/hack/.golint_failures @@ -121,7 +121,6 @@ pkg/apis/imagepolicy pkg/apis/imagepolicy/v1alpha1 pkg/apis/meta/v1 pkg/apis/networking -pkg/apis/networking/v1 pkg/apis/policy pkg/apis/policy/v1alpha1 pkg/apis/policy/v1beta1 diff --git a/pkg/api/defaulting_test.go b/pkg/api/defaulting_test.go index 0350a19926c..f592b10a88a 100644 --- a/pkg/api/defaulting_test.go +++ b/pkg/api/defaulting_test.go @@ -109,6 +109,8 @@ func TestDefaulting(t *testing.T) { {Group: "apps", Version: "v1beta2", Kind: "ReplicaSetList"}: {}, {Group: "extensions", Version: "v1beta1", Kind: "ReplicaSet"}: {}, {Group: "extensions", Version: "v1beta1", Kind: "ReplicaSetList"}: {}, + {Group: "extensions", Version: "v1beta1", Kind: "NetworkPolicy"}: {}, + {Group: "extensions", Version: "v1beta1", Kind: "NetworkPolicyList"}: {}, {Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBinding"}: {}, {Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBindingList"}: {}, {Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "RoleBinding"}: {}, diff --git a/pkg/apis/extensions/v1beta1/conversion.go b/pkg/apis/extensions/v1beta1/conversion.go index 292c07c5b17..04aa3a316e0 100644 --- a/pkg/apis/extensions/v1beta1/conversion.go +++ b/pkg/apis/extensions/v1beta1/conversion.go @@ -291,6 +291,15 @@ func Convert_v1beta1_NetworkPolicySpec_To_networking_NetworkPolicySpec(in *exten return err } } + if in.PolicyTypes != nil { + in, out := &in.PolicyTypes, &out.PolicyTypes + *out = make([]networking.PolicyType, len(*in)) + for i := range *in { + if err := s.Convert(&(*in)[i], &(*out)[i], 0); err != nil { + return err + } + } + } return nil } @@ -310,6 +319,15 @@ func Convert_networking_NetworkPolicySpec_To_v1beta1_NetworkPolicySpec(in *netwo return err } } + if in.PolicyTypes != nil { + in, out := &in.PolicyTypes, &out.PolicyTypes + *out = make([]extensionsv1beta1.PolicyType, len(*in)) + for i := range *in { + if err := s.Convert(&(*in)[i], &(*out)[i], 0); err != nil { + return err + } + } + } return nil } diff --git a/pkg/apis/extensions/v1beta1/defaults.go b/pkg/apis/extensions/v1beta1/defaults.go index 9fea0ee06db..c653e812bec 100644 --- a/pkg/apis/extensions/v1beta1/defaults.go +++ b/pkg/apis/extensions/v1beta1/defaults.go @@ -133,4 +133,12 @@ func SetDefaults_NetworkPolicy(obj *extensionsv1beta1.NetworkPolicy) { } } } + + if len(obj.Spec.PolicyTypes) == 0 { + // Any policy that does not specify policyTypes implies at least "Ingress". + obj.Spec.PolicyTypes = []extensionsv1beta1.PolicyType{extensionsv1beta1.PolicyTypeIngress} + if len(obj.Spec.Egress) != 0 { + obj.Spec.PolicyTypes = append(obj.Spec.PolicyTypes, extensionsv1beta1.PolicyTypeEgress) + } + } } diff --git a/pkg/apis/extensions/v1beta1/defaults_test.go b/pkg/apis/extensions/v1beta1/defaults_test.go index 9f62dd252de..e208b51afd7 100644 --- a/pkg/apis/extensions/v1beta1/defaults_test.go +++ b/pkg/apis/extensions/v1beta1/defaults_test.go @@ -509,6 +509,210 @@ func TestDefaultRequestIsNotSetForReplicaSet(t *testing.T) { } } +func TestSetDefaultNetworkPolicy(t *testing.T) { + tests := []struct { + original *extensionsv1beta1.NetworkPolicy + expected *extensionsv1beta1.NetworkPolicy + }{ + { // Empty NetworkPolicy should be set to PolicyTypes Ingress + original: &extensionsv1beta1.NetworkPolicy{ + Spec: extensionsv1beta1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + }, + }, + expected: &extensionsv1beta1.NetworkPolicy{ + Spec: extensionsv1beta1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + PolicyTypes: []extensionsv1beta1.PolicyType{extensionsv1beta1.PolicyTypeIngress}, + }, + }, + }, + { // Empty Ingress NetworkPolicy should be set to PolicyTypes Ingress + original: &extensionsv1beta1.NetworkPolicy{ + Spec: extensionsv1beta1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Ingress: []extensionsv1beta1.NetworkPolicyIngressRule{}, + }, + }, + expected: &extensionsv1beta1.NetworkPolicy{ + Spec: extensionsv1beta1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Ingress: []extensionsv1beta1.NetworkPolicyIngressRule{}, + PolicyTypes: []extensionsv1beta1.PolicyType{extensionsv1beta1.PolicyTypeIngress}, + }, + }, + }, + { // Defined Ingress and Egress should be set to Ingress,Egress + original: &extensionsv1beta1.NetworkPolicy{ + Spec: extensionsv1beta1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Ingress: []extensionsv1beta1.NetworkPolicyIngressRule{ + { + From: []extensionsv1beta1.NetworkPolicyPeer{ + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + }, + }, + }, + }, + Egress: []extensionsv1beta1.NetworkPolicyEgressRule{ + { + To: []extensionsv1beta1.NetworkPolicyPeer{ + { + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + }, + }, + }, + }, + }, + }, + expected: &extensionsv1beta1.NetworkPolicy{ + Spec: extensionsv1beta1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Ingress: []extensionsv1beta1.NetworkPolicyIngressRule{ + { + From: []extensionsv1beta1.NetworkPolicyPeer{ + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + }, + }, + }, + }, + Egress: []extensionsv1beta1.NetworkPolicyEgressRule{ + { + To: []extensionsv1beta1.NetworkPolicyPeer{ + { + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + }, + }, + }, + }, + PolicyTypes: []extensionsv1beta1.PolicyType{extensionsv1beta1.PolicyTypeIngress, extensionsv1beta1.PolicyTypeEgress}, + }, + }, + }, + { // Egress only with unset PolicyTypes should be set to Ingress, Egress + original: &extensionsv1beta1.NetworkPolicy{ + Spec: extensionsv1beta1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Egress: []extensionsv1beta1.NetworkPolicyEgressRule{ + { + To: []extensionsv1beta1.NetworkPolicyPeer{ + { + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + }, + }, + }, + }, + }, + }, + expected: &extensionsv1beta1.NetworkPolicy{ + Spec: extensionsv1beta1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Egress: []extensionsv1beta1.NetworkPolicyEgressRule{ + { + To: []extensionsv1beta1.NetworkPolicyPeer{ + { + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + }, + }, + }, + }, + PolicyTypes: []extensionsv1beta1.PolicyType{extensionsv1beta1.PolicyTypeIngress, extensionsv1beta1.PolicyTypeEgress}, + }, + }, + }, + { // Egress only with PolicyTypes set to Egress should be set to only Egress + original: &extensionsv1beta1.NetworkPolicy{ + Spec: extensionsv1beta1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Egress: []extensionsv1beta1.NetworkPolicyEgressRule{ + { + To: []extensionsv1beta1.NetworkPolicyPeer{ + { + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"Egress": "only"}, + }, + }, + }, + }, + }, + PolicyTypes: []extensionsv1beta1.PolicyType{extensionsv1beta1.PolicyTypeEgress}, + }, + }, + expected: &extensionsv1beta1.NetworkPolicy{ + Spec: extensionsv1beta1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Egress: []extensionsv1beta1.NetworkPolicyEgressRule{ + { + To: []extensionsv1beta1.NetworkPolicyPeer{ + { + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"Egress": "only"}, + }, + }, + }, + }, + }, + PolicyTypes: []extensionsv1beta1.PolicyType{extensionsv1beta1.PolicyTypeEgress}, + }, + }, + }, + } + + for i, test := range tests { + original := test.original + expected := test.expected + obj2 := roundTrip(t, runtime.Object(original)) + got, ok := obj2.(*extensionsv1beta1.NetworkPolicy) + if !ok { + t.Errorf("(%d) unexpected object: %v", i, got) + t.FailNow() + } + if !apiequality.Semantic.DeepEqual(got.Spec, expected.Spec) { + t.Errorf("(%d) got different than expected\ngot:\n\t%+v\nexpected:\n\t%+v", i, got.Spec, expected.Spec) + } + } +} + func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { data, err := runtime.Encode(api.Codecs.LegacyCodec(SchemeGroupVersion), obj) if err != nil { diff --git a/pkg/apis/networking/fuzzer/fuzzer.go b/pkg/apis/networking/fuzzer/fuzzer.go index 1325b9d336c..3d36b7c97b2 100644 --- a/pkg/apis/networking/fuzzer/fuzzer.go +++ b/pkg/apis/networking/fuzzer/fuzzer.go @@ -36,5 +36,13 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} { } } }, + func(np *networking.NetworkPolicy, c fuzz.Continue) { + c.FuzzNoCustom(np) // fuzz self without calling this function again + // TODO: Implement a fuzzer to generate valid keys, values and operators for + // selector requirements. + if len(np.Spec.PolicyTypes) == 0 { + np.Spec.PolicyTypes = []networking.PolicyType{networking.PolicyTypeIngress} + } + }, } } diff --git a/pkg/apis/networking/types.go b/pkg/apis/networking/types.go index 840841fa696..2109c374526 100644 --- a/pkg/apis/networking/types.go +++ b/pkg/apis/networking/types.go @@ -36,6 +36,17 @@ type NetworkPolicy struct { Spec NetworkPolicySpec } +// Policy Type string describes the NetworkPolicy type +// This type is beta-level in 1.8 +type PolicyType string + +const ( + // PolicyTypeIngress is a NetworkPolicy that affects ingress traffic on selected pods + PolicyTypeIngress PolicyType = "Ingress" + // PolicyTypeEgress is a NetworkPolicy that affects egress traffic on selected pods + PolicyTypeEgress PolicyType = "Egress" +) + // NetworkPolicySpec provides the specification of a NetworkPolicy type NetworkPolicySpec struct { // Selects the pods to which this NetworkPolicy object applies. The array of @@ -60,10 +71,24 @@ type NetworkPolicySpec struct { // allowed if there are no NetworkPolicies selecting the pod (and cluster policy // otherwise allows the traffic), OR if the traffic matches at least one egress rule // across all of the NetworkPolicy objects whose podSelector matches the pod. If - // this field is empty then this NetworkPolicy does not limit any outgoing traffic - // (and serves solely to ensure that the pods it selects does not allow any outgoing traffic.) + // this field is empty then this NetworkPolicy limits all outgoing traffic (and serves + // solely to ensure that the pods it selects are isolated by default). + // This field is beta-level in 1.8 // +optional Egress []NetworkPolicyEgressRule + + // List of rule types that the NetworkPolicy relates to. + // Valid options are Ingress, Egress, or Ingress,Egress. + // If this field is not specified, it will default based on the existence of Ingress or Egress rules; + // policies that contain an Egress section are assumed to affect Egress, and all policies + // (whether or not they contain an Ingress section) are assumed to affect Ingress. + // If you want to write an egress-only policy, you must explicitly specify policyTypes [ "Egress" ]. + // Likewise, if you want to write a policy that specifies that no egress is allowed, + // you must specify a policyTypes value that include "Egress" (since such a policy would not include + // an Egress section and would otherwise default to just [ "Ingress" ]). + // This field is beta-level in 1.8 + // +optional + PolicyTypes []PolicyType } // NetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods @@ -88,6 +113,7 @@ type NetworkPolicyIngressRule struct { // NetworkPolicyEgressRule describes a particular set of traffic that is allowed out of pods // matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and to. +// This type is beta-level in 1.8 type NetworkPolicyEgressRule struct { // List of destination ports for outgoing traffic. // Each item in this list is combined using a logical OR. If this field is diff --git a/pkg/apis/networking/v1/defaults.go b/pkg/apis/networking/v1/defaults.go index 71b7074b4e4..fce71ce7114 100644 --- a/pkg/apis/networking/v1/defaults.go +++ b/pkg/apis/networking/v1/defaults.go @@ -33,3 +33,13 @@ func SetDefaults_NetworkPolicyPort(obj *networkingv1.NetworkPolicyPort) { obj.Protocol = &proto } } + +func SetDefaults_NetworkPolicy(obj *networkingv1.NetworkPolicy) { + if len(obj.Spec.PolicyTypes) == 0 { + // Any policy that does not specify policyTypes implies at least "Ingress". + obj.Spec.PolicyTypes = []networkingv1.PolicyType{networkingv1.PolicyTypeIngress} + if len(obj.Spec.Egress) != 0 { + obj.Spec.PolicyTypes = append(obj.Spec.PolicyTypes, networkingv1.PolicyTypeEgress) + } + } +} diff --git a/pkg/apis/networking/v1/defaults_test.go b/pkg/apis/networking/v1/defaults_test.go new file mode 100644 index 00000000000..3a86610d736 --- /dev/null +++ b/pkg/apis/networking/v1/defaults_test.go @@ -0,0 +1,256 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1_test + +import ( + "reflect" + "testing" + + networkingv1 "k8s.io/api/networking/v1" + + apiequality "k8s.io/apimachinery/pkg/api/equality" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/kubernetes/pkg/api" + _ "k8s.io/kubernetes/pkg/api/install" + _ "k8s.io/kubernetes/pkg/apis/networking/install" + . "k8s.io/kubernetes/pkg/apis/networking/v1" +) + +func TestSetDefaultNetworkPolicy(t *testing.T) { + tests := []struct { + original *networkingv1.NetworkPolicy + expected *networkingv1.NetworkPolicy + }{ + { // Empty NetworkPolicy should be set to PolicyTypes Ingress + original: &networkingv1.NetworkPolicy{ + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + }, + }, + expected: &networkingv1.NetworkPolicy{ + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress}, + }, + }, + }, + { // Empty Ingress NetworkPolicy should be set to PolicyTypes Ingress + original: &networkingv1.NetworkPolicy{ + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{}, + }, + }, + expected: &networkingv1.NetworkPolicy{ + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{}, + PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress}, + }, + }, + }, + { // Defined Ingress and Egress should be set to Ingress,Egress + original: &networkingv1.NetworkPolicy{ + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + { + From: []networkingv1.NetworkPolicyPeer{ + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + }, + }, + }, + }, + Egress: []networkingv1.NetworkPolicyEgressRule{ + { + To: []networkingv1.NetworkPolicyPeer{ + { + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + }, + }, + }, + }, + }, + }, + expected: &networkingv1.NetworkPolicy{ + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Ingress: []networkingv1.NetworkPolicyIngressRule{ + { + From: []networkingv1.NetworkPolicyPeer{ + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + }, + }, + }, + }, + Egress: []networkingv1.NetworkPolicyEgressRule{ + { + To: []networkingv1.NetworkPolicyPeer{ + { + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + }, + }, + }, + }, + PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress, networkingv1.PolicyTypeEgress}, + }, + }, + }, + { // Egress only with unset PolicyTypes should be set to Ingress, Egress + original: &networkingv1.NetworkPolicy{ + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Egress: []networkingv1.NetworkPolicyEgressRule{ + { + To: []networkingv1.NetworkPolicyPeer{ + { + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + }, + }, + }, + }, + }, + }, + expected: &networkingv1.NetworkPolicy{ + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Egress: []networkingv1.NetworkPolicyEgressRule{ + { + To: []networkingv1.NetworkPolicyPeer{ + { + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + }, + }, + }, + }, + PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress, networkingv1.PolicyTypeEgress}, + }, + }, + }, + { // Egress only with PolicyTypes set to Egress should be set to only Egress + original: &networkingv1.NetworkPolicy{ + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Egress: []networkingv1.NetworkPolicyEgressRule{ + { + To: []networkingv1.NetworkPolicyPeer{ + { + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"Egress": "only"}, + }, + }, + }, + }, + }, + PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeEgress}, + }, + }, + expected: &networkingv1.NetworkPolicy{ + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Egress: []networkingv1.NetworkPolicyEgressRule{ + { + To: []networkingv1.NetworkPolicyPeer{ + { + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"Egress": "only"}, + }, + }, + }, + }, + }, + PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeEgress}, + }, + }, + }, + } + + for i, test := range tests { + original := test.original + expected := test.expected + obj2 := roundTrip(t, runtime.Object(original)) + got, ok := obj2.(*networkingv1.NetworkPolicy) + if !ok { + t.Errorf("(%d) unexpected object: %v", i, got) + t.FailNow() + } + if !apiequality.Semantic.DeepEqual(got.Spec, expected.Spec) { + t.Errorf("(%d) got different than expected\ngot:\n\t%+v\nexpected:\n\t%+v", i, got.Spec, expected.Spec) + } + } +} + +func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { + data, err := runtime.Encode(api.Codecs.LegacyCodec(SchemeGroupVersion), obj) + if err != nil { + t.Errorf("%v\n %#v", err, obj) + return nil + } + obj2, err := runtime.Decode(api.Codecs.UniversalDecoder(), data) + if err != nil { + t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), obj) + return nil + } + obj3 := reflect.New(reflect.TypeOf(obj).Elem()).Interface().(runtime.Object) + err = api.Scheme.Convert(obj2, obj3, nil) + if err != nil { + t.Errorf("%v\nSource: %#v", err, obj2) + return nil + } + return obj3 +} diff --git a/pkg/apis/networking/validation/validation.go b/pkg/apis/networking/validation/validation.go index 7541da6588a..49f638f91f0 100644 --- a/pkg/apis/networking/validation/validation.go +++ b/pkg/apis/networking/validation/validation.go @@ -21,6 +21,7 @@ import ( unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/kubernetes/pkg/api" @@ -123,6 +124,20 @@ func ValidateNetworkPolicySpec(spec *networking.NetworkPolicySpec, fldPath *fiel } } } + // Validate PolicyTypes + allowed := sets.NewString(string(networking.PolicyTypeIngress), string(networking.PolicyTypeEgress)) + if len(spec.PolicyTypes) > len(allowed) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("policyTypes"), &spec.PolicyTypes, "may not specify more than two policyTypes")) + return allErrs + } + for i, pType := range spec.PolicyTypes { + policyPath := fldPath.Child("policyTypes").Index(i) + for _, p := range spec.PolicyTypes { + if !allowed.Has(string(p)) { + allErrs = append(allErrs, field.NotSupported(policyPath, pType, []string{string(networking.PolicyTypeIngress), string(networking.PolicyTypeEgress)})) + } + } + } return allErrs } diff --git a/pkg/apis/networking/validation/validation_test.go b/pkg/apis/networking/validation/validation_test.go index 242fd77dada..401eb43b6c7 100644 --- a/pkg/apis/networking/validation/validation_test.go +++ b/pkg/apis/networking/validation/validation_test.go @@ -191,6 +191,28 @@ func TestValidateNetworkPolicy(t *testing.T) { }, }, }, + PolicyTypes: []networking.PolicyType{networking.PolicyTypeEgress}, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Egress: []networking.NetworkPolicyEgressRule{ + { + To: []networking.NetworkPolicyPeer{ + { + IPBlock: &networking.IPBlock{ + CIDR: "192.168.0.0/16", + Except: []string{"192.168.3.0/24", "192.168.4.0/24"}, + }, + }, + }, + }, + }, + PolicyTypes: []networking.PolicyType{networking.PolicyTypeIngress, networking.PolicyTypeEgress}, }, }, } @@ -421,6 +443,48 @@ func TestValidateNetworkPolicy(t *testing.T) { }, }, }, + "invalid policyTypes": { + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Egress: []networking.NetworkPolicyEgressRule{ + { + To: []networking.NetworkPolicyPeer{ + { + IPBlock: &networking.IPBlock{ + CIDR: "192.168.0.0/16", + Except: []string{"192.168.3.0/24", "192.168.4.0/24"}, + }, + }, + }, + }, + }, + PolicyTypes: []networking.PolicyType{"foo", "bar"}, + }, + }, + "too many policyTypes": { + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Egress: []networking.NetworkPolicyEgressRule{ + { + To: []networking.NetworkPolicyPeer{ + { + IPBlock: &networking.IPBlock{ + CIDR: "192.168.0.0/16", + Except: []string{"192.168.3.0/24", "192.168.4.0/24"}, + }, + }, + }, + }, + }, + PolicyTypes: []networking.PolicyType{"foo", "bar", "baz"}, + }, + }, } // Error cases are not expected to pass validation. diff --git a/staging/src/k8s.io/api/extensions/v1beta1/types.go b/staging/src/k8s.io/api/extensions/v1beta1/types.go index d372aa51aad..052cbb691c7 100644 --- a/staging/src/k8s.io/api/extensions/v1beta1/types.go +++ b/staging/src/k8s.io/api/extensions/v1beta1/types.go @@ -1157,6 +1157,17 @@ type NetworkPolicy struct { Spec NetworkPolicySpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` } +// Policy Type string describes the NetworkPolicy type +// This type is beta-level in 1.8 +type PolicyType string + +const ( + // PolicyTypeIngress is a NetworkPolicy that affects ingress traffic on selected pods + PolicyTypeIngress PolicyType = "Ingress" + // PolicyTypeEgress is a NetworkPolicy that affects egress traffic on selected pods + PolicyTypeEgress PolicyType = "Egress" +) + type NetworkPolicySpec struct { // Selects the pods to which this NetworkPolicy object applies. The array of ingress rules // is applied to any pods selected by this field. Multiple network policies can select the @@ -1179,10 +1190,24 @@ type NetworkPolicySpec struct { // allowed if there are no NetworkPolicies selecting the pod (and cluster policy // otherwise allows the traffic), OR if the traffic matches at least one egress rule // across all of the NetworkPolicy objects whose podSelector matches the pod. If - // this field is empty then this NetworkPolicy does not limit any outgoing traffic - // (and serves solely to ensure that the pods it selects does not allow any outgoing traffic.) + // this field is empty then this NetworkPolicy limits all outgoing traffic (and serves + // solely to ensure that the pods it selects are isolated by default). + // This field is beta-level in 1.8 // +optional Egress []NetworkPolicyEgressRule `json:"egress,omitempty" protobuf:"bytes,3,rep,name=egress"` + + // List of rule types that the NetworkPolicy relates to. + // Valid options are Ingress, Egress, or Ingress,Egress. + // If this field is not specified, it will default based on the existence of Ingress or Egress rules; + // policies that contain an Egress section are assumed to affect Egress, and all policies + // (whether or not they contain an Ingress section) are assumed to affect Ingress. + // If you want to write an egress-only policy, you must explicitly specify policyTypes [ "Egress" ]. + // Likewise, if you want to write a policy that specifies that no egress is allowed, + // you must specify a policyTypes value that include "Egress" (since such a policy would not include + // an Egress section and would otherwise default to just [ "Ingress" ]). + // This field is beta-level in 1.8 + // +optional + PolicyTypes []PolicyType `json:"policyTypes,omitempty" protobuf:"bytes,4,rep,name=policyTypes,casttype=PolicyType"` } // This NetworkPolicyIngressRule matches traffic if and only if the traffic matches both ports AND from. @@ -1206,6 +1231,7 @@ type NetworkPolicyIngressRule struct { // NetworkPolicyEgressRule describes a particular set of traffic that is allowed out of pods // matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and to. +// This type is beta-level in 1.8 type NetworkPolicyEgressRule struct { // List of destination ports for outgoing traffic. // Each item in this list is combined using a logical OR. If this field is diff --git a/staging/src/k8s.io/api/networking/v1/types.go b/staging/src/k8s.io/api/networking/v1/types.go index d420c4d7297..57bc8005e42 100644 --- a/staging/src/k8s.io/api/networking/v1/types.go +++ b/staging/src/k8s.io/api/networking/v1/types.go @@ -38,6 +38,17 @@ type NetworkPolicy struct { Spec NetworkPolicySpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` } +// Policy Type string describes the NetworkPolicy type +// This type is beta-level in 1.8 +type PolicyType string + +const ( + // PolicyTypeIngress is a NetworkPolicy that affects ingress traffic on selected pods + PolicyTypeIngress PolicyType = "Ingress" + // PolicyTypeEgress is a NetworkPolicy that affects egress traffic on selected pods + PolicyTypeEgress PolicyType = "Egress" +) + // NetworkPolicySpec provides the specification of a NetworkPolicy type NetworkPolicySpec struct { // Selects the pods to which this NetworkPolicy object applies. The array of @@ -62,10 +73,24 @@ type NetworkPolicySpec struct { // allowed if there are no NetworkPolicies selecting the pod (and cluster policy // otherwise allows the traffic), OR if the traffic matches at least one egress rule // across all of the NetworkPolicy objects whose podSelector matches the pod. If - // this field is empty then this NetworkPolicy does not limit any outgoing traffic - // (and serves solely to ensure that the pods it selects does not allow any outgoing traffic.) + // this field is empty then this NetworkPolicy limits all outgoing traffic (and serves + // solely to ensure that the pods it selects are isolated by default). + // This field is beta-level in 1.8 // +optional Egress []NetworkPolicyEgressRule `json:"egress,omitempty" protobuf:"bytes,3,rep,name=egress"` + + // List of rule types that the NetworkPolicy relates to. + // Valid options are Ingress, Egress, or Ingress,Egress. + // If this field is not specified, it will default based on the existence of Ingress or Egress rules; + // policies that contain an Egress section are assumed to affect Egress, and all policies + // (whether or not they contain an Ingress section) are assumed to affect Ingress. + // If you want to write an egress-only policy, you must explicitly specify policyTypes [ "Egress" ]. + // Likewise, if you want to write a policy that specifies that no egress is allowed, + // you must specify a policyTypes value that include "Egress" (since such a policy would not include + // an Egress section and would otherwise default to just [ "Ingress" ]). + // This field is beta-level in 1.8 + // +optional + PolicyTypes []PolicyType `json:"policyTypes,omitempty" protobuf:"bytes,4,rep,name=policyTypes,casttype=PolicyType"` } // NetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods @@ -90,6 +115,7 @@ type NetworkPolicyIngressRule struct { // NetworkPolicyEgressRule describes a particular set of traffic that is allowed out of pods // matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and to. +// This type is beta-level in 1.8 type NetworkPolicyEgressRule struct { // List of destination ports for outgoing traffic. // Each item in this list is combined using a logical OR. If this field is