Skip to content

[SPARK-58592][CORE] Redact secrets in Standalone Master RequestMasterState RPC - #57796

Open
wangyum wants to merge 3 commits into
apache:masterfrom
wangyum:SPARK-58592
Open

[SPARK-58592][CORE] Redact secrets in Standalone Master RequestMasterState RPC#57796
wangyum wants to merge 3 commits into
apache:masterfrom
wangyum:SPARK-58592

Conversation

@wangyum

@wangyum wangyum commented Aug 5, 2026

Copy link
Copy Markdown
Member

What changes were proposed in this pull request?

This PR redacts secrets in the Standalone Master RequestMasterState RPC response.

MasterStateResponse gains a writeReplace() hook that fires during cross-process Java serialization (the RPC path) and substitutes ApplicationInfo/DriverInfo copies whose Command.environment and Command.javaOpts are redacted. The Master attaches its SparkConf to the response via withConf(conf) in the RequestMasterState handler so writeReplace can apply the configured spark.redaction.regex.

The redaction logic lives in a new Command.redactedCopy(conf) method, which is also reused by JsonProtocol.writeApplicationDescription (previously had its own inline copy of the same logic). ApplicationInfo and DriverInfo each gain a thin redactedCopy(conf) that delegates to Command.redactedCopy. They do not get writeReplace themselves — only the RPC response wrapper does, so PersistenceEngine serialization is unaffected.

Why are the changes needed?

Any client that can connect to spark://master:port can call RequestMasterState and receive ApplicationInfo/DriverInfo objects containing Command.environment and Command.javaOpts with plaintext secrets (e.g. spark.executorEnv.PASSWORD, HADOOP_CREDSTORE_PASSWORD, AWS_SECRET_ACCESS_KEY). With spark.authenticate=false (the default), there is no authentication on the Master RPC.

The web UI JSON path already redacts these (SPARK-57098), but the RPC path does not — this closes that gap.

This only affects Spark Standalone mode. ApplicationInfo/DriverInfo are Standalone Master-specific classes; YARN and Kubernetes do not use them or have an equivalent RPC.

Does this PR introduce any user-facing change?

No. The redacted fields are in the internal RPC response between Spark processes; the Master web UI and REST JSON output were already redacted by SPARK-57098.

How was this patch tested?

Added two tests in JsonProtocolSuite:

  1. SPARK-58592: redactedCopy redacts secrets in ApplicationInfo and DriverInfo — unit test verifying that redactedCopy replaces secret values with Utils.REDACTION_REPLACEMENT_TEXT while preserving non-sensitive values.

  2. SPARK-58592: writeReplace redacts secrets during RPC serialization — end-to-end test that Java-serializes a MasterStateResponse.withConf(conf) containing both an ApplicationInfo and a DriverInfo with secrets, deserializes it, and asserts the secrets are redacted. This verifies that writeReplace is actually triggered during the serialization path used by RPC.

Was this patch authored or co-authored using generative AI tooling?

Generated-by: GLM 5.2.

ApplicationInfo/DriverInfo are Java-serialized as-is by PersistenceEngine
(ZooKeeper/filesystem/RocksDB) for Master HA recovery, so secrets in an
app or driver's Command (env vars, -D java opts) end up in plaintext in
ZK znodes or the recovery directory. Tag each instance with the SparkConf
at creation time and redact via writeReplace so the redacted copy is what
actually gets serialized, without changing the in-memory object used by
the running Master.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Redacts secrets (env vars and -D Java options) when ApplicationInfo / DriverInfo are persisted for Standalone Master HA recovery, without changing in-memory behavior.

Changes:

  • Tag ApplicationInfo / DriverInfo with SparkConf at construction in Master, and use writeReplace() to serialize a redacted substitute.
  • Add redactedCopy() helpers to redact Command environment and Java opts using existing Utils helpers.
  • Add unit tests covering redaction and Java serialization behavior.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
core/src/main/scala/org/apache/spark/deploy/master/Master.scala Ensures newly created app/driver infos are tagged with SparkConf so persistence triggers redaction.
core/src/main/scala/org/apache/spark/deploy/master/ApplicationInfo.scala Adds redactedCopy, stores transient conf, and writeReplace to persist redacted state.
core/src/main/scala/org/apache/spark/deploy/master/DriverInfo.scala Adds redactedCopy, stores transient conf, and writeReplace to persist redacted state.
core/src/test/scala/org/apache/spark/deploy/master/PersistenceEngineSuite.scala Adds filesystem persistence test asserting secrets aren’t present on disk and are redacted after recovery.
core/src/test/scala/org/apache/spark/deploy/JsonProtocolSuite.scala Adds unit tests for redactedCopy and writeReplace serialization redaction.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread core/src/main/scala/org/apache/spark/deploy/master/ApplicationInfo.scala Outdated
Comment thread core/src/main/scala/org/apache/spark/deploy/master/DriverInfo.scala
Comment thread core/src/main/scala/org/apache/spark/deploy/master/DriverInfo.scala Outdated
Comment thread core/src/test/scala/org/apache/spark/deploy/master/PersistenceEngineSuite.scala Outdated
Comment thread core/src/test/scala/org/apache/spark/deploy/master/PersistenceEngineSuite.scala Outdated
Comment thread core/src/main/scala/org/apache/spark/deploy/master/ApplicationInfo.scala Outdated
private def writeReplace(): AnyRef = {
if (_conf == null) this else redactedCopy(_conf)
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functional regression in HA recovery for supervised drivers and executors. After a master crash and recovery, the objects deserialized from disk are already-redacted copies: desc.command.environment and desc.command.javaOpts contain Utils.REDACTION_REPLACEMENT_TEXT in place of real secrets.

The recovery path in Master.completeRecovery() calls relaunchDriver(d) for every supervised driver whose worker is gone, and relaunchDriver calls createDriver(driver.desc); where driver.desc is the recovered, permanently-redacted DriverDescription. The resulting DriverInfo (even with withConf(conf) attached) has a redacted command, so the worker receives LaunchDriver with REDACTION_REPLACEMENT_TEXT in env vars. Any driver that requires env-var secrets (e.g. HADOOP_CREDSTORE_PASSWORD, AWS_ACCESS_KEY) will fail to authenticate after master-crash recovery. The same applies to executor re-launches: when a recovered application needs new executors, launchExecutor sends exec.application.desc (the redacted ApplicationDescription) to the worker, which builds the executor process with the redacted environment. The PersistenceEngineSuite test explicitly asserts recoveredApp.desc.command.environment("PASSWORD") == Utils.REDACTION_REPLACEMENT_TEXT, confirming the regression.

The fix should store an out-of-band, separate redacted copy for persistence (e.g., serialize a lightweight ApplicationDescription/DriverDescription snapshot with redacted fields) rather than having the live deserialized object carry permanently-redacted state.

@wangyum wangyum Aug 6, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @uros-b, I reduced the scope to RPC only, and this only affects Spark Standalone mode. ApplicationInfo/DriverInfo are Standalone/Master-specific classes; YARN and Kubernetes do not use them or have an equivalent RPC.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be persistence handling for sensitive fields:

  • When spark.authenticate=true: consider encrypting the persisted data instead of redacting it.
  • When spark.authenticate=false: no change to existing behavior.

wangyum added 2 commits August 6, 2026 12:04
…State RPC

Replace writeReplace on ApplicationInfo/DriverInfo with writeReplace on
MasterStateResponse only. This redacts secrets (Command.environment,
javaOpts) during cross-process RPC serialization via Utils.redact, while
leaving PersistenceEngine serialization untouched - avoiding the HA
recovery regression where redacted copies would be persisted and
deserialized with REDACTION_REPLACEMENT_TEXT instead of real secrets.

- Remove writeReplace/withConf/_conf from ApplicationInfo and DriverInfo
- Keep redactedCopy on both classes
- Add writeReplace/withConf to MasterStateResponse
- Call withConf(conf) only in RequestMasterState handler
- Revert createApplication/createDriver to not call withConf
- Remove PersistenceEngineSuite test (persistence not touched)
- Update JsonProtocolSuite tests for RPC serialization path

Generated-by: GLM 5.2.
Move the redactedCopy(conf) method down to Command itself, eliminating
the same 3-line redaction pattern duplicated in ApplicationInfo,
DriverInfo, and JsonProtocol.writeApplicationDescription.

Generated-by: GLM 5.2.
@wangyum wangyum changed the title [SPARK-58592][CORE] Redact secrets in persisted Master recovery state [SPARK-58592][CORE] Redact secrets in Standalone Master RequestMasterState RPC Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants