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

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


The following commit(s) were added to refs/heads/branch-4.x by this push:
     new c46a91c42055 [SPARK-57493][K8S] Set `allowPrivilegeEscalation` to 
false for driver/executor containers by default
c46a91c42055 is described below

commit c46a91c4205594d9bfc1c902175f83c5ec82b1ba
Author: Dongjoon Hyun <[email protected]>
AuthorDate: Tue Jun 16 16:30:57 2026 -0700

    [SPARK-57493][K8S] Set `allowPrivilegeEscalation` to false for 
driver/executor containers by default
    
    ### What changes were proposed in this pull request?
    
    This PR sets `securityContext.allowPrivilegeEscalation: false` on driver 
and executor containers by default, with new configs to control it:
    
    - `spark.kubernetes.securityContext.allowPrivilegeEscalation` (default 
`false`)
    - `spark.kubernetes.driver.securityContext.allowPrivilegeEscalation` (falls 
back to the above)
    - `spark.kubernetes.executor.securityContext.allowPrivilegeEscalation` 
(falls back to the above)
    
    ### Why are the changes needed?
    
    Kubernetes allows privilege escalation by default. Emitting 
`allowPrivilegeEscalation: false` makes Spark pods secure-by-default and 
compliant with the Pod Security Standards `restricted` profile, while the 
configs allow opting out.
    
    Note that `Pod Security Standards` `restricted` profile requires 
`allowPrivilegeEscalation: false` for all containers.
    
    ### Does this PR introduce _any_ user-facing change?
    
    Yes to improve the security. Since 4.3.0, driver/executor containers get 
`allowPrivilegeEscalation: false` by default (previously unset, so Kubernetes 
allowed it). To restore the legacy behavior, set 
`spark.kubernetes.securityContext.allowPrivilegeEscalation=true`.
    
    ### How was this patch tested?
    
    Pass the CIs with the newly added test cases.
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (claude-opus-4-8)
    
    Closes #56549 from dongjoon-hyun/SPARK-57493.
    
    Authored-by: Dongjoon Hyun <[email protected]>
    Signed-off-by: Dongjoon Hyun <[email protected]>
    (cherry picked from commit f7c26bc628abb78e9ee047e368580a1118bff6c4)
    Signed-off-by: Dongjoon Hyun <[email protected]>
---
 docs/core-migration-guide.md                       |  2 ++
 docs/running-on-kubernetes.md                      | 24 +++++++++++++++
 .../scala/org/apache/spark/deploy/k8s/Config.scala | 34 +++++++++++++++++++++-
 .../k8s/features/BasicDriverFeatureStep.scala      |  5 ++++
 .../k8s/features/BasicExecutorFeatureStep.scala    |  5 ++++
 .../k8s/features/BasicDriverFeatureStepSuite.scala | 25 ++++++++++++++++
 .../features/BasicExecutorFeatureStepSuite.scala   | 26 +++++++++++++++++
 7 files changed, 120 insertions(+), 1 deletion(-)

diff --git a/docs/core-migration-guide.md b/docs/core-migration-guide.md
index 9be60572387c..2d3df8a038f4 100644
--- a/docs/core-migration-guide.md
+++ b/docs/core-migration-guide.md
@@ -26,6 +26,8 @@ license: |
 
 - Since Spark 4.3, Spark executor pods connect to the driver via the driver 
pod IP directly instead of the driver's Kubernetes Service. To restore the 
legacy behavior, you can set `spark.kubernetes.executor.useDriverPodIP` to 
`false`.
 
+- Since Spark 4.3, Spark sets `allowPrivilegeEscalation` to `false` on the 
driver and executor containers' security context by default. To restore the 
legacy behavior, you can set 
`spark.kubernetes.securityContext.allowPrivilegeEscalation` to `true`.
+
 ## Upgrading from Core 4.1 to 4.2
 
 - Since Spark 4.2, Spark Master REST API uses Java 21 virtual threads by 
default when running on Java 21 or later. To restore the legacy behavior, you 
can set `spark.master.rest.virtualThread.enabled` to `false`.
diff --git a/docs/running-on-kubernetes.md b/docs/running-on-kubernetes.md
index 9be54d898486..390e054434ca 100644
--- a/docs/running-on-kubernetes.md
+++ b/docs/running-on-kubernetes.md
@@ -1583,6 +1583,30 @@ See the [configuration page](configuration.html) for 
information on Spark config
   </td>
   <td>3.4.0</td>
 </tr>
+<tr>
+  
<td><code>spark.kubernetes.securityContext.allowPrivilegeEscalation</code></td>
+  <td><code>false</code></td>
+  <td>
+    Sets the <code>allowPrivilegeEscalation</code> field of the driver and 
executor containers' security context. When <code>false</code> (default), a 
container cannot gain more privileges than its parent process. Set to 
<code>true</code> to opt out of this restriction. Driver and executor can be 
configured individually via the container type-specific config below.
+  </td>
+  <td>4.3.0</td>
+</tr>
+<tr>
+  
<td><code>spark.kubernetes.driver.securityContext.allowPrivilegeEscalation</code></td>
+  <td><code>(value of 
spark.kubernetes.securityContext.allowPrivilegeEscalation)</code></td>
+  <td>
+    Sets the <code>allowPrivilegeEscalation</code> field of the driver 
container's security context. Falls back to 
<code>spark.kubernetes.securityContext.allowPrivilegeEscalation</code> if not 
set.
+  </td>
+  <td>4.3.0</td>
+</tr>
+<tr>
+  
<td><code>spark.kubernetes.executor.securityContext.allowPrivilegeEscalation</code></td>
+  <td><code>(value of 
spark.kubernetes.securityContext.allowPrivilegeEscalation)</code></td>
+  <td>
+    Sets the <code>allowPrivilegeEscalation</code> field of the executor 
container's security context. Falls back to 
<code>spark.kubernetes.securityContext.allowPrivilegeEscalation</code> if not 
set.
+  </td>
+  <td>4.3.0</td>
+</tr>
 <tr>
   <td><code>spark.kubernetes.executor.useDriverPodIP</code></td>
   <td><code>true</code></td>
diff --git 
a/resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala
 
b/resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala
index 120881469e3a..4ec27b6fab75 100644
--- 
a/resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala
+++ 
b/resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/Config.scala
@@ -21,7 +21,7 @@ import java.util.concurrent.TimeUnit
 
 import org.apache.spark.deploy.k8s.Constants._
 import org.apache.spark.internal.Logging
-import org.apache.spark.internal.config.{ConfigBuilder, DYN_ALLOCATION_ENABLED}
+import org.apache.spark.internal.config.{ConfigBindingPolicy, ConfigBuilder, 
DYN_ALLOCATION_ENABLED}
 
 private[spark] object Config extends Logging {
 
@@ -120,6 +120,38 @@ private[spark] object Config extends Logging {
       .booleanConf
       .createWithDefault(false)
 
+  val KUBERNETES_ALLOW_PRIVILEGE_ESCALATION =
+    ConfigBuilder("spark.kubernetes.securityContext.allowPrivilegeEscalation")
+      .doc("Sets the allowPrivilegeEscalation field of the driver and executor 
" +
+        "containers' security context. When false (default), a container 
cannot gain " +
+        "more privileges than its parent process. Set to true to opt out of 
this " +
+        "restriction. Driver and executor can be configured individually via 
the " +
+        "container type-specific config.")
+      .version("4.3.0")
+      .withBindingPolicy(ConfigBindingPolicy.NOT_APPLICABLE)
+      .booleanConf
+      .createWithDefault(false)
+
+  val KUBERNETES_DRIVER_ALLOW_PRIVILEGE_ESCALATION =
+    
ConfigBuilder("spark.kubernetes.driver.securityContext.allowPrivilegeEscalation")
+      .doc("Sets the allowPrivilegeEscalation field of the driver container's 
security " +
+        "context. When false (default), the container cannot gain more 
privileges than " +
+        "its parent process. Set to true to opt out of this restriction. Falls 
back to " +
+        s"${KUBERNETES_ALLOW_PRIVILEGE_ESCALATION.key} if not set.")
+      .version("4.3.0")
+      .withBindingPolicy(ConfigBindingPolicy.NOT_APPLICABLE)
+      .fallbackConf(KUBERNETES_ALLOW_PRIVILEGE_ESCALATION)
+
+  val KUBERNETES_EXECUTOR_ALLOW_PRIVILEGE_ESCALATION =
+    
ConfigBuilder("spark.kubernetes.executor.securityContext.allowPrivilegeEscalation")
+      .doc("Sets the allowPrivilegeEscalation field of the executor 
container's security " +
+        "context. When false (default), the container cannot gain more 
privileges than " +
+        "its parent process. Set to true to opt out of this restriction. Falls 
back to " +
+        s"${KUBERNETES_ALLOW_PRIVILEGE_ESCALATION.key} if not set.")
+      .version("4.3.0")
+      .withBindingPolicy(ConfigBindingPolicy.NOT_APPLICABLE)
+      .fallbackConf(KUBERNETES_ALLOW_PRIVILEGE_ESCALATION)
+
   val KUBERNETES_EXECUTOR_USE_DRIVER_POD_IP =
     ConfigBuilder("spark.kubernetes.executor.useDriverPodIP")
       .doc("If true, executor pods use Driver pod IP directly instead of 
Driver Service.")
diff --git 
a/resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/features/BasicDriverFeatureStep.scala
 
b/resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/features/BasicDriverFeatureStep.scala
index eaf39914ea02..579e5baeffda 100644
--- 
a/resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/features/BasicDriverFeatureStep.scala
+++ 
b/resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/features/BasicDriverFeatureStep.scala
@@ -38,6 +38,8 @@ private[spark] class BasicDriverFeatureStep(conf: 
KubernetesDriverConf)
 
   private val driverContainerImage = conf.image
 
+  private val allowPrivilegeEscalation = 
conf.get(KUBERNETES_DRIVER_ALLOW_PRIVILEGE_ESCALATION)
+
   // CPU settings
   private val driverCpuCores = conf.get(DRIVER_CORES)
   private val driverCoresRequest = conf
@@ -140,6 +142,9 @@ private[spark] class BasicDriverFeatureStep(conf: 
KubernetesDriverConf)
         .addToLimits("memory", driverMemoryQuantity)
         .addToLimits(driverResourceQuantities.asJava)
         .endResources()
+      .editOrNewSecurityContext()
+        .withAllowPrivilegeEscalation(allowPrivilegeEscalation)
+        .endSecurityContext()
       .build()
 
     val driverPod = new PodBuilder(pod.pod)
diff --git 
a/resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/features/BasicExecutorFeatureStep.scala
 
b/resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/features/BasicExecutorFeatureStep.scala
index 655df6c5acad..4209b2f513c2 100644
--- 
a/resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/features/BasicExecutorFeatureStep.scala
+++ 
b/resource-managers/kubernetes/core/src/main/scala/org/apache/spark/deploy/k8s/features/BasicExecutorFeatureStep.scala
@@ -42,6 +42,8 @@ private[spark] class BasicExecutorFeatureStep(
 
   // Consider moving some of these fields to KubernetesConf or 
KubernetesExecutorSpecificConf
   private val executorContainerImage = kubernetesConf.image
+  private val allowPrivilegeEscalation =
+    kubernetesConf.get(KUBERNETES_EXECUTOR_ALLOW_PRIVILEGE_ESCALATION)
   private val blockManagerPort = kubernetesConf
     .sparkConf
     .getInt(BLOCK_MANAGER_PORT.key, DEFAULT_BLOCKMANAGER_PORT)
@@ -238,6 +240,9 @@ private[spark] class BasicExecutorFeatureStep(
       .addAllToEnv(executorEnv.asJava)
       .addAllToPorts(requiredPorts.asJava)
       .addToArgs("executor")
+      .editOrNewSecurityContext()
+        .withAllowPrivilegeEscalation(allowPrivilegeEscalation)
+        .endSecurityContext()
       .build()
     val executorContainerWithConfVolume = if (disableConfigMap) {
       executorContainer
diff --git 
a/resource-managers/kubernetes/core/src/test/scala/org/apache/spark/deploy/k8s/features/BasicDriverFeatureStepSuite.scala
 
b/resource-managers/kubernetes/core/src/test/scala/org/apache/spark/deploy/k8s/features/BasicDriverFeatureStepSuite.scala
index 55726ac40ce8..70e2239205ae 100644
--- 
a/resource-managers/kubernetes/core/src/test/scala/org/apache/spark/deploy/k8s/features/BasicDriverFeatureStepSuite.scala
+++ 
b/resource-managers/kubernetes/core/src/test/scala/org/apache/spark/deploy/k8s/features/BasicDriverFeatureStepSuite.scala
@@ -143,6 +143,31 @@ class BasicDriverFeatureStepSuite extends SparkFunSuite {
     assert(featureStep.getAdditionalPodSystemProperties() === 
expectedSparkConf)
   }
 
+  test("Set allowPrivilegeEscalation to false on the driver container by 
default") {
+    val sparkConf = new SparkConf().set(CONTAINER_IMAGE, "spark-driver:latest")
+    val conf = KubernetesTestConf.createDriverConf(sparkConf = sparkConf)
+    val pod = new 
BasicDriverFeatureStep(conf).configurePod(SparkPod.initialPod())
+    assert(pod.container.getSecurityContext.getAllowPrivilegeEscalation === 
false)
+  }
+
+  test("Support 
spark.kubernetes.driver.securityContext.allowPrivilegeEscalation") {
+    val sparkConf = new SparkConf()
+      .set(CONTAINER_IMAGE, "spark-driver:latest")
+      .set(KUBERNETES_DRIVER_ALLOW_PRIVILEGE_ESCALATION, true)
+    val conf = KubernetesTestConf.createDriverConf(sparkConf = sparkConf)
+    val pod = new 
BasicDriverFeatureStep(conf).configurePod(SparkPod.initialPod())
+    assert(pod.container.getSecurityContext.getAllowPrivilegeEscalation === 
true)
+  }
+
+  test("driver allowPrivilegeEscalation falls back to the shared config") {
+    val sparkConf = new SparkConf()
+      .set(CONTAINER_IMAGE, "spark-driver:latest")
+      .set(KUBERNETES_ALLOW_PRIVILEGE_ESCALATION, true)
+    val conf = KubernetesTestConf.createDriverConf(sparkConf = sparkConf)
+    val pod = new 
BasicDriverFeatureStep(conf).configurePod(SparkPod.initialPod())
+    assert(pod.container.getSecurityContext.getAllowPrivilegeEscalation === 
true)
+  }
+
   test("Check driver pod respects kubernetes driver request cores") {
     val sparkConf = new SparkConf()
       .set(KUBERNETES_DRIVER_POD_NAME, "spark-driver-pod")
diff --git 
a/resource-managers/kubernetes/core/src/test/scala/org/apache/spark/deploy/k8s/features/BasicExecutorFeatureStepSuite.scala
 
b/resource-managers/kubernetes/core/src/test/scala/org/apache/spark/deploy/k8s/features/BasicExecutorFeatureStepSuite.scala
index 77dcb11ba43b..e1261cadf3ab 100644
--- 
a/resource-managers/kubernetes/core/src/test/scala/org/apache/spark/deploy/k8s/features/BasicExecutorFeatureStepSuite.scala
+++ 
b/resource-managers/kubernetes/core/src/test/scala/org/apache/spark/deploy/k8s/features/BasicExecutorFeatureStepSuite.scala
@@ -160,6 +160,32 @@ class BasicExecutorFeatureStepSuite extends SparkFunSuite 
with BeforeAndAfter {
     assert(executor.pod.getSpec.getTerminationGracePeriodSeconds === 0L)
   }
 
+  test("Set allowPrivilegeEscalation to false on the executor container by 
default") {
+    initDefaultProfile(baseConf)
+    val step = new BasicExecutorFeatureStep(newExecutorConf(), new 
SecurityManager(baseConf),
+      defaultProfile)
+    val executor = step.configurePod(SparkPod.initialPod())
+    assert(executor.container.getSecurityContext.getAllowPrivilegeEscalation 
=== false)
+  }
+
+  test("Support 
spark.kubernetes.executor.securityContext.allowPrivilegeEscalation") {
+    baseConf.set(KUBERNETES_EXECUTOR_ALLOW_PRIVILEGE_ESCALATION, true)
+    initDefaultProfile(baseConf)
+    val step = new BasicExecutorFeatureStep(newExecutorConf(), new 
SecurityManager(baseConf),
+      defaultProfile)
+    val executor = step.configurePod(SparkPod.initialPod())
+    assert(executor.container.getSecurityContext.getAllowPrivilegeEscalation 
=== true)
+  }
+
+  test("executor allowPrivilegeEscalation falls back to the shared config") {
+    baseConf.set(KUBERNETES_ALLOW_PRIVILEGE_ESCALATION, true)
+    initDefaultProfile(baseConf)
+    val step = new BasicExecutorFeatureStep(newExecutorConf(), new 
SecurityManager(baseConf),
+      defaultProfile)
+    val executor = step.configurePod(SparkPod.initialPod())
+    assert(executor.container.getSecurityContext.getAllowPrivilegeEscalation 
=== true)
+  }
+
   test("basic executor pod with resources") {
     val fpgaResourceID = new ResourceID(SPARK_EXECUTOR_PREFIX, FPGA)
     val gpuExecutorResourceID = new ResourceID(SPARK_EXECUTOR_PREFIX, GPU)


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

Reply via email to