This is an automated email from the ASF dual-hosted git repository.
squakez pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git
The following commit(s) were added to refs/heads/main by this push:
new 0dd10d156 Feature/6859 network policy clean (#6866)
0dd10d156 is described below
commit 0dd10d156c6282ac3a55cdfddc55603b53a4d09a
Author: Sanjeev Kumar <[email protected]>
AuthorDate: Tue Oct 13 18:56:37 2026 +0530
Feature/6859 network policy clean (#6866)
* docs(installation): explain multi-namespace RBAC setup
* feat(service): add optional ingress NetworkPolicy
* feat(service): add optional ingress NetworkPolicy
* feat(service): add optional ingress NetworkPolicy
* feat(service): add optional ingress NetworkPolicy
* feat(service): add optional ingress NetworkPolicy
* feat(service): add optional ingress NetworkPolicy
* feat(service): add optional ingress NetworkPolicy
* feat(service): add optional ingress NetworkPolicy
* feat(service): add optional ingress NetworkPolicy
* feat(service): add optional ingress NetworkPolicy
* feat(service): add optional ingress NetworkPolicy
* feat(service): add optional ingress NetworkPolicy
* feat(service): add optional ingress NetworkPolicy
* feat(service): add optional ingress NetworkPolicy
* feat(service): add optional ingress NetworkPolicy
* test(e2e): retain NetworkPolicy diagnostics
* ci: use stable artifact uploader
* fix(ci): correct NetworkPolicy E2E test invocation
* chore: remove unrelated installation documentation change
* ci: fix network policy e2e workflow
* ci: run network policy workflow on every PR
* Fix #6859: address Service NetworkPolicy review feedback
* Fix #6859: preserve Makefile link in test target
* Fix #6859: keep Makefile change limited to test target
* Fix #6859: restore exact Makefile behavior
* Fix #6859: preserve Makefile variable escaping
* Fix #6859: address remaining NetworkPolicy review feedback
* ci: align NetworkPolicy workflow triggers
* test: follow shared NetworkPolicy test target pattern
* fix: use canonical license header for NetworkPolicy consumer
* test: add NetworkPolicy server fixture
* fix: preserve shell failure counter expansion
* fix: preserve shell failure counter expansion
* fix: make NetworkPolicy test failure propagation explicit
* style: separate workflow triggers from permissions
* ci: remove unnecessary GitHub token from network policy workflow
---
.github/actions/infra-setting/action.yml | 7 ++
.github/workflows/network-policy.yml | 84 ++++++++++++++++
docs/modules/ROOT/partials/apis/camel-k-crds.adoc | 18 ++++
docs/modules/traits/pages/service.adoc | 27 +++++
e2e/networkpolicy/files/NetworkPolicyConsumer.java | 37 +++++++
e2e/networkpolicy/files/PlatformHttpServer.java | 26 +++++
e2e/networkpolicy/network_policy_test.go | 110 +++++++++++++++++++++
helm/camel-k/templates/rbacs-descoped.yaml | 11 +++
helm/camel-k/templates/rbacs-namespaced.yaml | 11 +++
pkg/apis/camel/v1/trait/service.go | 7 ++
pkg/apis/camel/v1/trait/zz_generated.deepcopy.go | 19 ++++
.../camel.apache.org_integrationplatforms.yaml | 15 +++
.../camel.apache.org_integrationprofiles.yaml | 15 +++
.../crd/bases/camel.apache.org_integrations.yaml | 15 +++
.../rbac/descoped/operator-cluster-role.yaml | 11 +++
.../config/rbac/namespaced/operator-role.yaml | 11 +++
pkg/trait/service.go | 58 +++++++++++
pkg/trait/service_test.go | 95 ++++++++++++++++++
script/Makefile | 8 ++
19 files changed, 585 insertions(+)
diff --git a/.github/actions/infra-setting/action.yml
b/.github/actions/infra-setting/action.yml
index 79be5461b..14117c0cc 100644
--- a/.github/actions/infra-setting/action.yml
+++ b/.github/actions/infra-setting/action.yml
@@ -18,6 +18,12 @@
name: infra-setting
description: 'Setting CI infrastructure'
+inputs:
+ cni:
+ description: 'CNI plug-in to use for Minikube'
+ required: false
+ default: ''
+
runs:
using: "composite"
@@ -39,6 +45,7 @@ runs:
cpus: max
memory: max
container-runtime: docker
+ cni: ${{ inputs.cni }}
- id: minikube-settings
name: Set docker-env
diff --git a/.github/workflows/network-policy.yml
b/.github/workflows/network-policy.yml
new file mode 100644
index 000000000..f0b095e18
--- /dev/null
+++ b/.github/workflows/network-policy.yml
@@ -0,0 +1,84 @@
+# ---------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+# ---------------------------------------------------------------------------
+
+name: network-policy
+
+on:
+ pull_request:
+ branches:
+ - main
+ - "release-*"
+ paths-ignore:
+ - 'docs/**'
+ - 'java/**'
+ - 'proposals/**'
+ - '**.adoc'
+ - '**.md'
+ - 'KEYS'
+ - 'LICENSE'
+ - 'NOTICE'
+ push:
+ branches:
+ - main
+ - "release-*"
+ paths-ignore:
+ - 'docs/**'
+ - 'java/**'
+ - 'proposals/**'
+ - '**.adoc'
+ - '**.md'
+ - 'KEYS'
+ - 'LICENSE'
+ - 'NOTICE'
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+env:
+ CAMEL_K_TEST_TIMEOUT_SHORT: 2m
+ CAMEL_K_TEST_TIMEOUT_MEDIUM: 5m
+ CAMEL_K_TEST_TIMEOUT_LONG: 10m
+
+jobs:
+ network-policy:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v7
+ with:
+ persist-credentials: false
+ submodules: recursive
+
+ - name: Infra setting
+ uses: ./.github/actions/infra-setting
+ with:
+ cni: calico
+
+ - name: Install operator
+ shell: bash
+ run: |
+ kubectl create ns camel-k
+ kubectl create configmap camel-k-operator-configmap-configuration \
+
--from-literal=MAVEN_REPOSITORIES_ALLOWED="https://maven.repository.redhat.com/ga@id=redhat"
\
+ -n camel-k
+ kubectl apply -k install/overlays/all-namespaces/ --server-side
--force-conflicts
+ kubectl wait --for=condition=available deployment/camel-k-operator
-n camel-k --timeout=60s
+
+ - name: Run NetworkPolicy E2E test
+ shell: bash
+ run: make test-network-policy
diff --git a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
index a9ff2589e..a73dd0f2b 100644
--- a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
+++ b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
@@ -9725,6 +9725,24 @@ map[string]string
The labels added to the Service object.
+|`networkPolicyEnabled` +
+bool
+|
+
+Enables the NetworkPolicy managed by the Service trait. Defaults to false.
When enabled, at least one of the namespace or pod selectors must be configured.
+
+|`networkPolicyNamespaceSelector` +
+map[string]string
+|
+
+Labels used to restrict the source namespaces allowed by the NetworkPolicy.
+
+|`networkPolicyPodSelector` +
+map[string]string
+|
+
+Labels used to restrict the source pods allowed by the NetworkPolicy.
+
|`ports` +
[]string
|
diff --git a/docs/modules/traits/pages/service.adoc
b/docs/modules/traits/pages/service.adoc
old mode 100755
new mode 100644
index 0bdc83e48..bfda7c2bf
--- a/docs/modules/traits/pages/service.adoc
+++ b/docs/modules/traits/pages/service.adoc
@@ -54,6 +54,18 @@ Deprecated: Use service type instead.
| map[string]string
| The labels added to the Service object.
+| service.networkPolicyEnabled
+| bool
+| Enables the NetworkPolicy managed by the Service trait. Defaults to false.
When enabled, at least one of the namespace or pod selectors must be configured.
+
+| service.networkPolicyNamespaceSelector
+| map[string]string
+| Labels used to restrict the source namespaces allowed by the NetworkPolicy.
+
+| service.networkPolicyPodSelector
+| map[string]string
+| Labels used to restrict the source pods allowed by the NetworkPolicy.
+
| service.ports
| []string
| List of container ports available in the container to expose
@@ -80,3 +92,18 @@ $ kamel run PlatformHttpServer.java -p
quarkus.http.port=8085 -t container.ports
+
[source,console]
$ kamel run MyUDPServer.java -t container.ports=udp;95;UDP -t
service.ports=udp;95;9095;UDP
+
+* Restrict a Service with an ingress NetworkPolicy. The example allows
requests only from pods labeled app: my-client in namespaces labeled
kubernetes.io/metadata.name: backend:
++
+[source,yaml]
+----
+spec:
+ traits:
+ service:
+ enabled: true
+ network-policy-enabled: true
+ network-policy-namespace-selector:
+ kubernetes.io/metadata.name: backend
+ network-policy-pod-selector:
+ app: my-client
+----
diff --git a/e2e/networkpolicy/files/NetworkPolicyConsumer.java
b/e2e/networkpolicy/files/NetworkPolicyConsumer.java
new file mode 100644
index 000000000..d375bd24a
--- /dev/null
+++ b/e2e/networkpolicy/files/NetworkPolicyConsumer.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+
+public class NetworkPolicyConsumer extends RouteBuilder {
+ @Override
+ public void configure() {
+ onException(Exception.class)
+ .maximumRedeliveries(0)
+ .handled(true)
+ .log("ACCESS_DENIED");
+
+ from("timer:network-policy?delay=1000&period=1000&repeatCount=30")
+
.to("http://{{serviceName}}/hello?throwExceptionOnFailure=false&connectTimeout=3000&responseTimeout=3000")
+ .choice()
+ .when(header(Exchange.HTTP_RESPONSE_CODE).isEqualTo(200))
+ .log("ACCESS_GRANTED")
+ .otherwise()
+ .log("ACCESS_DENIED");
+ }
+}
diff --git a/e2e/networkpolicy/files/PlatformHttpServer.java
b/e2e/networkpolicy/files/PlatformHttpServer.java
new file mode 100644
index 000000000..31d8d19f3
--- /dev/null
+++ b/e2e/networkpolicy/files/PlatformHttpServer.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+import org.apache.camel.builder.RouteBuilder;
+
+public class PlatformHttpServer extends RouteBuilder {
+ @Override
+ public void configure() throws Exception {
+ from("platform-http:/hello?httpMethodRestrict=GET")
+ .setBody(simple("Hello ${header.name}"));
+ }
+}
diff --git a/e2e/networkpolicy/network_policy_test.go
b/e2e/networkpolicy/network_policy_test.go
new file mode 100644
index 000000000..e72a35908
--- /dev/null
+++ b/e2e/networkpolicy/network_policy_test.go
@@ -0,0 +1,110 @@
+//go:build integration
+// +build integration
+
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You 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"
+ "testing"
+
+ . "github.com/onsi/gomega"
+
+ 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"
+ ctrl "sigs.k8s.io/controller-runtime/pkg/client"
+
+ . "github.com/apache/camel-k/v2/e2e/support"
+ v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
+)
+
+func TestServiceNetworkPolicy(t *testing.T) {
+ WithNewTestNamespace(t, func(ctx context.Context, g *WithT, ns string) {
+ namespace, err := TestClient(t).CoreV1().Namespaces().Get(ctx,
ns, metav1.GetOptions{})
+ g.Expect(err).ToNot(HaveOccurred())
+
+ labels := namespace.Labels
+ if labels == nil {
+ labels = map[string]string{}
+ }
+ labels["network-policy-test"] = "enabled"
+ namespace.Labels = labels
+ _, err = TestClient(t).CoreV1().Namespaces().Update(ctx,
namespace, metav1.UpdateOptions{})
+ g.Expect(err).ToNot(HaveOccurred())
+
+ serverName := RandomizedSuffixName("network-policy-server")
+ g.Expect(KamelRun(t, ctx, ns, "files/PlatformHttpServer.java",
+ "-t", "service.enabled=true",
+ "-t", "service.network-policy-enabled=true",
+ "-t",
"service.network-policy-namespace-selector.'network-policy-test'=enabled",
+ "-t", "service.network-policy-pod-selector.app=allowed",
+ "--name", serverName,
+ ).Execute()).To(Succeed())
+
+ g.Eventually(IntegrationConditionStatus(t, ctx, ns, serverName,
v1.IntegrationConditionReady), TestTimeoutMedium).
+ Should(Equal(corev1.ConditionTrue))
+ g.Eventually(NetworkPolicyByName(t, ctx, ns, serverName),
TestTimeoutShort).ShouldNot(BeNil())
+
+ allowedName := RandomizedSuffixName("network-policy-allowed")
+ deniedName := RandomizedSuffixName("network-policy-denied")
+
+ g.Expect(KamelRun(t, ctx, ns,
"files/NetworkPolicyConsumer.java",
+ "-p", "serviceName="+serverName,
+ "--label", "app=allowed",
+ "-t", "owner.target-labels=*",
+ "--name", allowedName,
+ ).Execute()).To(Succeed())
+
+ g.Expect(KamelRun(t, ctx, ns,
"files/NetworkPolicyConsumer.java",
+ "-p", "serviceName="+serverName,
+ "--label", "app=denied",
+ "-t", "owner.target-labels=*",
+ "--name", deniedName,
+ ).Execute()).To(Succeed())
+
+ g.Eventually(IntegrationConditionStatus(t, ctx, ns,
allowedName, v1.IntegrationConditionReady),
TestTimeoutMedium).Should(Equal(corev1.ConditionTrue))
+ g.Eventually(IntegrationConditionStatus(t, ctx, ns, deniedName,
v1.IntegrationConditionReady),
TestTimeoutMedium).Should(Equal(corev1.ConditionTrue))
+
+ allowedPod := IntegrationPod(t, ctx, ns, allowedName)()
+ deniedPod := IntegrationPod(t, ctx, ns, deniedName)()
+ g.Expect(allowedPod.GetLabels()["app"]).To(Equal("allowed"))
+ g.Expect(deniedPod.GetLabels()["app"]).To(Equal("denied"))
+
+ g.Eventually(IntegrationLogs(t, ctx, ns, allowedName),
TestTimeoutMedium).
+ Should(ContainSubstring("ACCESS_GRANTED"))
+ g.Eventually(IntegrationLogs(t, ctx, ns, deniedName),
TestTimeoutMedium).
+ Should(ContainSubstring("ACCESS_DENIED"))
+ })
+}
+
+func NetworkPolicyByName(t *testing.T, ctx context.Context, namespace, name
string) func() *networkingv1.NetworkPolicy {
+ return func() *networkingv1.NetworkPolicy {
+ policy := &networkingv1.NetworkPolicy{}
+ err := TestClient(t).Get(ctx, ctrl.ObjectKey{Name: name,
Namespace: namespace}, policy)
+ if err != nil {
+ if !k8serrors.IsNotFound(err) {
+ t.Logf("failed to read NetworkPolicy %s/%s:
%v", namespace, name, err)
+ }
+ return nil
+ }
+ return policy
+ }
+}
diff --git a/helm/camel-k/templates/rbacs-descoped.yaml
b/helm/camel-k/templates/rbacs-descoped.yaml
index 255a5d0d4..2a57524c2 100644
--- a/helm/camel-k/templates/rbacs-descoped.yaml
+++ b/helm/camel-k/templates/rbacs-descoped.yaml
@@ -176,6 +176,17 @@ rules:
- get
- list
- patch
+- apiGroups:
+ - networking.k8s.io
+ resources:
+ - networkpolicies
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - watch
- apiGroups:
- gateway.networking.k8s.io
resources:
diff --git a/helm/camel-k/templates/rbacs-namespaced.yaml
b/helm/camel-k/templates/rbacs-namespaced.yaml
index 5465347ad..2b3bbbb9c 100644
--- a/helm/camel-k/templates/rbacs-namespaced.yaml
+++ b/helm/camel-k/templates/rbacs-namespaced.yaml
@@ -160,6 +160,17 @@ rules:
- get
- list
- patch
+- apiGroups:
+ - networking.k8s.io
+ resources:
+ - networkpolicies
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - watch
- apiGroups:
- gateway.networking.k8s.io
resources:
diff --git a/pkg/apis/camel/v1/trait/service.go
b/pkg/apis/camel/v1/trait/service.go
index ca3b58509..f9d4f655a 100644
--- a/pkg/apis/camel/v1/trait/service.go
+++ b/pkg/apis/camel/v1/trait/service.go
@@ -43,6 +43,13 @@ type ServiceTrait struct {
Annotations map[string]string `json:"annotations,omitempty"
property:"annotations"`
// The labels added to the Service object.
Labels map[string]string `json:"labels,omitempty" property:"labels"`
+ // Enables the NetworkPolicy managed by the Service trait. Defaults to
false.
+ // When enabled, at least one of the namespace or pod selectors must be
configured.
+ NetworkPolicyEnabled *bool `json:"networkPolicyEnabled,omitempty"
property:"network-policy-enabled"`
+ // Labels used to restrict the source namespaces allowed by the
NetworkPolicy.
+ NetworkPolicyNamespaceSelector map[string]string
`json:"networkPolicyNamespaceSelector,omitempty"
property:"network-policy-namespace-selector"`
+ // Labels used to restrict the source pods allowed by the NetworkPolicy.
+ NetworkPolicyPodSelector map[string]string
`json:"networkPolicyPodSelector,omitempty"
property:"network-policy-pod-selector"`
// List of container ports available in the container to expose
// (syntax:
<port-name>;<port-number>;<container-port-number>[;<port-protocol]).
// When omitted, `port-protocol` (admitted values `TCP`, `UDP` or
`SCTP`) is `TCP`.
diff --git a/pkg/apis/camel/v1/trait/zz_generated.deepcopy.go
b/pkg/apis/camel/v1/trait/zz_generated.deepcopy.go
index 5466a9cb0..20bf1e321 100644
--- a/pkg/apis/camel/v1/trait/zz_generated.deepcopy.go
+++ b/pkg/apis/camel/v1/trait/zz_generated.deepcopy.go
@@ -1475,6 +1475,25 @@ func (in *ServiceTrait) DeepCopyInto(out *ServiceTrait) {
(*out)[key] = val
}
}
+ if in.NetworkPolicyEnabled != nil {
+ in, out := &in.NetworkPolicyEnabled, &out.NetworkPolicyEnabled
+ *out = new(bool)
+ **out = **in
+ }
+ if in.NetworkPolicyNamespaceSelector != nil {
+ in, out := &in.NetworkPolicyNamespaceSelector,
&out.NetworkPolicyNamespaceSelector
+ *out = make(map[string]string, len(*in))
+ for key, val := range *in {
+ (*out)[key] = val
+ }
+ }
+ if in.NetworkPolicyPodSelector != nil {
+ in, out := &in.NetworkPolicyPodSelector,
&out.NetworkPolicyPodSelector
+ *out = make(map[string]string, len(*in))
+ for key, val := range *in {
+ (*out)[key] = val
+ }
+ }
if in.Ports != nil {
in, out := &in.Ports, &out.Ports
*out = make([]string, len(*in))
diff --git
a/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml
b/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml
index 2b1454c90..ce3b54181 100644
--- a/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml
+++ b/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml
@@ -2496,6 +2496,21 @@ spec:
Deprecated: Use service type instead.
type: boolean
+ networkPolicyEnabled:
+ description: |-
+ Enables the NetworkPolicy managed by the Service
trait. Defaults to false.
+ When enabled, at least one of the namespace or pod
selectors must be configured.
+ type: boolean
+ networkPolicyNamespaceSelector:
+ additionalProperties:
+ type: string
+ description: Labels used to restrict the source
namespaces allowed by the NetworkPolicy.
+ type: object
+ networkPolicyPodSelector:
+ additionalProperties:
+ type: string
+ description: Labels used to restrict the source pods
allowed by the NetworkPolicy.
+ type: object
ports:
description: |-
List of container ports available in the container
to expose
diff --git
a/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml
b/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml
index 6a77205b6..de3440185 100644
--- a/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml
+++ b/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml
@@ -2421,6 +2421,21 @@ spec:
Deprecated: Use service type instead.
type: boolean
+ networkPolicyEnabled:
+ description: |-
+ Enables the NetworkPolicy managed by the Service
trait. Defaults to false.
+ When enabled, at least one of the namespace or pod
selectors must be configured.
+ type: boolean
+ networkPolicyNamespaceSelector:
+ additionalProperties:
+ type: string
+ description: Labels used to restrict the source
namespaces allowed by the NetworkPolicy.
+ type: object
+ networkPolicyPodSelector:
+ additionalProperties:
+ type: string
+ description: Labels used to restrict the source pods
allowed by the NetworkPolicy.
+ type: object
ports:
description: |-
List of container ports available in the container
to expose
diff --git a/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml
b/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml
index 3d495a82d..35b4d8f18 100644
--- a/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml
+++ b/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml
@@ -9474,6 +9474,21 @@ spec:
Deprecated: Use service type instead.
type: boolean
+ networkPolicyEnabled:
+ description: |-
+ Enables the NetworkPolicy managed by the Service
trait. Defaults to false.
+ When enabled, at least one of the namespace or pod
selectors must be configured.
+ type: boolean
+ networkPolicyNamespaceSelector:
+ additionalProperties:
+ type: string
+ description: Labels used to restrict the source
namespaces allowed by the NetworkPolicy.
+ type: object
+ networkPolicyPodSelector:
+ additionalProperties:
+ type: string
+ description: Labels used to restrict the source pods
allowed by the NetworkPolicy.
+ type: object
ports:
description: |-
List of container ports available in the container
to expose
diff --git a/pkg/resources/config/rbac/descoped/operator-cluster-role.yaml
b/pkg/resources/config/rbac/descoped/operator-cluster-role.yaml
index f5db9280e..a21bef406 100644
--- a/pkg/resources/config/rbac/descoped/operator-cluster-role.yaml
+++ b/pkg/resources/config/rbac/descoped/operator-cluster-role.yaml
@@ -182,6 +182,17 @@ rules:
- get
- list
- patch
+- apiGroups:
+ - networking.k8s.io
+ resources:
+ - networkpolicies
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - watch
# Required by gateway trait
- apiGroups:
- gateway.networking.k8s.io
diff --git a/pkg/resources/config/rbac/namespaced/operator-role.yaml
b/pkg/resources/config/rbac/namespaced/operator-role.yaml
index 9071708f7..0999ba5b0 100644
--- a/pkg/resources/config/rbac/namespaced/operator-role.yaml
+++ b/pkg/resources/config/rbac/namespaced/operator-role.yaml
@@ -163,6 +163,17 @@ rules:
- get
- list
- patch
+- apiGroups:
+ - networking.k8s.io
+ resources:
+ - networkpolicies
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - watch
# Required by gateway trait
- apiGroups:
- gateway.networking.k8s.io
diff --git a/pkg/trait/service.go b/pkg/trait/service.go
index 779c3fa6e..110740a47 100644
--- a/pkg/trait/service.go
+++ b/pkg/trait/service.go
@@ -18,12 +18,15 @@ limitations under the License.
package trait
import (
+ "errors"
"fmt"
"maps"
"strconv"
"strings"
corev1 "k8s.io/api/core/v1"
+ networkingv1 "k8s.io/api/networking/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/utils/ptr"
@@ -139,11 +142,66 @@ func (t *serviceTrait) Apply(e *Environment) error {
}
svc.Spec.Type = serviceType
}
+ var networkPolicy *networkingv1.NetworkPolicy
+ if ptr.Deref(t.NetworkPolicyEnabled, false) {
+ var err error
+ networkPolicy, err = t.getNetworkPolicyFor(e.Integration.Name,
e.Integration.Namespace)
+ if err != nil {
+ return err
+ }
+ }
+
e.Resources.Add(svc)
+ if networkPolicy != nil {
+ e.Resources.Add(networkPolicy)
+ }
return nil
}
+func (t *serviceTrait) getNetworkPolicyFor(itName, itNamespace string)
(*networkingv1.NetworkPolicy, error) {
+ if len(t.NetworkPolicyNamespaceSelector) == 0 &&
len(t.NetworkPolicyPodSelector) == 0 {
+ return nil, errors.New("network policy is enabled but no
namespace or pod selector is configured")
+ }
+
+ from := networkingv1.NetworkPolicyPeer{}
+ if len(t.NetworkPolicyNamespaceSelector) > 0 {
+ from.NamespaceSelector = &metav1.LabelSelector{
+ MatchLabels:
maps.Clone(t.NetworkPolicyNamespaceSelector),
+ }
+ }
+ if len(t.NetworkPolicyPodSelector) > 0 {
+ from.PodSelector = &metav1.LabelSelector{
+ MatchLabels: maps.Clone(t.NetworkPolicyPodSelector),
+ }
+ }
+
+ policy := &networkingv1.NetworkPolicy{
+ Name: itName,
+ Namespace: itNamespace,
+ Labels: map[string]string{
+ v1.IntegrationLabel: itName,
+ },
+ Kind: "NetworkPolicy",
+ APIVersion: networkingv1.SchemeGroupVersion.String(),
+ Spec: networkingv1.NetworkPolicySpec{
+ PodSelector: metav1.LabelSelector{
+ MatchLabels: map[string]string{
+ v1.IntegrationLabel: itName,
+ },
+ },
+ PolicyTypes: []networkingv1.PolicyType{
+ networkingv1.PolicyTypeIngress,
+ },
+ Ingress: []networkingv1.NetworkPolicyIngressRule{
+ {From: []networkingv1.NetworkPolicyPeer{from}},
+ },
+ },
+ }
+
+ return policy, nil
+}
+
func (t *serviceTrait) getServiceFor(itName, itNamespace string)
*corev1.Service {
labels := map[string]string{
v1.IntegrationLabel: itName,
diff --git a/pkg/trait/service_test.go b/pkg/trait/service_test.go
index 74ad9b5e0..139b42bcd 100644
--- a/pkg/trait/service_test.go
+++ b/pkg/trait/service_test.go
@@ -25,7 +25,9 @@ import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
+ networkingv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/utils/ptr"
@@ -791,3 +793,96 @@ func TestServicePorts(t *testing.T) {
Protocol: corev1.ProtocolUDP,
})
}
+
+func TestServiceNetworkPolicyDisabledByDefault(t *testing.T) {
+ trait := &serviceTrait{
+ ServiceTrait: traitv1.ServiceTrait{
+ Trait: traitv1.Trait{Enabled: ptr.To(true)},
+ Auto: ptr.To(false),
+ },
+ }
+
+ environment := Environment{
+ Integration: &v1.Integration{
+ ObjectMeta: metav1.ObjectMeta{Name: ServiceTestName,
Namespace: ServiceTestNamespace},
+ },
+ Resources: kubernetes.NewCollection(),
+ }
+
+ require.NoError(t, trait.Apply(&environment))
+
+ assert.Nil(t, findNetworkPolicy(environment.Resources))
+}
+
+func TestServiceNetworkPolicyUsesCombinedSelectors(t *testing.T) {
+ trait := &serviceTrait{
+ ServiceTrait: traitv1.ServiceTrait{
+ Trait: traitv1.Trait{Enabled: ptr.To(true)},
+ Auto: ptr.To(false),
+ NetworkPolicyEnabled: ptr.To(true),
+ NetworkPolicyNamespaceSelector: map[string]string{
+ "kubernetes.io/metadata.name":
ServiceTestNamespace,
+ "environment": "backend",
+ },
+ NetworkPolicyPodSelector: map[string]string{
+ "app": "client",
+ "tier": "trusted",
+ },
+ },
+ }
+
+ environment := Environment{
+ Integration: &v1.Integration{
+ ObjectMeta: metav1.ObjectMeta{Name: ServiceTestName,
Namespace: ServiceTestNamespace},
+ },
+ Resources: kubernetes.NewCollection(),
+ }
+
+ require.NoError(t, trait.Apply(&environment))
+
+ networkPolicy := findNetworkPolicy(environment.Resources)
+ require.NotNil(t, networkPolicy)
+ assert.Equal(t, ServiceTestName, networkPolicy.Name)
+ assert.Equal(t, ServiceTestNamespace, networkPolicy.Namespace)
+ assert.Equal(t, map[string]string{v1.IntegrationLabel:
ServiceTestName}, networkPolicy.Spec.PodSelector.MatchLabels)
+ assert.Equal(t,
[]networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
networkPolicy.Spec.PolicyTypes)
+ require.Len(t, networkPolicy.Spec.Ingress, 1)
+ require.Len(t, networkPolicy.Spec.Ingress[0].From, 1)
+
+ peer := networkPolicy.Spec.Ingress[0].From[0]
+ assert.Equal(t, trait.NetworkPolicyNamespaceSelector,
peer.NamespaceSelector.MatchLabels)
+ assert.Equal(t, trait.NetworkPolicyPodSelector,
peer.PodSelector.MatchLabels)
+}
+
+func TestServiceNetworkPolicyRequiresSelector(t *testing.T) {
+ trait := &serviceTrait{
+ ServiceTrait: traitv1.ServiceTrait{
+ Trait: traitv1.Trait{Enabled: ptr.To(true)},
+ Auto: ptr.To(false),
+ NetworkPolicyEnabled: ptr.To(true),
+ },
+ }
+
+ environment := Environment{
+ Integration: &v1.Integration{
+ ObjectMeta: metav1.ObjectMeta{Name: ServiceTestName,
Namespace: ServiceTestNamespace},
+ },
+ Resources: kubernetes.NewCollection(),
+ }
+
+ err := trait.Apply(&environment)
+ require.Error(t, err)
+ assert.Equal(t, "network policy is enabled but no namespace or pod
selector is configured", err.Error())
+ assert.Nil(t,
environment.Resources.GetServiceForIntegration(environment.Integration))
+}
+
+func findNetworkPolicy(resources *kubernetes.Collection)
*networkingv1.NetworkPolicy {
+ var result *networkingv1.NetworkPolicy
+ resources.Visit(func(resource runtime.Object) {
+ if networkPolicy, ok := resource.(*networkingv1.NetworkPolicy);
ok {
+ result = networkPolicy
+ }
+ })
+ return result
+}
+
diff --git a/script/Makefile b/script/Makefile
index 4ac1ec8c9..5a9db1ca6 100644
--- a/script/Makefile
+++ b/script/Makefile
@@ -277,6 +277,14 @@ test-common: gotestfmt-install
go test -p $(TEST_COMMON_PARALLEL_COUNT) -timeout 90m -v
./e2e/common/... --parallel 1 -tags=integration $(GOTESTFMT) || ((FAILED++)); \
exit $${FAILED}
+#
+# NetworkPolicy test that requires a Calico-backed Minikube cluster
+#
+test-network-policy: gotestfmt-install
+ FAILED=0; \
+ go test -p $(TEST_COMMON_PARALLEL_COUNT) -timeout 20m -v
./e2e/networkpolicy/... --parallel 1 -tags=integration $(GOTESTFMT) ||
((FAILED++)); \
+ if ((FAILED)); then exit 1; fi
+
#
# Smoke tests are a subset of common tests used to quickly verify the operator
#