diff --git a/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go b/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go index 680a2abd0..5c81d49ef 100644 --- a/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go +++ b/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go @@ -21,6 +21,7 @@ import ( dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -285,6 +286,9 @@ type WorkspaceConfig struct { // Overrides defines configuration options for `container-overrides` and // `pod-overrides` DevWorkspace attributes. Overrides *OverrideConfig `json:"overrides,omitempty"` + // NetworkPolicy defines configuration options for the NetworkPolicy provisioned + // for each DevWorkspace. + NetworkPolicy *NetworkPolicyConfig `json:"networkPolicy,omitempty"` } type WebhookConfig struct { @@ -320,6 +324,43 @@ type PersistentHomeConfig struct { DisableInitContainer *bool `json:"disableInitContainer,omitempty"` } +// NetworkPolicyConfig defines the NetworkPolicy the DevWorkspace Operator provisions for +// DevWorkspaces. One NetworkPolicy is created per DevWorkspace and applies to that +// workspace's pods only. The policy is owned by its DevWorkspace and is removed along +// with it. +// +// The name, labels, podSelector and policyTypes of the NetworkPolicy are controlled by +// the DevWorkspace Operator; only the ingress and egress rules are configurable. +type NetworkPolicyConfig struct { + // Enabled determines whether a NetworkPolicy is provisioned for each DevWorkspace. + // Disabled by default. Changing this field does not immediately affect existing + // DevWorkspaces: changing the DevWorkspaceOperatorConfig does not enqueue the + // DevWorkspaces it affects, so the new value is applied to a DevWorkspace the next + // time that DevWorkspace is reconciled for any reason. Restarting a workspace is not + // required. Both enabling and disabling apply to running and stopped DevWorkspaces + // alike. + Enabled *bool `json:"enabled,omitempty"` + // Ingress defines the ingress rules applied to DevWorkspace pods. If this field is not + // specified, the default ingress rules of the DevWorkspace Operator apply. On OpenShift, + // the defaults allow traffic from the operator's own namespace and from the OpenShift + // monitoring and ingress namespaces, and deny all other ingress traffic. On Kubernetes, + // the default allows all ingress traffic, since the namespace of the cluster's ingress + // controller is not known to the operator; administrators are expected to replace this + // with rules appropriate to their cluster. + // If this field is specified as an empty list, all ingress traffic to DevWorkspace pods + // is denied. If this field is specified as a non-empty list, exactly those rules apply + // and the default rules no longer apply. + // +kubebuilder:validation:Optional + Ingress []networkingv1.NetworkPolicyIngressRule `json:"ingress,omitempty"` + // Egress defines the egress rules applied to DevWorkspace pods. If this field is not + // specified, the default egress rule of the DevWorkspace Operator applies, which allows + // all egress traffic. If this field is specified as an empty list, all egress traffic + // from DevWorkspace pods is denied. If this field is specified as a non-empty list, + // exactly those rules apply and the default rule no longer applies. + // +kubebuilder:validation:Optional + Egress []networkingv1.NetworkPolicyEgressRule `json:"egress,omitempty"` +} + type Proxy struct { // HttpProxy is the URL of the proxy for HTTP requests, in the format http://USERNAME:PASSWORD@SERVER:PORT/. To ignore // automatically detected proxy settings for the cluster, set this field to an empty string ("") diff --git a/apis/controller/v1alpha1/zz_generated.deepcopy.go b/apis/controller/v1alpha1/zz_generated.deepcopy.go index 0c3e6e4f9..96a3b84d4 100644 --- a/apis/controller/v1alpha1/zz_generated.deepcopy.go +++ b/apis/controller/v1alpha1/zz_generated.deepcopy.go @@ -22,6 +22,7 @@ package v1alpha1 import ( "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" v1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -439,6 +440,40 @@ func (in *KeyNotFoundError) DeepCopy() *KeyNotFoundError { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkPolicyConfig) DeepCopyInto(out *NetworkPolicyConfig) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + if in.Ingress != nil { + in, out := &in.Ingress, &out.Ingress + *out = make([]networkingv1.NetworkPolicyIngressRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Egress != nil { + in, out := &in.Egress, &out.Egress + *out = make([]networkingv1.NetworkPolicyEgressRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicyConfig. +func (in *NetworkPolicyConfig) DeepCopy() *NetworkPolicyConfig { + if in == nil { + return nil + } + out := new(NetworkPolicyConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OperatorConfiguration) DeepCopyInto(out *OperatorConfiguration) { *out = *in @@ -969,6 +1004,11 @@ func (in *WorkspaceConfig) DeepCopyInto(out *WorkspaceConfig) { *out = new(OverrideConfig) (*in).DeepCopyInto(*out) } + if in.NetworkPolicy != nil { + in, out := &in.NetworkPolicy, &out.NetworkPolicy + *out = new(NetworkPolicyConfig) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceConfig. diff --git a/controllers/controller/devworkspacerouting/devworkspacerouting_controller.go b/controllers/controller/devworkspacerouting/devworkspacerouting_controller.go index c95a4842a..1a4bbfd42 100644 --- a/controllers/controller/devworkspacerouting/devworkspacerouting_controller.go +++ b/controllers/controller/devworkspacerouting/devworkspacerouting_controller.go @@ -66,7 +66,6 @@ type DevWorkspaceRoutingReconciler struct { // +kubebuilder:rbac:groups=controller.devfile.io,resources=devworkspaceroutings/status,verbs=get;update;patch // +kubebuilder:rbac:groups="",resources=services,verbs=* // +kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=* -// +kubebuilder:rbac:groups=networking.k8s.io,resources=networkpolicies,verbs=create;delete;update;patch;get;list;watch // +kubebuilder:rbac:groups=route.openshift.io,resources=routes,verbs=* // +kubebuidler:rbac:groups=route.openshift.io,resources=routes/status,verbs=get,list,watch // +kubebuilder:rbac:groups=route.openshift.io,resources=routes/custom-host,verbs=create diff --git a/controllers/workspace/devworkspace_controller.go b/controllers/workspace/devworkspace_controller.go index 37cc6e20d..9f731f1a0 100644 --- a/controllers/workspace/devworkspace_controller.go +++ b/controllers/workspace/devworkspace_controller.go @@ -50,12 +50,14 @@ import ( "github.com/devfile/devworkspace-operator/pkg/provision/storage" "github.com/devfile/devworkspace-operator/pkg/provision/sync" wsprovision "github.com/devfile/devworkspace-operator/pkg/provision/workspace" + "github.com/devfile/devworkspace-operator/pkg/provision/workspace/networkpolicy" "github.com/devfile/devworkspace-operator/pkg/provision/workspace/rbac" "github.com/go-logr/logr" "github.com/google/uuid" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" k8sErrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -91,6 +93,7 @@ type DevWorkspaceReconciler struct { // +kubebuilder:rbac:groups="",resources=pods;serviceaccounts;secrets;configmaps;persistentvolumeclaims,verbs=* // +kubebuilder:rbac:groups="",resources=namespaces;events,verbs=get;list;watch // +kubebuilder:rbac:groups="batch",resources=jobs,verbs=get;create;list;watch;update;patch;delete +// +kubebuilder:rbac:groups=networking.k8s.io,resources=networkpolicies,verbs=create;delete;update;patch;get;list;watch // +kubebuilder:rbac:groups=admissionregistration.k8s.io,resources=mutatingwebhookconfigurations;validatingwebhookconfigurations,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=authorization.k8s.io,resources=subjectaccessreviews;localsubjectaccessreviews,verbs=create // +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=clusterroles;clusterrolebindings,verbs=get;list;watch;create;update @@ -165,6 +168,13 @@ func (r *DevWorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request return reconcile.Result{Requeue: true}, err } + // Sync the NetworkPolicy early, so that it follows the operator configuration for every + // workspace and not just the starting ones, and exists before any workspace pod does. + err = networkpolicy.SyncNetworkPolicy(workspace, clusterAPI) + if shouldReturn, reconcileResult, reconcileErr := r.checkDWError(workspace, err, "Error provisioning network policy", metrics.ReasonInfrastructureFailure, reqLogger, &reconcileStatus); shouldReturn { + return reconcileResult, reconcileErr + } + // Stop failed workspaces if workspace.Status.Phase == devworkspacePhaseFailing && workspace.Spec.Started { // If debug annotation is present, leave the deployment in place to let users @@ -818,6 +828,7 @@ func (r *DevWorkspaceReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&corev1.ConfigMap{}). Owns(&corev1.Secret{}). Owns(&corev1.ServiceAccount{}). + Owns(&networkingv1.NetworkPolicy{}). Watches(&corev1.Pod{}, handler.EnqueueRequestsFromMapFunc(dwRelatedPodsHandler)). Watches(&corev1.PersistentVolumeClaim{}, handler.EnqueueRequestsFromMapFunc(r.dwPVCHandler)). Watches(&corev1.Secret{}, handler.EnqueueRequestsFromMapFunc(r.runningWorkspacesHandler), automountWatcher). diff --git a/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml b/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml index 5637227be..a7418782b 100644 --- a/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml +++ b/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml @@ -4010,6 +4010,402 @@ spec: - name type: object type: array + networkPolicy: + description: |- + NetworkPolicy defines configuration options for the NetworkPolicy provisioned + for each DevWorkspace. + properties: + egress: + description: |- + Egress defines the egress rules applied to DevWorkspace pods. If this field is not + specified, the default egress rule of the DevWorkspace Operator applies, which allows + all egress traffic. If this field is specified as an empty list, all egress traffic + from DevWorkspace pods is denied. If this field is specified as a non-empty list, + exactly those rules apply and the default rule no longer applies. + items: + description: |- + 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 + properties: + ports: + description: |- + ports is a list of destination ports for outgoing traffic. + Each item in this list is combined using a logical OR. If this field is + empty or missing, this rule matches all ports (traffic not restricted by port). + If this field is present and contains at least one item, then this rule allows + traffic only if the traffic matches at least one port in the list. + items: + description: NetworkPolicyPort describes a port to allow traffic on + properties: + endPort: + description: |- + endPort indicates that the range of ports from port to endPort if set, inclusive, + should be allowed by the policy. This field cannot be defined if the port field + is not defined or if the port field is defined as a named (string) port. + The endPort must be equal or greater than port. + format: int32 + type: integer + port: + anyOf: + - type: integer + - type: string + description: |- + port represents the port on the given protocol. This can either be a numerical or named + port on a pod. If this field is not provided, this matches all port names and + numbers. + If present, only traffic on the specified protocol AND port will be matched. + x-kubernetes-int-or-string: true + protocol: + description: |- + protocol represents the protocol (TCP, UDP, or SCTP) which traffic must match. + If not specified, this field defaults to TCP. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + to: + description: |- + to is a list of destinations for outgoing traffic of pods selected for this rule. + Items in this list are combined using a logical OR operation. If this field is + empty or missing, this rule matches all destinations (traffic not restricted by + destination). If this field is present and contains at least one item, this rule + allows traffic only if the traffic matches at least one item in the to list. + items: + description: |- + NetworkPolicyPeer describes a peer to allow traffic to/from. Only certain combinations of + fields are allowed + properties: + ipBlock: + description: |- + ipBlock defines policy on a particular IPBlock. If this field is set then + neither of the other fields can be. + properties: + cidr: + description: |- + cidr is a string representing the IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + type: string + except: + description: |- + except is a slice of CIDRs that should not be included within an IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + Except values will be rejected if they are outside the cidr range + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - cidr + type: object + namespaceSelector: + description: |- + namespaceSelector selects namespaces using cluster-scoped labels. This field follows + standard label selector semantics; if present but empty, it selects all namespaces. + + If podSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the namespaces selected by namespaceSelector. + Otherwise it selects all pods in the namespaces selected by namespaceSelector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + podSelector: + description: |- + podSelector is a label selector which selects pods. This field follows standard label + selector semantics; if present but empty, it selects all pods. + + If namespaceSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the Namespaces selected by NamespaceSelector. + Otherwise it selects the pods matching podSelector in the policy's own namespace. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + enabled: + description: |- + Enabled determines whether a NetworkPolicy is provisioned for each DevWorkspace. + Disabled by default. Changing this field does not immediately affect existing + DevWorkspaces: changing the DevWorkspaceOperatorConfig does not enqueue the + DevWorkspaces it affects, so the new value is applied to a DevWorkspace the next + time that DevWorkspace is reconciled for any reason. Restarting a workspace is not + required. Both enabling and disabling apply to running and stopped DevWorkspaces + alike. + type: boolean + ingress: + description: |- + Ingress defines the ingress rules applied to DevWorkspace pods. If this field is not + specified, the default ingress rules of the DevWorkspace Operator apply. On OpenShift, + the defaults allow traffic from the operator's own namespace and from the OpenShift + monitoring and ingress namespaces, and deny all other ingress traffic. On Kubernetes, + the default allows all ingress traffic, since the namespace of the cluster's ingress + controller is not known to the operator; administrators are expected to replace this + with rules appropriate to their cluster. + If this field is specified as an empty list, all ingress traffic to DevWorkspace pods + is denied. If this field is specified as a non-empty list, exactly those rules apply + and the default rules no longer apply. + items: + description: |- + NetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods + matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and from. + properties: + from: + description: |- + from is a list of sources which should be able to access the pods selected for this rule. + Items in this list are combined using a logical OR operation. If this field is + empty or missing, this rule matches all sources (traffic not restricted by + source). If this field is present and contains at least one item, this rule + allows traffic only if the traffic matches at least one item in the from list. + items: + description: |- + NetworkPolicyPeer describes a peer to allow traffic to/from. Only certain combinations of + fields are allowed + properties: + ipBlock: + description: |- + ipBlock defines policy on a particular IPBlock. If this field is set then + neither of the other fields can be. + properties: + cidr: + description: |- + cidr is a string representing the IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + type: string + except: + description: |- + except is a slice of CIDRs that should not be included within an IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + Except values will be rejected if they are outside the cidr range + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - cidr + type: object + namespaceSelector: + description: |- + namespaceSelector selects namespaces using cluster-scoped labels. This field follows + standard label selector semantics; if present but empty, it selects all namespaces. + + If podSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the namespaces selected by namespaceSelector. + Otherwise it selects all pods in the namespaces selected by namespaceSelector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + podSelector: + description: |- + podSelector is a label selector which selects pods. This field follows standard label + selector semantics; if present but empty, it selects all pods. + + If namespaceSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the Namespaces selected by NamespaceSelector. + Otherwise it selects the pods matching podSelector in the policy's own namespace. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + ports: + description: |- + ports is a list of ports which should be made accessible on the pods selected for + this rule. Each item in this list is combined using a logical OR. If this field is + empty or missing, this rule matches all ports (traffic not restricted by port). + If this field is present and contains at least one item, then this rule allows + traffic only if the traffic matches at least one port in the list. + items: + description: NetworkPolicyPort describes a port to allow traffic on + properties: + endPort: + description: |- + endPort indicates that the range of ports from port to endPort if set, inclusive, + should be allowed by the policy. This field cannot be defined if the port field + is not defined or if the port field is defined as a named (string) port. + The endPort must be equal or greater than port. + format: int32 + type: integer + port: + anyOf: + - type: integer + - type: string + description: |- + port represents the port on the given protocol. This can either be a numerical or named + port on a pod. If this field is not provided, this matches all port names and + numbers. + If present, only traffic on the specified protocol AND port will be matched. + x-kubernetes-int-or-string: true + protocol: + description: |- + protocol represents the protocol (TCP, UDP, or SCTP) which traffic must match. + If not specified, this field defaults to TCP. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + type: object overrides: description: |- Overrides defines configuration options for `container-overrides` and diff --git a/deploy/deployment/kubernetes/combined.yaml b/deploy/deployment/kubernetes/combined.yaml index c3a61351b..e6b7126f3 100644 --- a/deploy/deployment/kubernetes/combined.yaml +++ b/deploy/deployment/kubernetes/combined.yaml @@ -4211,6 +4211,416 @@ spec: - name type: object type: array + networkPolicy: + description: |- + NetworkPolicy defines configuration options for the NetworkPolicy provisioned + for each DevWorkspace. + properties: + egress: + description: |- + Egress defines the egress rules applied to DevWorkspace pods. If this field is not + specified, the default egress rule of the DevWorkspace Operator applies, which allows + all egress traffic. If this field is specified as an empty list, all egress traffic + from DevWorkspace pods is denied. If this field is specified as a non-empty list, + exactly those rules apply and the default rule no longer applies. + items: + description: |- + 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 + properties: + ports: + description: |- + ports is a list of destination ports for outgoing traffic. + Each item in this list is combined using a logical OR. If this field is + empty or missing, this rule matches all ports (traffic not restricted by port). + If this field is present and contains at least one item, then this rule allows + traffic only if the traffic matches at least one port in the list. + items: + description: NetworkPolicyPort describes a port to + allow traffic on + properties: + endPort: + description: |- + endPort indicates that the range of ports from port to endPort if set, inclusive, + should be allowed by the policy. This field cannot be defined if the port field + is not defined or if the port field is defined as a named (string) port. + The endPort must be equal or greater than port. + format: int32 + type: integer + port: + anyOf: + - type: integer + - type: string + description: |- + port represents the port on the given protocol. This can either be a numerical or named + port on a pod. If this field is not provided, this matches all port names and + numbers. + If present, only traffic on the specified protocol AND port will be matched. + x-kubernetes-int-or-string: true + protocol: + description: |- + protocol represents the protocol (TCP, UDP, or SCTP) which traffic must match. + If not specified, this field defaults to TCP. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + to: + description: |- + to is a list of destinations for outgoing traffic of pods selected for this rule. + Items in this list are combined using a logical OR operation. If this field is + empty or missing, this rule matches all destinations (traffic not restricted by + destination). If this field is present and contains at least one item, this rule + allows traffic only if the traffic matches at least one item in the to list. + items: + description: |- + NetworkPolicyPeer describes a peer to allow traffic to/from. Only certain combinations of + fields are allowed + properties: + ipBlock: + description: |- + ipBlock defines policy on a particular IPBlock. If this field is set then + neither of the other fields can be. + properties: + cidr: + description: |- + cidr is a string representing the IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + type: string + except: + description: |- + except is a slice of CIDRs that should not be included within an IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + Except values will be rejected if they are outside the cidr range + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - cidr + type: object + namespaceSelector: + description: |- + namespaceSelector selects namespaces using cluster-scoped labels. This field follows + standard label selector semantics; if present but empty, it selects all namespaces. + + If podSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the namespaces selected by namespaceSelector. + Otherwise it selects all pods in the namespaces selected by namespaceSelector. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + podSelector: + description: |- + podSelector is a label selector which selects pods. This field follows standard label + selector semantics; if present but empty, it selects all pods. + + If namespaceSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the Namespaces selected by NamespaceSelector. + Otherwise it selects the pods matching podSelector in the policy's own namespace. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + enabled: + description: |- + Enabled determines whether a NetworkPolicy is provisioned for each DevWorkspace. + Disabled by default. Changing this field does not immediately affect existing + DevWorkspaces: changing the DevWorkspaceOperatorConfig does not enqueue the + DevWorkspaces it affects, so the new value is applied to a DevWorkspace the next + time that DevWorkspace is reconciled for any reason. Restarting a workspace is not + required. Both enabling and disabling apply to running and stopped DevWorkspaces + alike. + type: boolean + ingress: + description: |- + Ingress defines the ingress rules applied to DevWorkspace pods. If this field is not + specified, the default ingress rules of the DevWorkspace Operator apply. On OpenShift, + the defaults allow traffic from the operator's own namespace and from the OpenShift + monitoring and ingress namespaces, and deny all other ingress traffic. On Kubernetes, + the default allows all ingress traffic, since the namespace of the cluster's ingress + controller is not known to the operator; administrators are expected to replace this + with rules appropriate to their cluster. + If this field is specified as an empty list, all ingress traffic to DevWorkspace pods + is denied. If this field is specified as a non-empty list, exactly those rules apply + and the default rules no longer apply. + items: + description: |- + NetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods + matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and from. + properties: + from: + description: |- + from is a list of sources which should be able to access the pods selected for this rule. + Items in this list are combined using a logical OR operation. If this field is + empty or missing, this rule matches all sources (traffic not restricted by + source). If this field is present and contains at least one item, this rule + allows traffic only if the traffic matches at least one item in the from list. + items: + description: |- + NetworkPolicyPeer describes a peer to allow traffic to/from. Only certain combinations of + fields are allowed + properties: + ipBlock: + description: |- + ipBlock defines policy on a particular IPBlock. If this field is set then + neither of the other fields can be. + properties: + cidr: + description: |- + cidr is a string representing the IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + type: string + except: + description: |- + except is a slice of CIDRs that should not be included within an IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + Except values will be rejected if they are outside the cidr range + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - cidr + type: object + namespaceSelector: + description: |- + namespaceSelector selects namespaces using cluster-scoped labels. This field follows + standard label selector semantics; if present but empty, it selects all namespaces. + + If podSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the namespaces selected by namespaceSelector. + Otherwise it selects all pods in the namespaces selected by namespaceSelector. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + podSelector: + description: |- + podSelector is a label selector which selects pods. This field follows standard label + selector semantics; if present but empty, it selects all pods. + + If namespaceSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the Namespaces selected by NamespaceSelector. + Otherwise it selects the pods matching podSelector in the policy's own namespace. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + ports: + description: |- + ports is a list of ports which should be made accessible on the pods selected for + this rule. Each item in this list is combined using a logical OR. If this field is + empty or missing, this rule matches all ports (traffic not restricted by port). + If this field is present and contains at least one item, then this rule allows + traffic only if the traffic matches at least one port in the list. + items: + description: NetworkPolicyPort describes a port to + allow traffic on + properties: + endPort: + description: |- + endPort indicates that the range of ports from port to endPort if set, inclusive, + should be allowed by the policy. This field cannot be defined if the port field + is not defined or if the port field is defined as a named (string) port. + The endPort must be equal or greater than port. + format: int32 + type: integer + port: + anyOf: + - type: integer + - type: string + description: |- + port represents the port on the given protocol. This can either be a numerical or named + port on a pod. If this field is not provided, this matches all port names and + numbers. + If present, only traffic on the specified protocol AND port will be matched. + x-kubernetes-int-or-string: true + protocol: + description: |- + protocol represents the protocol (TCP, UDP, or SCTP) which traffic must match. + If not specified, this field defaults to TCP. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + type: object overrides: description: |- Overrides defines configuration options for `container-overrides` and diff --git a/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml b/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml index 0eb484b64..8c28754fa 100644 --- a/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml +++ b/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml @@ -4211,6 +4211,416 @@ spec: - name type: object type: array + networkPolicy: + description: |- + NetworkPolicy defines configuration options for the NetworkPolicy provisioned + for each DevWorkspace. + properties: + egress: + description: |- + Egress defines the egress rules applied to DevWorkspace pods. If this field is not + specified, the default egress rule of the DevWorkspace Operator applies, which allows + all egress traffic. If this field is specified as an empty list, all egress traffic + from DevWorkspace pods is denied. If this field is specified as a non-empty list, + exactly those rules apply and the default rule no longer applies. + items: + description: |- + 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 + properties: + ports: + description: |- + ports is a list of destination ports for outgoing traffic. + Each item in this list is combined using a logical OR. If this field is + empty or missing, this rule matches all ports (traffic not restricted by port). + If this field is present and contains at least one item, then this rule allows + traffic only if the traffic matches at least one port in the list. + items: + description: NetworkPolicyPort describes a port to + allow traffic on + properties: + endPort: + description: |- + endPort indicates that the range of ports from port to endPort if set, inclusive, + should be allowed by the policy. This field cannot be defined if the port field + is not defined or if the port field is defined as a named (string) port. + The endPort must be equal or greater than port. + format: int32 + type: integer + port: + anyOf: + - type: integer + - type: string + description: |- + port represents the port on the given protocol. This can either be a numerical or named + port on a pod. If this field is not provided, this matches all port names and + numbers. + If present, only traffic on the specified protocol AND port will be matched. + x-kubernetes-int-or-string: true + protocol: + description: |- + protocol represents the protocol (TCP, UDP, or SCTP) which traffic must match. + If not specified, this field defaults to TCP. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + to: + description: |- + to is a list of destinations for outgoing traffic of pods selected for this rule. + Items in this list are combined using a logical OR operation. If this field is + empty or missing, this rule matches all destinations (traffic not restricted by + destination). If this field is present and contains at least one item, this rule + allows traffic only if the traffic matches at least one item in the to list. + items: + description: |- + NetworkPolicyPeer describes a peer to allow traffic to/from. Only certain combinations of + fields are allowed + properties: + ipBlock: + description: |- + ipBlock defines policy on a particular IPBlock. If this field is set then + neither of the other fields can be. + properties: + cidr: + description: |- + cidr is a string representing the IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + type: string + except: + description: |- + except is a slice of CIDRs that should not be included within an IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + Except values will be rejected if they are outside the cidr range + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - cidr + type: object + namespaceSelector: + description: |- + namespaceSelector selects namespaces using cluster-scoped labels. This field follows + standard label selector semantics; if present but empty, it selects all namespaces. + + If podSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the namespaces selected by namespaceSelector. + Otherwise it selects all pods in the namespaces selected by namespaceSelector. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + podSelector: + description: |- + podSelector is a label selector which selects pods. This field follows standard label + selector semantics; if present but empty, it selects all pods. + + If namespaceSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the Namespaces selected by NamespaceSelector. + Otherwise it selects the pods matching podSelector in the policy's own namespace. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + enabled: + description: |- + Enabled determines whether a NetworkPolicy is provisioned for each DevWorkspace. + Disabled by default. Changing this field does not immediately affect existing + DevWorkspaces: changing the DevWorkspaceOperatorConfig does not enqueue the + DevWorkspaces it affects, so the new value is applied to a DevWorkspace the next + time that DevWorkspace is reconciled for any reason. Restarting a workspace is not + required. Both enabling and disabling apply to running and stopped DevWorkspaces + alike. + type: boolean + ingress: + description: |- + Ingress defines the ingress rules applied to DevWorkspace pods. If this field is not + specified, the default ingress rules of the DevWorkspace Operator apply. On OpenShift, + the defaults allow traffic from the operator's own namespace and from the OpenShift + monitoring and ingress namespaces, and deny all other ingress traffic. On Kubernetes, + the default allows all ingress traffic, since the namespace of the cluster's ingress + controller is not known to the operator; administrators are expected to replace this + with rules appropriate to their cluster. + If this field is specified as an empty list, all ingress traffic to DevWorkspace pods + is denied. If this field is specified as a non-empty list, exactly those rules apply + and the default rules no longer apply. + items: + description: |- + NetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods + matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and from. + properties: + from: + description: |- + from is a list of sources which should be able to access the pods selected for this rule. + Items in this list are combined using a logical OR operation. If this field is + empty or missing, this rule matches all sources (traffic not restricted by + source). If this field is present and contains at least one item, this rule + allows traffic only if the traffic matches at least one item in the from list. + items: + description: |- + NetworkPolicyPeer describes a peer to allow traffic to/from. Only certain combinations of + fields are allowed + properties: + ipBlock: + description: |- + ipBlock defines policy on a particular IPBlock. If this field is set then + neither of the other fields can be. + properties: + cidr: + description: |- + cidr is a string representing the IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + type: string + except: + description: |- + except is a slice of CIDRs that should not be included within an IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + Except values will be rejected if they are outside the cidr range + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - cidr + type: object + namespaceSelector: + description: |- + namespaceSelector selects namespaces using cluster-scoped labels. This field follows + standard label selector semantics; if present but empty, it selects all namespaces. + + If podSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the namespaces selected by namespaceSelector. + Otherwise it selects all pods in the namespaces selected by namespaceSelector. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + podSelector: + description: |- + podSelector is a label selector which selects pods. This field follows standard label + selector semantics; if present but empty, it selects all pods. + + If namespaceSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the Namespaces selected by NamespaceSelector. + Otherwise it selects the pods matching podSelector in the policy's own namespace. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + ports: + description: |- + ports is a list of ports which should be made accessible on the pods selected for + this rule. Each item in this list is combined using a logical OR. If this field is + empty or missing, this rule matches all ports (traffic not restricted by port). + If this field is present and contains at least one item, then this rule allows + traffic only if the traffic matches at least one port in the list. + items: + description: NetworkPolicyPort describes a port to + allow traffic on + properties: + endPort: + description: |- + endPort indicates that the range of ports from port to endPort if set, inclusive, + should be allowed by the policy. This field cannot be defined if the port field + is not defined or if the port field is defined as a named (string) port. + The endPort must be equal or greater than port. + format: int32 + type: integer + port: + anyOf: + - type: integer + - type: string + description: |- + port represents the port on the given protocol. This can either be a numerical or named + port on a pod. If this field is not provided, this matches all port names and + numbers. + If present, only traffic on the specified protocol AND port will be matched. + x-kubernetes-int-or-string: true + protocol: + description: |- + protocol represents the protocol (TCP, UDP, or SCTP) which traffic must match. + If not specified, this field defaults to TCP. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + type: object overrides: description: |- Overrides defines configuration options for `container-overrides` and diff --git a/deploy/deployment/openshift/combined.yaml b/deploy/deployment/openshift/combined.yaml index 2d5d8aa5f..ed7d2768d 100644 --- a/deploy/deployment/openshift/combined.yaml +++ b/deploy/deployment/openshift/combined.yaml @@ -4211,6 +4211,416 @@ spec: - name type: object type: array + networkPolicy: + description: |- + NetworkPolicy defines configuration options for the NetworkPolicy provisioned + for each DevWorkspace. + properties: + egress: + description: |- + Egress defines the egress rules applied to DevWorkspace pods. If this field is not + specified, the default egress rule of the DevWorkspace Operator applies, which allows + all egress traffic. If this field is specified as an empty list, all egress traffic + from DevWorkspace pods is denied. If this field is specified as a non-empty list, + exactly those rules apply and the default rule no longer applies. + items: + description: |- + 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 + properties: + ports: + description: |- + ports is a list of destination ports for outgoing traffic. + Each item in this list is combined using a logical OR. If this field is + empty or missing, this rule matches all ports (traffic not restricted by port). + If this field is present and contains at least one item, then this rule allows + traffic only if the traffic matches at least one port in the list. + items: + description: NetworkPolicyPort describes a port to + allow traffic on + properties: + endPort: + description: |- + endPort indicates that the range of ports from port to endPort if set, inclusive, + should be allowed by the policy. This field cannot be defined if the port field + is not defined or if the port field is defined as a named (string) port. + The endPort must be equal or greater than port. + format: int32 + type: integer + port: + anyOf: + - type: integer + - type: string + description: |- + port represents the port on the given protocol. This can either be a numerical or named + port on a pod. If this field is not provided, this matches all port names and + numbers. + If present, only traffic on the specified protocol AND port will be matched. + x-kubernetes-int-or-string: true + protocol: + description: |- + protocol represents the protocol (TCP, UDP, or SCTP) which traffic must match. + If not specified, this field defaults to TCP. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + to: + description: |- + to is a list of destinations for outgoing traffic of pods selected for this rule. + Items in this list are combined using a logical OR operation. If this field is + empty or missing, this rule matches all destinations (traffic not restricted by + destination). If this field is present and contains at least one item, this rule + allows traffic only if the traffic matches at least one item in the to list. + items: + description: |- + NetworkPolicyPeer describes a peer to allow traffic to/from. Only certain combinations of + fields are allowed + properties: + ipBlock: + description: |- + ipBlock defines policy on a particular IPBlock. If this field is set then + neither of the other fields can be. + properties: + cidr: + description: |- + cidr is a string representing the IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + type: string + except: + description: |- + except is a slice of CIDRs that should not be included within an IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + Except values will be rejected if they are outside the cidr range + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - cidr + type: object + namespaceSelector: + description: |- + namespaceSelector selects namespaces using cluster-scoped labels. This field follows + standard label selector semantics; if present but empty, it selects all namespaces. + + If podSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the namespaces selected by namespaceSelector. + Otherwise it selects all pods in the namespaces selected by namespaceSelector. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + podSelector: + description: |- + podSelector is a label selector which selects pods. This field follows standard label + selector semantics; if present but empty, it selects all pods. + + If namespaceSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the Namespaces selected by NamespaceSelector. + Otherwise it selects the pods matching podSelector in the policy's own namespace. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + enabled: + description: |- + Enabled determines whether a NetworkPolicy is provisioned for each DevWorkspace. + Disabled by default. Changing this field does not immediately affect existing + DevWorkspaces: changing the DevWorkspaceOperatorConfig does not enqueue the + DevWorkspaces it affects, so the new value is applied to a DevWorkspace the next + time that DevWorkspace is reconciled for any reason. Restarting a workspace is not + required. Both enabling and disabling apply to running and stopped DevWorkspaces + alike. + type: boolean + ingress: + description: |- + Ingress defines the ingress rules applied to DevWorkspace pods. If this field is not + specified, the default ingress rules of the DevWorkspace Operator apply. On OpenShift, + the defaults allow traffic from the operator's own namespace and from the OpenShift + monitoring and ingress namespaces, and deny all other ingress traffic. On Kubernetes, + the default allows all ingress traffic, since the namespace of the cluster's ingress + controller is not known to the operator; administrators are expected to replace this + with rules appropriate to their cluster. + If this field is specified as an empty list, all ingress traffic to DevWorkspace pods + is denied. If this field is specified as a non-empty list, exactly those rules apply + and the default rules no longer apply. + items: + description: |- + NetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods + matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and from. + properties: + from: + description: |- + from is a list of sources which should be able to access the pods selected for this rule. + Items in this list are combined using a logical OR operation. If this field is + empty or missing, this rule matches all sources (traffic not restricted by + source). If this field is present and contains at least one item, this rule + allows traffic only if the traffic matches at least one item in the from list. + items: + description: |- + NetworkPolicyPeer describes a peer to allow traffic to/from. Only certain combinations of + fields are allowed + properties: + ipBlock: + description: |- + ipBlock defines policy on a particular IPBlock. If this field is set then + neither of the other fields can be. + properties: + cidr: + description: |- + cidr is a string representing the IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + type: string + except: + description: |- + except is a slice of CIDRs that should not be included within an IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + Except values will be rejected if they are outside the cidr range + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - cidr + type: object + namespaceSelector: + description: |- + namespaceSelector selects namespaces using cluster-scoped labels. This field follows + standard label selector semantics; if present but empty, it selects all namespaces. + + If podSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the namespaces selected by namespaceSelector. + Otherwise it selects all pods in the namespaces selected by namespaceSelector. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + podSelector: + description: |- + podSelector is a label selector which selects pods. This field follows standard label + selector semantics; if present but empty, it selects all pods. + + If namespaceSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the Namespaces selected by NamespaceSelector. + Otherwise it selects the pods matching podSelector in the policy's own namespace. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + ports: + description: |- + ports is a list of ports which should be made accessible on the pods selected for + this rule. Each item in this list is combined using a logical OR. If this field is + empty or missing, this rule matches all ports (traffic not restricted by port). + If this field is present and contains at least one item, then this rule allows + traffic only if the traffic matches at least one port in the list. + items: + description: NetworkPolicyPort describes a port to + allow traffic on + properties: + endPort: + description: |- + endPort indicates that the range of ports from port to endPort if set, inclusive, + should be allowed by the policy. This field cannot be defined if the port field + is not defined or if the port field is defined as a named (string) port. + The endPort must be equal or greater than port. + format: int32 + type: integer + port: + anyOf: + - type: integer + - type: string + description: |- + port represents the port on the given protocol. This can either be a numerical or named + port on a pod. If this field is not provided, this matches all port names and + numbers. + If present, only traffic on the specified protocol AND port will be matched. + x-kubernetes-int-or-string: true + protocol: + description: |- + protocol represents the protocol (TCP, UDP, or SCTP) which traffic must match. + If not specified, this field defaults to TCP. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + type: object overrides: description: |- Overrides defines configuration options for `container-overrides` and diff --git a/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml b/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml index 0eb484b64..8c28754fa 100644 --- a/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml +++ b/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml @@ -4211,6 +4211,416 @@ spec: - name type: object type: array + networkPolicy: + description: |- + NetworkPolicy defines configuration options for the NetworkPolicy provisioned + for each DevWorkspace. + properties: + egress: + description: |- + Egress defines the egress rules applied to DevWorkspace pods. If this field is not + specified, the default egress rule of the DevWorkspace Operator applies, which allows + all egress traffic. If this field is specified as an empty list, all egress traffic + from DevWorkspace pods is denied. If this field is specified as a non-empty list, + exactly those rules apply and the default rule no longer applies. + items: + description: |- + 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 + properties: + ports: + description: |- + ports is a list of destination ports for outgoing traffic. + Each item in this list is combined using a logical OR. If this field is + empty or missing, this rule matches all ports (traffic not restricted by port). + If this field is present and contains at least one item, then this rule allows + traffic only if the traffic matches at least one port in the list. + items: + description: NetworkPolicyPort describes a port to + allow traffic on + properties: + endPort: + description: |- + endPort indicates that the range of ports from port to endPort if set, inclusive, + should be allowed by the policy. This field cannot be defined if the port field + is not defined or if the port field is defined as a named (string) port. + The endPort must be equal or greater than port. + format: int32 + type: integer + port: + anyOf: + - type: integer + - type: string + description: |- + port represents the port on the given protocol. This can either be a numerical or named + port on a pod. If this field is not provided, this matches all port names and + numbers. + If present, only traffic on the specified protocol AND port will be matched. + x-kubernetes-int-or-string: true + protocol: + description: |- + protocol represents the protocol (TCP, UDP, or SCTP) which traffic must match. + If not specified, this field defaults to TCP. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + to: + description: |- + to is a list of destinations for outgoing traffic of pods selected for this rule. + Items in this list are combined using a logical OR operation. If this field is + empty or missing, this rule matches all destinations (traffic not restricted by + destination). If this field is present and contains at least one item, this rule + allows traffic only if the traffic matches at least one item in the to list. + items: + description: |- + NetworkPolicyPeer describes a peer to allow traffic to/from. Only certain combinations of + fields are allowed + properties: + ipBlock: + description: |- + ipBlock defines policy on a particular IPBlock. If this field is set then + neither of the other fields can be. + properties: + cidr: + description: |- + cidr is a string representing the IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + type: string + except: + description: |- + except is a slice of CIDRs that should not be included within an IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + Except values will be rejected if they are outside the cidr range + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - cidr + type: object + namespaceSelector: + description: |- + namespaceSelector selects namespaces using cluster-scoped labels. This field follows + standard label selector semantics; if present but empty, it selects all namespaces. + + If podSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the namespaces selected by namespaceSelector. + Otherwise it selects all pods in the namespaces selected by namespaceSelector. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + podSelector: + description: |- + podSelector is a label selector which selects pods. This field follows standard label + selector semantics; if present but empty, it selects all pods. + + If namespaceSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the Namespaces selected by NamespaceSelector. + Otherwise it selects the pods matching podSelector in the policy's own namespace. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + enabled: + description: |- + Enabled determines whether a NetworkPolicy is provisioned for each DevWorkspace. + Disabled by default. Changing this field does not immediately affect existing + DevWorkspaces: changing the DevWorkspaceOperatorConfig does not enqueue the + DevWorkspaces it affects, so the new value is applied to a DevWorkspace the next + time that DevWorkspace is reconciled for any reason. Restarting a workspace is not + required. Both enabling and disabling apply to running and stopped DevWorkspaces + alike. + type: boolean + ingress: + description: |- + Ingress defines the ingress rules applied to DevWorkspace pods. If this field is not + specified, the default ingress rules of the DevWorkspace Operator apply. On OpenShift, + the defaults allow traffic from the operator's own namespace and from the OpenShift + monitoring and ingress namespaces, and deny all other ingress traffic. On Kubernetes, + the default allows all ingress traffic, since the namespace of the cluster's ingress + controller is not known to the operator; administrators are expected to replace this + with rules appropriate to their cluster. + If this field is specified as an empty list, all ingress traffic to DevWorkspace pods + is denied. If this field is specified as a non-empty list, exactly those rules apply + and the default rules no longer apply. + items: + description: |- + NetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods + matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and from. + properties: + from: + description: |- + from is a list of sources which should be able to access the pods selected for this rule. + Items in this list are combined using a logical OR operation. If this field is + empty or missing, this rule matches all sources (traffic not restricted by + source). If this field is present and contains at least one item, this rule + allows traffic only if the traffic matches at least one item in the from list. + items: + description: |- + NetworkPolicyPeer describes a peer to allow traffic to/from. Only certain combinations of + fields are allowed + properties: + ipBlock: + description: |- + ipBlock defines policy on a particular IPBlock. If this field is set then + neither of the other fields can be. + properties: + cidr: + description: |- + cidr is a string representing the IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + type: string + except: + description: |- + except is a slice of CIDRs that should not be included within an IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + Except values will be rejected if they are outside the cidr range + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - cidr + type: object + namespaceSelector: + description: |- + namespaceSelector selects namespaces using cluster-scoped labels. This field follows + standard label selector semantics; if present but empty, it selects all namespaces. + + If podSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the namespaces selected by namespaceSelector. + Otherwise it selects all pods in the namespaces selected by namespaceSelector. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + podSelector: + description: |- + podSelector is a label selector which selects pods. This field follows standard label + selector semantics; if present but empty, it selects all pods. + + If namespaceSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the Namespaces selected by NamespaceSelector. + Otherwise it selects the pods matching podSelector in the policy's own namespace. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + ports: + description: |- + ports is a list of ports which should be made accessible on the pods selected for + this rule. Each item in this list is combined using a logical OR. If this field is + empty or missing, this rule matches all ports (traffic not restricted by port). + If this field is present and contains at least one item, then this rule allows + traffic only if the traffic matches at least one port in the list. + items: + description: NetworkPolicyPort describes a port to + allow traffic on + properties: + endPort: + description: |- + endPort indicates that the range of ports from port to endPort if set, inclusive, + should be allowed by the policy. This field cannot be defined if the port field + is not defined or if the port field is defined as a named (string) port. + The endPort must be equal or greater than port. + format: int32 + type: integer + port: + anyOf: + - type: integer + - type: string + description: |- + port represents the port on the given protocol. This can either be a numerical or named + port on a pod. If this field is not provided, this matches all port names and + numbers. + If present, only traffic on the specified protocol AND port will be matched. + x-kubernetes-int-or-string: true + protocol: + description: |- + protocol represents the protocol (TCP, UDP, or SCTP) which traffic must match. + If not specified, this field defaults to TCP. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + type: object overrides: description: |- Overrides defines configuration options for `container-overrides` and diff --git a/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml b/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml index ab5b2f381..96abef38f 100644 --- a/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml +++ b/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml @@ -4209,6 +4209,416 @@ spec: - name type: object type: array + networkPolicy: + description: |- + NetworkPolicy defines configuration options for the NetworkPolicy provisioned + for each DevWorkspace. + properties: + egress: + description: |- + Egress defines the egress rules applied to DevWorkspace pods. If this field is not + specified, the default egress rule of the DevWorkspace Operator applies, which allows + all egress traffic. If this field is specified as an empty list, all egress traffic + from DevWorkspace pods is denied. If this field is specified as a non-empty list, + exactly those rules apply and the default rule no longer applies. + items: + description: |- + 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 + properties: + ports: + description: |- + ports is a list of destination ports for outgoing traffic. + Each item in this list is combined using a logical OR. If this field is + empty or missing, this rule matches all ports (traffic not restricted by port). + If this field is present and contains at least one item, then this rule allows + traffic only if the traffic matches at least one port in the list. + items: + description: NetworkPolicyPort describes a port to + allow traffic on + properties: + endPort: + description: |- + endPort indicates that the range of ports from port to endPort if set, inclusive, + should be allowed by the policy. This field cannot be defined if the port field + is not defined or if the port field is defined as a named (string) port. + The endPort must be equal or greater than port. + format: int32 + type: integer + port: + anyOf: + - type: integer + - type: string + description: |- + port represents the port on the given protocol. This can either be a numerical or named + port on a pod. If this field is not provided, this matches all port names and + numbers. + If present, only traffic on the specified protocol AND port will be matched. + x-kubernetes-int-or-string: true + protocol: + description: |- + protocol represents the protocol (TCP, UDP, or SCTP) which traffic must match. + If not specified, this field defaults to TCP. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + to: + description: |- + to is a list of destinations for outgoing traffic of pods selected for this rule. + Items in this list are combined using a logical OR operation. If this field is + empty or missing, this rule matches all destinations (traffic not restricted by + destination). If this field is present and contains at least one item, this rule + allows traffic only if the traffic matches at least one item in the to list. + items: + description: |- + NetworkPolicyPeer describes a peer to allow traffic to/from. Only certain combinations of + fields are allowed + properties: + ipBlock: + description: |- + ipBlock defines policy on a particular IPBlock. If this field is set then + neither of the other fields can be. + properties: + cidr: + description: |- + cidr is a string representing the IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + type: string + except: + description: |- + except is a slice of CIDRs that should not be included within an IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + Except values will be rejected if they are outside the cidr range + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - cidr + type: object + namespaceSelector: + description: |- + namespaceSelector selects namespaces using cluster-scoped labels. This field follows + standard label selector semantics; if present but empty, it selects all namespaces. + + If podSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the namespaces selected by namespaceSelector. + Otherwise it selects all pods in the namespaces selected by namespaceSelector. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + podSelector: + description: |- + podSelector is a label selector which selects pods. This field follows standard label + selector semantics; if present but empty, it selects all pods. + + If namespaceSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the Namespaces selected by NamespaceSelector. + Otherwise it selects the pods matching podSelector in the policy's own namespace. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + enabled: + description: |- + Enabled determines whether a NetworkPolicy is provisioned for each DevWorkspace. + Disabled by default. Changing this field does not immediately affect existing + DevWorkspaces: changing the DevWorkspaceOperatorConfig does not enqueue the + DevWorkspaces it affects, so the new value is applied to a DevWorkspace the next + time that DevWorkspace is reconciled for any reason. Restarting a workspace is not + required. Both enabling and disabling apply to running and stopped DevWorkspaces + alike. + type: boolean + ingress: + description: |- + Ingress defines the ingress rules applied to DevWorkspace pods. If this field is not + specified, the default ingress rules of the DevWorkspace Operator apply. On OpenShift, + the defaults allow traffic from the operator's own namespace and from the OpenShift + monitoring and ingress namespaces, and deny all other ingress traffic. On Kubernetes, + the default allows all ingress traffic, since the namespace of the cluster's ingress + controller is not known to the operator; administrators are expected to replace this + with rules appropriate to their cluster. + If this field is specified as an empty list, all ingress traffic to DevWorkspace pods + is denied. If this field is specified as a non-empty list, exactly those rules apply + and the default rules no longer apply. + items: + description: |- + NetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods + matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and from. + properties: + from: + description: |- + from is a list of sources which should be able to access the pods selected for this rule. + Items in this list are combined using a logical OR operation. If this field is + empty or missing, this rule matches all sources (traffic not restricted by + source). If this field is present and contains at least one item, this rule + allows traffic only if the traffic matches at least one item in the from list. + items: + description: |- + NetworkPolicyPeer describes a peer to allow traffic to/from. Only certain combinations of + fields are allowed + properties: + ipBlock: + description: |- + ipBlock defines policy on a particular IPBlock. If this field is set then + neither of the other fields can be. + properties: + cidr: + description: |- + cidr is a string representing the IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + type: string + except: + description: |- + except is a slice of CIDRs that should not be included within an IPBlock + Valid examples are "192.168.1.0/24" or "2001:db8::/64" + Except values will be rejected if they are outside the cidr range + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - cidr + type: object + namespaceSelector: + description: |- + namespaceSelector selects namespaces using cluster-scoped labels. This field follows + standard label selector semantics; if present but empty, it selects all namespaces. + + If podSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the namespaces selected by namespaceSelector. + Otherwise it selects all pods in the namespaces selected by namespaceSelector. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + podSelector: + description: |- + podSelector is a label selector which selects pods. This field follows standard label + selector semantics; if present but empty, it selects all pods. + + If namespaceSelector is also set, then the NetworkPolicyPeer as a whole selects + the pods matching podSelector in the Namespaces selected by NamespaceSelector. + Otherwise it selects the pods matching podSelector in the policy's own namespace. + properties: + matchExpressions: + description: matchExpressions is a list of + label selector requirements. The requirements + are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic + ports: + description: |- + ports is a list of ports which should be made accessible on the pods selected for + this rule. Each item in this list is combined using a logical OR. If this field is + empty or missing, this rule matches all ports (traffic not restricted by port). + If this field is present and contains at least one item, then this rule allows + traffic only if the traffic matches at least one port in the list. + items: + description: NetworkPolicyPort describes a port to + allow traffic on + properties: + endPort: + description: |- + endPort indicates that the range of ports from port to endPort if set, inclusive, + should be allowed by the policy. This field cannot be defined if the port field + is not defined or if the port field is defined as a named (string) port. + The endPort must be equal or greater than port. + format: int32 + type: integer + port: + anyOf: + - type: integer + - type: string + description: |- + port represents the port on the given protocol. This can either be a numerical or named + port on a pod. If this field is not provided, this matches all port names and + numbers. + If present, only traffic on the specified protocol AND port will be matched. + x-kubernetes-int-or-string: true + protocol: + description: |- + protocol represents the protocol (TCP, UDP, or SCTP) which traffic must match. + If not specified, this field defaults to TCP. + type: string + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: array + type: object overrides: description: |- Overrides defines configuration options for `container-overrides` and diff --git a/docs/dwo-configuration.md b/docs/dwo-configuration.md index c20588afc..fa18301a6 100644 --- a/docs/dwo-configuration.md +++ b/docs/dwo-configuration.md @@ -231,6 +231,47 @@ config: The config above will have newly created PVCs to have its access mode set to `ReadWriteMany`. +## Configuring Workspace NetworkPolicy + +By default, DevWorkspace pods accept traffic from anywhere in the cluster. +Administrators can enable NetworkPolicy provisioning to restrict workspace network access. +When enabled, the operator creates one NetworkPolicy per DevWorkspace, +named `-networkpolicy` in the workspace's namespace. Each policy applies only +to the pods of its own workspace, selected by the `controller.devfile.io/devworkspace_id` label. + +**Rule semantics:** +- Omitting a rule field (e.g., no `ingress` key) means the operator's default rules for that direction apply. +- Setting a rule field to an empty list (e.g., `ingress: []`) denies all traffic in that direction. +- Setting `ingress` or `egress` to a non-empty list applies exactly those rules and replaces the defaults +entirely rather than appending to them. Any default rule that should be kept must be repeated in the configuration. + +**Lifecycle:** +- A workspace's policy is synced on every reconcile of that workspace, whether it is running or stopped, +and before any workspace pod is created. +- Changing this configuration does not enqueue the workspaces it affects, so a new value reaches a given +workspace on its next reconcile rather than immediately. Restarting a workspace is not required. +- The policy is owned by its DevWorkspace, so it is garbage collected when the workspace is deleted. +- Disabling the feature (setting `enabled: false`) removes the policies of both running and stopped workspaces, +restoring connectivity rather than leaving stale policies in place. +- While the feature is enabled, the policy of a stopped workspace is left in place, where it governs no pods, +and is removed when the workspace is deleted. + +```yaml +apiVersion: controller.devfile.io/v1alpha1 +kind: DevWorkspaceOperatorConfig +metadata: + name: devworkspace-operator-config + namespace: $OPERATOR_INSTALL_NAMESPACE +config: + workspace: + networkPolicy: + enabled: true + ingress: + - {} + egress: + - {} +``` + ## Configuring Custom Init Containers The DevWorkspace Operator allows cluster administrators to inject custom init containers into all workspace pods via the `config.workspace.initContainers` field in the global DWOC. This feature enables use cases such as: diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go index 5d747da65..aa77c9bdb 100644 --- a/pkg/cache/cache.go +++ b/pkg/cache/cache.go @@ -92,6 +92,9 @@ func GetCacheFunc() (cache.NewCacheFunc, error) { &rbacv1.RoleBinding{}: { Label: rbacObjectSelector, }, + &networkingv1.NetworkPolicy{}: { + Label: devworkspaceObjectSelector, + }, } if infrastructure.IsOpenShift() { diff --git a/pkg/common/naming.go b/pkg/common/naming.go index e8f4e183c..c7f2504f6 100644 --- a/pkg/common/naming.go +++ b/pkg/common/naming.go @@ -166,6 +166,10 @@ func WorkspaceRolebindingName() string { return "devworkspace-default-rolebinding" } +func NetworkPolicyName(workspaceId string) string { + return fmt.Sprintf("%s-%s", workspaceId, "networkpolicy") +} + func WorkspaceSCCRoleName(sccName string) string { return fmt.Sprintf("devworkspace-use-%s", sccName) } diff --git a/pkg/config/common_test.go b/pkg/config/common_test.go index 7a161b074..07565f4f5 100644 --- a/pkg/config/common_test.go +++ b/pkg/config/common_test.go @@ -57,6 +57,7 @@ func setupForTest(t *testing.T) { setDefaultPodSecurityContext() setDefaultContainerSecurityContext() setDefaultOverrideConfig() + setDefaultNetworkPolicy() configNamespace = testNamespace originalDefaultConfig := defaultConfig.DeepCopy() t.Cleanup(func() { diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index 7b299ac38..7e74bf29e 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -19,10 +19,13 @@ import ( "fmt" "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" + "github.com/devfile/devworkspace-operator/pkg/constants" "github.com/devfile/devworkspace-operator/pkg/infrastructure" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/pointer" ) @@ -55,6 +58,7 @@ var defaultConfig = &v1alpha1.OperatorConfiguration{ CleanupOnStop: pointer.Bool(false), PodSecurityContext: nil, // Set per-platform in setDefaultPodSecurityContext() ContainerSecurityContext: nil, // Set per-platform in setDefaultContainerSecurityContext() + NetworkPolicy: nil, // Set per-platform in setDefaultNetworkPolicy() DefaultTemplate: nil, ProjectCloneConfig: &v1alpha1.ProjectCloneConfig{ Resources: &corev1.ResourceRequirements{ @@ -150,6 +154,21 @@ var ( }, }, } + + defaultEgressPolicyRules = []networkingv1.NetworkPolicyEgressRule{{}} + defaultKubernetesIngressPolicyRules = []networkingv1.NetworkPolicyIngressRule{{}} + defaultOpenShiftIngressPolicyRules = []networkingv1.NetworkPolicyIngressRule{ + { + From: []networkingv1.NetworkPolicyPeer{ + {NamespaceSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"network.openshift.io/policy-group": "monitoring"}}}, + }, + }, + { + From: []networkingv1.NetworkPolicyPeer{ + {NamespaceSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"network.openshift.io/policy-group": "ingress"}}}, + }, + }, + } ) // Necessary variables for setting pointer values @@ -158,6 +177,14 @@ var ( perWorkspaceStorageSize = resource.MustParse("10Gi") ) +// GetDefaultConfig returns a copy of the operator's default configuration. It has no +// callers inside this repository: it exists for projects that embed DWO as a dependency, +// such as che-operator, which read the defaults in order to extend them rather than +// restate them. +func GetDefaultConfig() *v1alpha1.OperatorConfiguration { + return defaultConfig.DeepCopy() +} + func setDefaultPodSecurityContext() error { if !infrastructure.IsInitialized() { return fmt.Errorf("can not set default pod security context, infrastructure not detected") @@ -193,3 +220,40 @@ func setDefaultOverrideConfig() error { } return nil } + +func setDefaultNetworkPolicy() error { + if !infrastructure.IsInitialized() { + return fmt.Errorf("can not set default network policy, infrastructure not detected") + } + operatorNamespace, err := infrastructure.GetNamespace() + if err != nil { + return err + } + + var ingressPolicyRules []networkingv1.NetworkPolicyIngressRule + if infrastructure.IsOpenShift() { + allowFromDevWorkspaceIngressPolicyRule := networkingv1.NetworkPolicyIngressRule{ + From: []networkingv1.NetworkPolicyPeer{ + { + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"kubernetes.io/metadata.name": operatorNamespace}, + }, + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app.kubernetes.io/part-of": "devworkspace-operator"}, + }, + }, + }, + } + ingressPolicyRules = []networkingv1.NetworkPolicyIngressRule{allowFromDevWorkspaceIngressPolicyRule} + ingressPolicyRules = append(ingressPolicyRules, defaultOpenShiftIngressPolicyRules...) + } else { + ingressPolicyRules = defaultKubernetesIngressPolicyRules + } + + defaultConfig.Workspace.NetworkPolicy = &v1alpha1.NetworkPolicyConfig{ + Enabled: pointer.Bool(constants.DefaultNetworkPolicyEnabled), + Ingress: ingressPolicyRules, + Egress: defaultEgressPolicyRules, + } + return nil +} diff --git a/pkg/config/sync.go b/pkg/config/sync.go index 8ff002135..574654440 100644 --- a/pkg/config/sync.go +++ b/pkg/config/sync.go @@ -102,6 +102,7 @@ func SetGlobalConfigForTesting(testConfig *controller.OperatorConfiguration) { setDefaultPodSecurityContext() setDefaultContainerSecurityContext() setDefaultOverrideConfig() + setDefaultNetworkPolicy() internalConfig = defaultConfig.DeepCopy() mergeConfig(testConfig, internalConfig) } @@ -119,7 +120,9 @@ func SetupControllerConfig(client crclient.Client) error { if err := setDefaultOverrideConfig(); err != nil { return err } - + if err := setDefaultNetworkPolicy(); err != nil { + return err + } internalConfig = &controller.OperatorConfiguration{} namespace, err := infrastructure.GetNamespace() @@ -522,6 +525,20 @@ func mergeConfig(from, to *controller.OperatorConfiguration) { to.Workspace.Overrides.RestrictedPodOverrideFields = from.Workspace.Overrides.RestrictedPodOverrideFields } } + if from.Workspace.NetworkPolicy != nil { + if to.Workspace.NetworkPolicy == nil { + to.Workspace.NetworkPolicy = &controller.NetworkPolicyConfig{} + } + if from.Workspace.NetworkPolicy.Enabled != nil { + to.Workspace.NetworkPolicy.Enabled = from.Workspace.NetworkPolicy.Enabled + } + if from.Workspace.NetworkPolicy.Ingress != nil { + to.Workspace.NetworkPolicy.Ingress = from.Workspace.NetworkPolicy.Ingress + } + if from.Workspace.NetworkPolicy.Egress != nil { + to.Workspace.NetworkPolicy.Egress = from.Workspace.NetworkPolicy.Egress + } + } } } @@ -665,6 +682,9 @@ func GetCurrentConfigString(currConfig *controller.OperatorConfiguration) string if workspace.DeploymentStrategy != defaultConfig.Workspace.DeploymentStrategy { config = append(config, fmt.Sprintf("workspace.deploymentStrategy=%s", workspace.DeploymentStrategy)) } + if workspace.NetworkPolicy != nil && pointer.BoolDeref(workspace.NetworkPolicy.Enabled, constants.DefaultNetworkPolicyEnabled) { + config = append(config, "workspace.networkPolicy.enabled=true") + } if workspace.PVCName != defaultConfig.Workspace.PVCName { config = append(config, fmt.Sprintf("workspace.pvcName=%s", workspace.PVCName)) } diff --git a/pkg/config/sync_test.go b/pkg/config/sync_test.go index c01cada4a..0882e52bf 100644 --- a/pkg/config/sync_test.go +++ b/pkg/config/sync_test.go @@ -32,6 +32,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/pointer" "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -110,6 +111,7 @@ func TestMergesAllFieldsFromClusterConfig(t *testing.T) { fuzzQuantity, fuzzResourceList, fuzzResourceRequirements, + fuzzIntOrString, ) for i := 0; i < 100; i++ { fuzzedConfig := &v1alpha1.OperatorConfiguration{} @@ -433,6 +435,7 @@ func TestMergeConfigLooksAtAllFields(t *testing.T) { fuzzResourceList, fuzzResourceRequirements, fuzzStringPtr, + fuzzIntOrString, ) expectedConfig := &v1alpha1.OperatorConfiguration{} actualConfig := &v1alpha1.OperatorConfiguration{} @@ -636,6 +639,13 @@ func fuzzResourceRequirements(req *corev1.ResourceRequirements, c fuzz.Continue) req.Requests = requests } +// fuzzIntOrString generates a port that can survive a round trip through the API server. +// Fuzzing the struct directly picks a random value for the Type discriminator, and any +// value other than Int or String makes marshalling fail with "impossible IntOrString.Type". +func fuzzIntOrString(port *intstr.IntOrString, c fuzz.Continue) { + *port = intstr.FromInt32(c.Int31n(65535) + 1) +} + func fuzzStringPtr(str **string, c fuzz.Continue) { randString := c.RandString() // Ensure we never assign an empty string to avoid mergeConfig skipping updates. diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 43e187e7e..b8012a066 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -114,6 +114,10 @@ const ( RbacRoleKind = "Role" // ClusterRole kind RbacClusterRoleKind = "ClusterRole" + + // DefaultNetworkPolicyEnabled defines the default value for the Enabled field, + // disabling NetworkPolicy provisioning into DevWorkspace namespaces by default. + DefaultNetworkPolicyEnabled = false ) const ( diff --git a/pkg/provision/sync/diff.go b/pkg/provision/sync/diff.go index 83e115dea..fdb3fd945 100644 --- a/pkg/provision/sync/diff.go +++ b/pkg/provision/sync/diff.go @@ -49,6 +49,7 @@ var diffFuncs = map[reflect.Type]diffFunc{ reflect.TypeOf(batchv1.Job{}): allDiffFuncs(metadataDiffFunc, jobDiffFunc), reflect.TypeOf(corev1.Service{}): allDiffFuncs(metadataDiffFunc, serviceDiffFunc), reflect.TypeOf(networkingv1.Ingress{}): allDiffFuncs(metadataDiffFunc, basicDiffFunc(ingressDiffOpts)), + reflect.TypeOf(networkingv1.NetworkPolicy{}): allDiffFuncs(metadataDiffFunc, basicDiffFunc(networkPolicyDiffOpts)), reflect.TypeOf(routev1.Route{}): allDiffFuncs(metadataDiffFunc, basicDiffFunc(routeDiffOpts)), } diff --git a/pkg/provision/sync/diffopts.go b/pkg/provision/sync/diffopts.go index 536e82f77..3e7a6e6f6 100644 --- a/pkg/provision/sync/diffopts.go +++ b/pkg/provision/sync/diffopts.go @@ -92,6 +92,10 @@ var ingressDiffOpts = cmp.Options{ cmpopts.IgnoreFields(networkingv1.HTTPIngressPath{}, "PathType"), } +var networkPolicyDiffOpts = cmp.Options{ + cmpopts.IgnoreFields(networkingv1.NetworkPolicy{}, "TypeMeta", "ObjectMeta"), +} + func getNameFromEnvFrom(source corev1.EnvFromSource) string { switch { case source.ConfigMapRef != nil: diff --git a/pkg/provision/sync/sync.go b/pkg/provision/sync/sync.go index a231e45f8..25cfd9a36 100644 --- a/pkg/provision/sync/sync.go +++ b/pkg/provision/sync/sync.go @@ -204,6 +204,8 @@ func printDiff(specObj, clusterObj crclient.Object, log logr.Logger) { diffOpts = routingDiffOpts case *networkingv1.Ingress: diffOpts = ingressDiffOpts + case *networkingv1.NetworkPolicy: + diffOpts = networkPolicyDiffOpts case *routev1.Route: diffOpts = routeDiffOpts case *corev1.Secret: diff --git a/pkg/provision/workspace/networkpolicy/networkpolicy.go b/pkg/provision/workspace/networkpolicy/networkpolicy.go new file mode 100644 index 000000000..48a09cc9a --- /dev/null +++ b/pkg/provision/workspace/networkpolicy/networkpolicy.go @@ -0,0 +1,153 @@ +// Copyright (c) 2019-2026 Red Hat, Inc. +// 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 networkpolicy + +import ( + "fmt" + + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + k8sErrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" + "github.com/devfile/devworkspace-operator/pkg/common" + "github.com/devfile/devworkspace-operator/pkg/constants" + "github.com/devfile/devworkspace-operator/pkg/dwerrors" + "github.com/devfile/devworkspace-operator/pkg/provision/sync" +) + +// generateNetworkPolicy builds the NetworkPolicy applied to a single DevWorkspace's pods. +// The name, labels, podSelector and policyTypes are owned by the operator; only the ingress +// and egress rules come from configuration. Both directions are always listed in policyTypes, +// so a direction whose configured rule list is empty denies all traffic in that direction. +func generateNetworkPolicy(workspace *common.DevWorkspaceWithConfig, npConfig *v1alpha1.NetworkPolicyConfig) *networkingv1.NetworkPolicy { + workspaceId := workspace.Status.DevWorkspaceId + policy := &networkingv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.NetworkPolicyName(workspaceId), + Namespace: workspace.Namespace, + Labels: map[string]string{ + constants.DevWorkspaceIDLabel: workspaceId, + constants.DevWorkspaceNameLabel: workspace.Name, + }, + }, + Spec: networkingv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + constants.DevWorkspaceIDLabel: workspaceId, + }, + }, + PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress, networkingv1.PolicyTypeEgress}, + }, + } + + if len(npConfig.Ingress) > 0 { + ingress := make([]networkingv1.NetworkPolicyIngressRule, len(npConfig.Ingress)) + for i, rule := range npConfig.Ingress { + ruleCopy := rule.DeepCopy() + normalizePorts(ruleCopy.Ports) + ingress[i] = *ruleCopy + } + policy.Spec.Ingress = ingress + } + + if len(npConfig.Egress) > 0 { + egress := make([]networkingv1.NetworkPolicyEgressRule, len(npConfig.Egress)) + for i, rule := range npConfig.Egress { + ruleCopy := rule.DeepCopy() + normalizePorts(ruleCopy.Ports) + egress[i] = *ruleCopy + } + policy.Spec.Egress = egress + } + + return policy +} + +// normalizePorts fills in the protocol the API server would default, so that the generated +// spec compares equal to the object stored on the cluster and does not trigger an endless +// update loop. +func normalizePorts(ports []networkingv1.NetworkPolicyPort) { + for i := range ports { + if ports[i].Protocol == nil { + ports[i].Protocol = ptr.To(corev1.ProtocolTCP) + } + } +} + +func ShouldProvision(workspace *common.DevWorkspaceWithConfig) bool { + npConfig := workspace.Config.Workspace.NetworkPolicy + if npConfig == nil { + return false + } + return ptr.Deref(npConfig.Enabled, constants.DefaultNetworkPolicyEnabled) +} + +func SyncNetworkPolicy(workspace *common.DevWorkspaceWithConfig, api sync.ClusterAPI) error { + if !ShouldProvision(workspace) { + return DeleteNetworkPolicy(workspace, api) + } + return CreateNetworkPolicy(workspace, api) +} + +func CreateNetworkPolicy(workspace *common.DevWorkspaceWithConfig, api sync.ClusterAPI) error { + specPolicy := generateNetworkPolicy(workspace, workspace.Config.Workspace.NetworkPolicy) + if err := controllerutil.SetControllerReference(workspace.DevWorkspace, specPolicy, api.Scheme); err != nil { + return &dwerrors.FailError{ + Message: "failed to set owner reference on workspace network policy", + Err: err, + } + } + if _, err := sync.SyncObjectWithCluster(specPolicy, api); err != nil { + return dwerrors.WrapSyncError(err) + } + return nil +} + +// DeleteNetworkPolicy removes a DevWorkspace's NetworkPolicy if it exists. It is a no-op +// when no policy is present, so callers can invoke it unconditionally. Deleting the +// DevWorkspace itself does not require this call: the policy carries an ownerReference and +// is garbage collected with the workspace. +func DeleteNetworkPolicy(workspace *common.DevWorkspaceWithConfig, api sync.ClusterAPI) error { + name := common.NetworkPolicyName(workspace.Status.DevWorkspaceId) + policy := &networkingv1.NetworkPolicy{} + namespacedName := types.NamespacedName{ + Name: name, + Namespace: workspace.Namespace, + } + err := api.Client.Get(api.Ctx, namespacedName, policy) + switch { + case err == nil: + if err := api.Client.Delete(api.Ctx, policy); err != nil && !k8sErrors.IsNotFound(err) { + return &dwerrors.RetryError{ + Message: fmt.Sprintf("failed to delete network policy %s in namespace %s", name, workspace.Namespace), + Err: err, + } + } + api.Logger.Info("Deleted workspace network policy", "name", name, "namespace", workspace.Namespace) + return nil + case k8sErrors.IsNotFound(err): + // Already deleted + return nil + default: + return &dwerrors.RetryError{ + Message: fmt.Sprintf("failed to read network policy %s in namespace %s", name, workspace.Namespace), + Err: err, + } + } +} diff --git a/pkg/provision/workspace/networkpolicy/networkpolicy_test.go b/pkg/provision/workspace/networkpolicy/networkpolicy_test.go new file mode 100644 index 000000000..82846561f --- /dev/null +++ b/pkg/provision/workspace/networkpolicy/networkpolicy_test.go @@ -0,0 +1,204 @@ +// Copyright (c) 2019-2026 Red Hat, Inc. +// 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 networkpolicy + +import ( + "context" + "fmt" + "testing" + + dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" + "github.com/devfile/devworkspace-operator/pkg/common" + "github.com/devfile/devworkspace-operator/pkg/config" + "github.com/devfile/devworkspace-operator/pkg/constants" + "github.com/devfile/devworkspace-operator/pkg/dwerrors" + "github.com/devfile/devworkspace-operator/pkg/provision/sync" + "github.com/go-logr/logr/testr" + "github.com/stretchr/testify/assert" + networkingv1 "k8s.io/api/networking/v1" + k8sErrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +const ( + testNamespace = "test-namespace" + testDevworkspaceName = "test-devworkspace" +) + +var scheme = runtime.NewScheme() + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(v1alpha1.AddToScheme(scheme)) + utilruntime.Must(dw.AddToScheme(scheme)) +} + +func getTestClusterAPI(t *testing.T, initialObjects ...client.Object) sync.ClusterAPI { + fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initialObjects...).Build() + return sync.ClusterAPI{ + Ctx: context.Background(), + Client: fakeClient, + Scheme: scheme, + Logger: testr.New(t), + } +} + +func getTestDevWorkspaceWithConfig(name string, npConfig *v1alpha1.NetworkPolicyConfig) *common.DevWorkspaceWithConfig { + return &common.DevWorkspaceWithConfig{ + DevWorkspace: &dw.DevWorkspace{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: testNamespace, + UID: types.UID(fmt.Sprintf("uid-%s", name)), + }, + Status: dw.DevWorkspaceStatus{ + DevWorkspaceId: fmt.Sprintf("workspace%s", name), + }, + }, + Config: config.GetConfigForTesting(&v1alpha1.OperatorConfiguration{ + Workspace: &v1alpha1.WorkspaceConfig{ + NetworkPolicy: npConfig, + }, + }), + } +} + +func getEnabledNetworkPolicyConfig() *v1alpha1.NetworkPolicyConfig { + return &v1alpha1.NetworkPolicyConfig{ + Enabled: new(true), + } +} + +func getNetworkPolicyFromCluster(testDevworkspace *common.DevWorkspaceWithConfig, api sync.ClusterAPI) (*networkingv1.NetworkPolicy, error) { + actual := &networkingv1.NetworkPolicy{} + err := api.Client.Get( + api.Ctx, + types.NamespacedName{ + Name: common.NetworkPolicyName(testDevworkspace.Status.DevWorkspaceId), + Namespace: testDevworkspace.Namespace, + }, + actual) + + return actual, err +} + +func TestGeneratedPolicySelectsOnlyItsOwnWorkspacePods(t *testing.T) { + testDevWorkspace := getTestDevWorkspaceWithConfig(testDevworkspaceName, getEnabledNetworkPolicyConfig()) + policy := generateNetworkPolicy(testDevWorkspace, getEnabledNetworkPolicyConfig()) + + assert.Equal(t, common.NetworkPolicyName(testDevWorkspace.Status.DevWorkspaceId), policy.Name) + assert.Equal(t, testNamespace, policy.Namespace) + assert.Equal(t, map[string]string{ + constants.DevWorkspaceIDLabel: testDevWorkspace.Status.DevWorkspaceId, + constants.DevWorkspaceNameLabel: testDevWorkspace.Name, + }, policy.Labels) + assert.Equal(t, metav1.LabelSelector{ + MatchLabels: map[string]string{ + constants.DevWorkspaceIDLabel: testDevWorkspace.Status.DevWorkspaceId, + }, + }, policy.Spec.PodSelector) +} + +func TestUnsetIngressDeniesAllIngress(t *testing.T) { + tests := []struct { + name string + ingress []networkingv1.NetworkPolicyIngressRule + }{ + {name: "nil ingress list", ingress: nil}, + {name: "empty ingress list", ingress: []networkingv1.NetworkPolicyIngressRule{}}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + npConfig := &v1alpha1.NetworkPolicyConfig{ + Enabled: new(true), + Ingress: tt.ingress, + Egress: []networkingv1.NetworkPolicyEgressRule{{}}, + } + policy := generateNetworkPolicy(getTestDevWorkspaceWithConfig(testDevworkspaceName, npConfig), npConfig) + + assert.Equal(t, []networkingv1.PolicyType{networkingv1.PolicyTypeIngress, networkingv1.PolicyTypeEgress}, policy.Spec.PolicyTypes) + assert.Nil(t, policy.Spec.Ingress) + }) + } +} + +func TestBothDirectionsNilDeniesAllTraffic(t *testing.T) { + npConfig := &v1alpha1.NetworkPolicyConfig{Enabled: new(true)} + policy := generateNetworkPolicy(getTestDevWorkspaceWithConfig(testDevworkspaceName, npConfig), npConfig) + + assert.NotNil(t, policy) + assert.Equal(t, []networkingv1.PolicyType{networkingv1.PolicyTypeIngress, networkingv1.PolicyTypeEgress}, policy.Spec.PolicyTypes) + assert.Nil(t, policy.Spec.Ingress) + assert.Nil(t, policy.Spec.Egress) +} + +func TestSyncCreatesPolicyWhenEnabled(t *testing.T) { + testDevworkspace := getTestDevWorkspaceWithConfig(testDevworkspaceName, getEnabledNetworkPolicyConfig()) + api := getTestClusterAPI(t, testDevworkspace.DevWorkspace) + + err := SyncNetworkPolicy(testDevworkspace, api) + retryErr := &dwerrors.RetryError{} + assert.Error(t, err) + assert.ErrorAs(t, err, &retryErr) + + err = SyncNetworkPolicy(testDevworkspace, api) + assert.NoError(t, err) + + policy, err := getNetworkPolicyFromCluster(testDevworkspace, api) + assert.NoError(t, err) + + assert.Equal(t, testDevworkspace.Name, policy.OwnerReferences[0].Name) + assert.Equal(t, testDevworkspace.UID, policy.OwnerReferences[0].UID) + assert.Equal(t, "DevWorkspace", policy.OwnerReferences[0].Kind) + assert.True(t, ptr.Deref(policy.OwnerReferences[0].Controller, false)) +} + +func TestSyncDeletesPolicyWhenDisabled(t *testing.T) { + testDevworkspace := getTestDevWorkspaceWithConfig(testDevworkspaceName, getEnabledNetworkPolicyConfig()) + api := getTestClusterAPI(t, testDevworkspace.DevWorkspace) + + err := SyncNetworkPolicy(testDevworkspace, api) + assert.Error(t, err) + + err = SyncNetworkPolicy(testDevworkspace, api) + assert.NoError(t, err) + + _, err = getNetworkPolicyFromCluster(testDevworkspace, api) + assert.NoError(t, err) + + testDevworkspace.Config.Workspace.NetworkPolicy.Enabled = new(false) + err = SyncNetworkPolicy(testDevworkspace, api) + assert.NoError(t, err) + + _, err = getNetworkPolicyFromCluster(testDevworkspace, api) + assert.True(t, k8sErrors.IsNotFound(err)) +} + +func TestSyncIsTolerantOfMissingPolicyWhenDisabled(t *testing.T) { + npConfig := getEnabledNetworkPolicyConfig() + npConfig.Enabled = ptr.To(false) + testdw := getTestDevWorkspaceWithConfig("test-devworkspace", npConfig) + api := getTestClusterAPI(t, testdw.DevWorkspace) + err := SyncNetworkPolicy(testdw, api) + assert.NoError(t, err) +}