This is an automated email from the ASF dual-hosted git repository.

dongjoon-hyun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/spark-kubernetes-operator.git


The following commit(s) were added to refs/heads/main by this push:
     new cac534c  [SPARK-57934] Support opt-in `NetworkPolicy` for operator pod 
in Helm chart
cac534c is described below

commit cac534c62797a30aa4183b0838fa5a99a459bc6e
Author: Dongjoon Hyun <[email protected]>
AuthorDate: Sun Jul 5 12:16:59 2026 -0700

    [SPARK-57934] Support opt-in `NetworkPolicy` for operator pod in Helm chart
    
    ### What changes were proposed in this pull request?
    
    This PR aims to support an opt-in `NetworkPolicy` for the operator pod in 
the Helm chart.
    
    When `operatorDeployment.networkPolicy.enable` is `true` (default: 
`false`), all ingress to the
    operator pod is denied except the health probe port (open to any source, 
for kubelet probes) and
    the metrics port, which is reachable only from the peers listed in
    `operatorDeployment.networkPolicy.metricsIngress`.
    
    ```yaml
    operatorDeployment:
      networkPolicy:
        enable: true
        metricsIngress:
          - namespaceSelector:
              matchLabels:
                kubernetes.io/metadata.name: "monitoring"
    ```
    
    ### Why are the changes needed?
    
    The operator serves unauthenticated plain-HTTP probe and metrics endpoints, 
and the chart
    provided no way to restrict in-cluster access to them.
    
    ### Does this PR introduce _any_ user-facing change?
    
    No, because this is disabled by default.
    
    ### How was this patch tested?
    
    Pass the CIs.
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Fable 5
    
    Closes #742 from dongjoon-hyun/SPARK-57934.
    
    Authored-by: Dongjoon Hyun <[email protected]>
    Signed-off-by: Dongjoon Hyun <[email protected]>
---
 .github/workflows/build_and_test.yml               |  1 +
 .../templates/network-policy.yaml                  | 44 +++++++++++
 .../templates/tests/test-network-policy.yaml       | 85 ++++++++++++++++++++++
 .../spark-kubernetes-operator/values.schema.json   | 21 ++++++
 .../helm/spark-kubernetes-operator/values.yaml     | 13 ++++
 docs/operations.md                                 | 31 ++++++++
 .../helm-test-values/network-policy/values.yaml    | 28 +++++++
 7 files changed, 223 insertions(+)

diff --git a/.github/workflows/build_and_test.yml 
b/.github/workflows/build_and_test.yml
index 3a08c1e..9f7be94 100644
--- a/.github/workflows/build_and_test.yml
+++ b/.github/workflows/build_and_test.yml
@@ -233,6 +233,7 @@ jobs:
           - "1.36.0"
         test-group:
           - configmap-metadata
+          - network-policy
     steps:
       - name: Checkout repository
         uses: actions/checkout@v6
diff --git 
a/build-tools/helm/spark-kubernetes-operator/templates/network-policy.yaml 
b/build-tools/helm/spark-kubernetes-operator/templates/network-policy.yaml
new file mode 100644
index 0000000..cf557ca
--- /dev/null
+++ b/build-tools/helm/spark-kubernetes-operator/templates/network-policy.yaml
@@ -0,0 +1,44 @@
+# 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.
+
+{{- if .Values.operatorDeployment.networkPolicy.enable }}
+---
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: {{ include "spark-operator.name" . }}
+  namespace: {{ .Release.Namespace }}
+  labels:
+    {{- include "spark-operator.commonLabels" . | nindent 4 }}
+spec:
+  podSelector:
+    matchLabels:
+      {{- include "spark-operator.deploymentSelectorLabels" . | nindent 6 }}
+  policyTypes:
+    - Ingress
+  ingress:
+    # Health probe port must stay reachable by the kubelet, whose traffic
+    # originates from the node and cannot be selected by pod/namespace 
selectors.
+    - ports:
+        - port: {{ include "spark-operator.probePort" . }}
+          protocol: TCP
+    {{- with .Values.operatorDeployment.networkPolicy.metricsIngress }}
+    - from:
+        {{- toYaml . | nindent 8 }}
+      ports:
+        - port: {{ include "spark-operator.metricsPort" $ }}
+          protocol: TCP
+    {{- end }}
+{{- end }}
diff --git 
a/build-tools/helm/spark-kubernetes-operator/templates/tests/test-network-policy.yaml
 
b/build-tools/helm/spark-kubernetes-operator/templates/tests/test-network-policy.yaml
new file mode 100644
index 0000000..9ba24b8
--- /dev/null
+++ 
b/build-tools/helm/spark-kubernetes-operator/templates/tests/test-network-policy.yaml
@@ -0,0 +1,85 @@
+# 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.
+
+{{- if .Values.operatorDeployment.networkPolicy.enable }}
+apiVersion: v1
+kind: Pod
+metadata:
+  name: "{{ include "spark-operator.name" . }}-test-network-policy"
+  namespace: {{ .Release.Namespace }}
+  labels:
+    {{- include "spark-operator.commonLabels" . | nindent 4 }}
+  annotations:
+    "helm.sh/hook": test
+    "helm.sh/hook-delete-policy": before-hook-creation
+spec:
+  containers:
+    - name: kubectl
+      image: bitnamisecure/kubectl:latest
+      command: ['bash', '-c']
+      args:
+        - |
+          set -e
+          echo "Testing operator NetworkPolicy..."
+
+          NP_JSON=$(kubectl get networkpolicy {{ include "spark-operator.name" 
. }} -n {{ .Release.Namespace }} -o json)
+
+          echo "Validating policyTypes..."
+          POLICY_TYPES=$(echo "$NP_JSON" | jq -r '.spec.policyTypes | 
join(",")')
+          if [ "$POLICY_TYPES" != "Ingress" ]; then
+            echo "ERROR: Expected policyTypes [Ingress], Got: $POLICY_TYPES"
+            exit 1
+          fi
+
+          echo "Validating podSelector..."
+          SELECTOR_NAME=$(echo "$NP_JSON" | jq -r 
'.spec.podSelector.matchLabels["app.kubernetes.io/name"]')
+          SELECTOR_COMPONENT=$(echo "$NP_JSON" | jq -r 
'.spec.podSelector.matchLabels["app.kubernetes.io/component"]')
+          if [ "$SELECTOR_NAME" != "{{ include "spark-operator.name" . }}" ] 
|| [ "$SELECTOR_COMPONENT" != "operator-deployment" ]; then
+            echo "ERROR: podSelector does not match operator deployment 
labels. Got: $SELECTOR_NAME/$SELECTOR_COMPONENT"
+            exit 1
+          fi
+
+          echo "Validating probe port ingress rule..."
+          PROBE_PORT=$(echo "$NP_JSON" | jq -r '[.spec.ingress[] | 
select(has("from") | not)][0].ports[0].port')
+          if [ "$PROBE_PORT" != "{{ include "spark-operator.probePort" . }}" 
]; then
+            echo "ERROR: Expected probe port {{ include 
"spark-operator.probePort" . }} open to all sources, Got: $PROBE_PORT"
+            exit 1
+          fi
+
+          {{- if .Values.operatorDeployment.networkPolicy.metricsIngress }}
+          echo "Validating metrics port ingress rule..."
+          METRICS_PORT=$(echo "$NP_JSON" | jq -r '[.spec.ingress[] | 
select(has("from"))][0].ports[0].port')
+          if [ "$METRICS_PORT" != "{{ include "spark-operator.metricsPort" . 
}}" ]; then
+            echo "ERROR: Expected metrics port {{ include 
"spark-operator.metricsPort" . }} in the restricted rule, Got: $METRICS_PORT"
+            exit 1
+          fi
+          FROM_MATCHES=$(echo "$NP_JSON" | jq --argjson expected '{{ toJson 
.Values.operatorDeployment.networkPolicy.metricsIngress }}' '[.spec.ingress[] | 
select(has("from"))][0].from == $expected')
+          if [ "$FROM_MATCHES" != "true" ]; then
+            echo "ERROR: Metrics ingress sources do not match values. Got: 
$(echo "$NP_JSON" | jq -c '[.spec.ingress[] | select(has("from"))][0].from')"
+            exit 1
+          fi
+          {{- else }}
+          echo "Validating that the metrics port is denied..."
+          RESTRICTED_RULES=$(echo "$NP_JSON" | jq '[.spec.ingress[] | 
select(has("from"))] | length')
+          if [ "$RESTRICTED_RULES" != "0" ]; then
+            echo "ERROR: Expected no ingress rule with sources when 
metricsIngress is empty, Got: $RESTRICTED_RULES"
+            exit 1
+          fi
+          {{- end }}
+
+          echo "All NetworkPolicy tests passed!"
+  restartPolicy: Never
+  serviceAccountName: {{ .Values.operatorRbac.serviceAccount.name }}
+{{- end }}
diff --git a/build-tools/helm/spark-kubernetes-operator/values.schema.json 
b/build-tools/helm/spark-kubernetes-operator/values.schema.json
index 314c661..9d85fb8 100644
--- a/build-tools/helm/spark-kubernetes-operator/values.schema.json
+++ b/build-tools/helm/spark-kubernetes-operator/values.schema.json
@@ -391,6 +391,27 @@
               "additionalProperties": true
             }
           }
+        },
+        "networkPolicy": {
+          "type": "object",
+          "description": "NetworkPolicy for the operator pod",
+          "required": [
+            "enable"
+          ],
+          "properties": {
+            "enable": {
+              "type": "boolean",
+              "description": "Whether to create a NetworkPolicy for the 
operator pod"
+            },
+            "metricsIngress": {
+              "type": ["array", "null"],
+              "description": "List of NetworkPolicyPeer(s) allowed to reach 
the metrics port; when empty, ingress to the metrics port is denied",
+              "items": {
+                "type": "object",
+                "additionalProperties": true
+              }
+            }
+          }
         }
       }
     },
diff --git a/build-tools/helm/spark-kubernetes-operator/values.yaml 
b/build-tools/helm/spark-kubernetes-operator/values.yaml
index 68fddac..1fb1442 100644
--- a/build-tools/helm/spark-kubernetes-operator/values.yaml
+++ b/build-tools/helm/spark-kubernetes-operator/values.yaml
@@ -87,6 +87,19 @@ operatorDeployment:
     securityContext: { }
     dnsPolicy:
     dnsConfig:
+  # When enabled, a NetworkPolicy is created for the operator pod that allows 
ingress only to
+  # the health probe port (from any source, for kubelet probes) and to the 
metrics port from
+  # the sources listed in {networkPolicy.metricsIngress}. All other ingress 
traffic to the
+  # operator pod is denied. Note that this requires a CNI plugin with 
NetworkPolicy support,
+  # and that egress traffic is not restricted.
+  networkPolicy:
+    enable: false
+    # List of NetworkPolicyPeer(s) allowed to reach the metrics port, e.g. the 
Prometheus
+    # scraper. When empty, ingress to the metrics port is denied.
+    metricsIngress: [ ]
+    #  - namespaceSelector:
+    #      matchLabels:
+    #        kubernetes.io/metadata.name: "monitoring"
 
 operatorRbac:
   serviceAccount:
diff --git a/docs/operations.md b/docs/operations.md
index 971eb01..8ff47d9 100644
--- a/docs/operations.md
+++ b/docs/operations.md
@@ -132,6 +132,37 @@ __Notice__: The pod resources should be set as your 
workload in different enviro
 achieve a matched K8s pod QoS. See
 also [Pod Quality of Service 
Classes](https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/#quality-of-service-classes).
 
+## Restricting Network Access to the Operator
+
+The operator serves two plain-HTTP endpoints without authentication: the 
health probes
+(port 19091 by default) and the Prometheus metrics (port 19090 by default). To 
restrict
+in-cluster access to them, the chart can create a
+[NetworkPolicy](https://kubernetes.io/docs/concepts/services-networking/network-policies/)
+for the operator pod:
+
+```yaml
+operatorDeployment:
+  networkPolicy:
+    enable: true
+    metricsIngress:
+      - namespaceSelector:
+          matchLabels:
+            kubernetes.io/metadata.name: "monitoring"
+```
+
+When enabled, all ingress traffic to the operator pod is denied except:
+
+- the health probe port, reachable from any source — kubelet probe traffic 
originates from
+  the node and cannot be matched by pod or namespace selectors, and
+- the metrics port, reachable only from the `NetworkPolicyPeer`(s) listed in
+  `metricsIngress`. When the list is empty, ingress to the metrics port is 
denied, so make
+  sure your metrics scraper is listed before enabling this in an environment 
that collects
+  operator metrics.
+
+Note that this requires a CNI plugin that enforces NetworkPolicy; on clusters 
without such
+a plugin the policy is silently ignored. Egress traffic of the operator 
(Kubernetes API
+server, DNS) is not restricted by this policy.
+
 ## Operator Health(Liveness) Probe with Sentinel Resource
 
 Learning
diff --git a/tests/e2e/helm/helm-test-values/network-policy/values.yaml 
b/tests/e2e/helm/helm-test-values/network-policy/values.yaml
new file mode 100644
index 0000000..cef0a92
--- /dev/null
+++ b/tests/e2e/helm/helm-test-values/network-policy/values.yaml
@@ -0,0 +1,28 @@
+# 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.
+
+# Test values for validating the operator NetworkPolicy feature
+#
+# Usage:
+#   helm install spark-operator . -f 
tests/e2e/helm/helm-test-values/network-policy/values.yaml
+#   helm test spark-operator
+
+operatorDeployment:
+  networkPolicy:
+    enable: true
+    metricsIngress:
+      - namespaceSelector:
+          matchLabels:
+            kubernetes.io/metadata.name: "monitoring"


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to