sunchao commented on code in PR #57525:
URL: https://github.com/apache/spark/pull/57525#discussion_r3714069804


##########
core/src/main/scala/org/apache/spark/executor/CoarseGrainedExecutorBackend.scala:
##########
@@ -104,7 +104,8 @@ private[spark] class CoarseGrainedExecutorBackend(
       driver = Some(ref)
       env.executorBackend = Option(this)
       ref.ask[Boolean](RegisterExecutor(executorId, self, hostname, cores, 
extractLogUrls,
-        extractAttributes, _resources, resourceProfile.id))
+        extractAttributes ++ Map(EXECUTOR_DRIVER_INSTANCE_TOKEN -> 
getDriverInstanceToken),

Review Comment:
   [P1] Keep the driver instance token out of public executor attributes
   
   Putting the bearer UUID in `RegisterExecutor.attributes` makes it public as 
soon as the first executor registers. `DriverEndpoint` copies the same 
attributes into `ExecutorData` and publishes `SparkListenerExecutorAdded`; 
`EventLoggingListener.onExecutorAdded` writes that event without redaction, 
`JsonProtocol.executorInfoToJson` serializes its `Attributes` map verbatim, and 
`AppStatusListener` exposes the same map through 
`/api/v1/applications/<app-id>/executors`. The normal `spark.redaction.regex` 
protects environment updates but is not applied to executor attributes, and UI 
ACLs are disabled by default. Anyone able to read the executor API or event log 
can replay the token in `RetrieveSparkAppConfigWithIdentity` to obtain the 
application's Hadoop delegation credentials/I/O-encryption key or register a 
fake executor. Remove the token from the attributes immediately after 
validation, before creating `ExecutorData` or publishing the event, and add 
event-log/REST non-disclosure cov
 erage.



##########
core/src/main/scala/org/apache/spark/scheduler/cluster/CoarseGrainedSchedulerBackend.scala:
##########
@@ -62,6 +63,11 @@ class CoarseGrainedSchedulerBackend(scheduler: 
TaskSchedulerImpl, val rpcEnv: Rp
   // Total number of executors that are currently registered
   protected val totalRegisteredExecutors = new AtomicInteger(0)
   protected val conf = scheduler.sc.conf
+  private val driverInstanceToken = UUID.randomUUID().toString
+  // Propagate the token as an environment variable to executor processes.
+  // setExecutorEnv covers YARN and Kubernetes; sc.executorEnvs is updated 
directly for Standalone.
+  conf.setExecutorEnv(EXECUTOR_DRIVER_INSTANCE_TOKEN, driverInstanceToken)
+  scheduler.sc.executorEnvs(EXECUTOR_DRIVER_INSTANCE_TOKEN) = 
driverInstanceToken

Review Comment:
   [P1] Do not disclose driver tokens through Standalone master state
   
   Adding the bearer token to `sc.executorEnvs` causes 
`StandaloneSchedulerBackend.start` to embed it in 
`ApplicationDescription.command.environment` 
(`StandaloneSchedulerBackend.scala:115-135`). The Standalone master retains 
that complete serializable description and returns every application's 
unredacted `ApplicationInfo.desc.command.environment` to any caller of 
`RequestMasterState` (`Master.scala:505-509`). `docs/security.md:44-50` 
explicitly documents Standalone applications and daemons sharing the same RPC 
authentication secret, so application A can query the master for application 
B's raw token and immediately replay it against B's credential-bearing 
bootstrap or registration endpoint. SparkConf/UI redaction does not protect 
this RPC response, and fixing the separate executor-attribute leak does not 
close this earlier exposure. Avoid distributing per-application bearer secrets 
through globally readable master state, and add a regression covering two 
applications sharing the cl
 uster RPC secret.



##########
resource-managers/kubernetes/core/src/test/scala/org/apache/spark/scheduler/cluster/k8s/KubernetesClusterSchedulerBackendSuite.scala:
##########
@@ -119,6 +120,7 @@ class KubernetesClusterSchedulerBackendSuite extends 
SparkFunSuite with BeforeAn
     MockitoAnnotations.openMocks(this).close()
     when(taskScheduler.sc).thenReturn(sc)
     when(sc.conf).thenReturn(sparkConf)
+    when(sc.executorEnvs).thenReturn(new 
scala.collection.mutable.HashMap[String, String])

Review Comment:
   [P2] Stub the isolated Kubernetes SparkContext environment too
   
   This new setup only stubs `executorEnvs` on the shared `sc` mock. The 
existing `SPARK-56238: applicationId() is stable across calls when spark.app.id 
is not set` test below creates a separate `localSc = 
mock(classOf[SparkContext])` at lines 324-340 without stubbing 
`localSc.executorEnvs`. Constructing its `KubernetesClusterSchedulerBackend` 
now immediately executes 
`scheduler.sc.executorEnvs(EXECUTOR_DRIVER_INSTANCE_TOKEN) = 
driverInstanceToken` in the base constructor; Mockito returns `null` for that 
separate mock, so the test throws `NullPointerException` before it reaches 
either application-ID assertion. Add the same mutable-map stub for `localSc` so 
the existing Kubernetes regression continues to run.



##########
core/src/main/scala/org/apache/spark/scheduler/cluster/CoarseGrainedSchedulerBackend.scala:
##########
@@ -244,11 +263,27 @@ class CoarseGrainedSchedulerBackend(scheduler: 
TaskSchedulerImpl, val rpcEnv: Rp
         logError(log"Received unexpected message. ${MDC(ERROR, e)}")
     }
 
+    private def replySparkAppConfig(resourceProfileId: Int, context: 
RpcCallContext): Unit = {
+      val rp = 
scheduler.sc.resourceProfileManager.resourceProfileFromId(resourceProfileId)
+      val reply = SparkAppConfig(
+        sparkProperties,
+        SparkEnv.get.securityManager.getIOEncryptionKey(),
+        Option(delegationTokens.get()),
+        rp,
+        currentLogLevel)
+      context.reply(reply)
+    }
+
     override def receiveAndReply(context: RpcCallContext): 
PartialFunction[Any, Unit] = {
 
       case RegisterExecutor(executorId, executorRef, hostname, cores, logUrls,
           attributes, resources, resourceProfileId) =>
-        if (executorDataMap.contains(executorId)) {
+        val mismatch = 
verifyDriverInstanceToken(attributes.get(EXECUTOR_DRIVER_INSTANCE_TOKEN))

Review Comment:
   [P1] Update all existing registration senders before requiring the token
   
   This new check rejects existing in-tree `RegisterExecutor(..., attributes = 
Map.empty, ...)` callers that were not updated in 
`HeartbeatReceiverSuite.scala:180-185` and 
`StandaloneDynamicAllocationSuite.scala:507-508,630-632`. The exact-head Core 
JUnit artifacts confirm **11 failing tests**: one in `HeartbeatReceiverSuite` 
and ten in `StandaloneDynamicAllocationSuite`, with the underlying exception 
`SparkException: Executor did not supply a driver instance token.` at this 
exact line. Nine standalone cases fail in `syncExecutors`, and the 
excluded-host regression now receives the missing-token exception instead of 
the expected `IllegalStateException`. Populate the matching driver token in 
every existing synthetic registration and restore the required Core check.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to