Skip to content

Commit 658814c

Browse files
kanzhangsrowen
authored andcommitted
[SPARK-8129] [CORE] [Sec] Pass auth secrets to executors via env variables
Env variables are not visible to non-Spark users, based on suggestion from vanzin. Author: Kan Zhang <[email protected]> Closes apache#6774 from kanzhang/env and squashes the following commits: 5dd84c6 [Kan Zhang] remove auth secret conf from initial set up for executors 90cb7d2 [Kan Zhang] always filter out auth secret af4d89d [Kan Zhang] minor refactering e88993e [Kan Zhang] pass auth secret to executors via env variable
1 parent ccf010f commit 658814c

File tree

7 files changed

+72
-19
lines changed

7 files changed

+72
-19
lines changed

core/src/main/scala/org/apache/spark/SecurityManager.scala

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ private[spark] class SecurityManager(sparkConf: SparkConf)
192192
// key used to store the spark secret in the Hadoop UGI
193193
private val sparkSecretLookupKey = "sparkCookie"
194194

195-
private val authOn = sparkConf.getBoolean("spark.authenticate", false)
195+
private val authOn = sparkConf.getBoolean(SecurityManager.SPARK_AUTH_CONF, false)
196196
// keep spark.ui.acls.enable for backwards compatibility with 1.0
197197
private var aclsOn =
198198
sparkConf.getBoolean("spark.acls.enable", sparkConf.getBoolean("spark.ui.acls.enable", false))
@@ -365,10 +365,12 @@ private[spark] class SecurityManager(sparkConf: SparkConf)
365365
cookie
366366
} else {
367367
// user must have set spark.authenticate.secret config
368-
sparkConf.getOption("spark.authenticate.secret") match {
368+
// For Master/Worker, auth secret is in conf; for Executors, it is in env variable
369+
sys.env.get(SecurityManager.ENV_AUTH_SECRET)
370+
.orElse(sparkConf.getOption(SecurityManager.SPARK_AUTH_SECRET_CONF)) match {
369371
case Some(value) => value
370372
case None => throw new Exception("Error: a secret key must be specified via the " +
371-
"spark.authenticate.secret config")
373+
SecurityManager.SPARK_AUTH_SECRET_CONF + " config")
372374
}
373375
}
374376
sCookie
@@ -449,3 +451,12 @@ private[spark] class SecurityManager(sparkConf: SparkConf)
449451
override def getSaslUser(appId: String): String = getSaslUser()
450452
override def getSecretKey(appId: String): String = getSecretKey()
451453
}
454+
455+
private[spark] object SecurityManager {
456+
457+
val SPARK_AUTH_CONF: String = "spark.authenticate"
458+
val SPARK_AUTH_SECRET_CONF: String = "spark.authenticate.secret"
459+
// This is used to set auth secret to an executor's env variable. It should have the same
460+
// value as SPARK_AUTH_SECERET_CONF set in SparkConf
461+
val ENV_AUTH_SECRET = "_SPARK_AUTH_SECRET"
462+
}

core/src/main/scala/org/apache/spark/SparkConf.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ private[spark] object SparkConf extends Logging {
557557
def isExecutorStartupConf(name: String): Boolean = {
558558
isAkkaConf(name) ||
559559
name.startsWith("spark.akka") ||
560-
name.startsWith("spark.auth") ||
560+
(name.startsWith("spark.auth") && name != SecurityManager.SPARK_AUTH_SECRET_CONF) ||
561561
name.startsWith("spark.ssl") ||
562562
isSparkPortConf(name)
563563
}

core/src/main/scala/org/apache/spark/deploy/worker/CommandUtils.scala

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import scala.collection.JavaConversions._
2424
import scala.collection.Map
2525

2626
import org.apache.spark.Logging
27+
import org.apache.spark.SecurityManager
2728
import org.apache.spark.deploy.Command
2829
import org.apache.spark.launcher.WorkerCommandBuilder
2930
import org.apache.spark.util.Utils
@@ -40,12 +41,14 @@ object CommandUtils extends Logging {
4041
*/
4142
def buildProcessBuilder(
4243
command: Command,
44+
securityMgr: SecurityManager,
4345
memory: Int,
4446
sparkHome: String,
4547
substituteArguments: String => String,
4648
classPaths: Seq[String] = Seq[String](),
4749
env: Map[String, String] = sys.env): ProcessBuilder = {
48-
val localCommand = buildLocalCommand(command, substituteArguments, classPaths, env)
50+
val localCommand = buildLocalCommand(
51+
command, securityMgr, substituteArguments, classPaths, env)
4952
val commandSeq = buildCommandSeq(localCommand, memory, sparkHome)
5053
val builder = new ProcessBuilder(commandSeq: _*)
5154
val environment = builder.environment()
@@ -69,27 +72,34 @@ object CommandUtils extends Logging {
6972
*/
7073
private def buildLocalCommand(
7174
command: Command,
75+
securityMgr: SecurityManager,
7276
substituteArguments: String => String,
7377
classPath: Seq[String] = Seq[String](),
7478
env: Map[String, String]): Command = {
7579
val libraryPathName = Utils.libraryPathEnvName
7680
val libraryPathEntries = command.libraryPathEntries
7781
val cmdLibraryPath = command.environment.get(libraryPathName)
7882

79-
val newEnvironment = if (libraryPathEntries.nonEmpty && libraryPathName.nonEmpty) {
83+
var newEnvironment = if (libraryPathEntries.nonEmpty && libraryPathName.nonEmpty) {
8084
val libraryPaths = libraryPathEntries ++ cmdLibraryPath ++ env.get(libraryPathName)
8185
command.environment + ((libraryPathName, libraryPaths.mkString(File.pathSeparator)))
8286
} else {
8387
command.environment
8488
}
8589

90+
// set auth secret to env variable if needed
91+
if (securityMgr.isAuthenticationEnabled) {
92+
newEnvironment += (SecurityManager.ENV_AUTH_SECRET -> securityMgr.getSecretKey)
93+
}
94+
8695
Command(
8796
command.mainClass,
8897
command.arguments.map(substituteArguments),
8998
newEnvironment,
9099
command.classPathEntries ++ classPath,
91100
Seq[String](), // library path already captured in environment variable
92-
command.javaOpts)
101+
// filter out auth secret from java options
102+
command.javaOpts.filterNot(_.startsWith("-D" + SecurityManager.SPARK_AUTH_SECRET_CONF)))
93103
}
94104

95105
/** Spawn a thread that will redirect a given stream to a file */

core/src/main/scala/org/apache/spark/deploy/worker/DriverRunner.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ private[deploy] class DriverRunner(
8585
}
8686

8787
// TODO: If we add ability to submit multiple jars they should also be added here
88-
val builder = CommandUtils.buildProcessBuilder(driverDesc.command, driverDesc.mem,
89-
sparkHome.getAbsolutePath, substituteVariables)
88+
val builder = CommandUtils.buildProcessBuilder(driverDesc.command, securityManager,
89+
driverDesc.mem, sparkHome.getAbsolutePath, substituteVariables)
9090
launchDriver(builder, driverDir, driverDesc.supervise)
9191
}
9292
catch {

core/src/main/scala/org/apache/spark/deploy/worker/ExecutorRunner.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import akka.actor.ActorRef
2525
import com.google.common.base.Charsets.UTF_8
2626
import com.google.common.io.Files
2727

28-
import org.apache.spark.{SparkConf, Logging}
28+
import org.apache.spark.{SecurityManager, SparkConf, Logging}
2929
import org.apache.spark.deploy.{ApplicationDescription, ExecutorState}
3030
import org.apache.spark.deploy.DeployMessages.ExecutorStateChanged
3131
import org.apache.spark.util.Utils
@@ -125,8 +125,8 @@ private[deploy] class ExecutorRunner(
125125
private def fetchAndRunExecutor() {
126126
try {
127127
// Launch the process
128-
val builder = CommandUtils.buildProcessBuilder(appDesc.command, memory,
129-
sparkHome.getAbsolutePath, substituteVariables)
128+
val builder = CommandUtils.buildProcessBuilder(appDesc.command, new SecurityManager(conf),
129+
memory, sparkHome.getAbsolutePath, substituteVariables)
130130
val command = builder.command()
131131
logInfo("Launch command: " + command.mkString("\"", "\" \"", "\""))
132132

core/src/test/scala/org/apache/spark/deploy/worker/CommandUtilsSuite.scala

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,52 @@
1717

1818
package org.apache.spark.deploy.worker
1919

20-
import org.apache.spark.SparkFunSuite
20+
import org.apache.spark.{SecurityManager, SparkConf, SparkFunSuite}
2121
import org.apache.spark.deploy.Command
2222
import org.apache.spark.util.Utils
23-
import org.scalatest.Matchers
23+
import org.scalatest.{Matchers, PrivateMethodTester}
2424

25-
class CommandUtilsSuite extends SparkFunSuite with Matchers {
25+
class CommandUtilsSuite extends SparkFunSuite with Matchers with PrivateMethodTester {
2626

2727
test("set libraryPath correctly") {
2828
val appId = "12345-worker321-9876"
2929
val sparkHome = sys.props.getOrElse("spark.test.home", fail("spark.test.home is not set!"))
3030
val cmd = new Command("mainClass", Seq(), Map(), Seq(), Seq("libraryPathToB"), Seq())
31-
val builder = CommandUtils.buildProcessBuilder(cmd, 512, sparkHome, t => t)
31+
val builder = CommandUtils.buildProcessBuilder(
32+
cmd, new SecurityManager(new SparkConf), 512, sparkHome, t => t)
3233
val libraryPath = Utils.libraryPathEnvName
3334
val env = builder.environment
3435
env.keySet should contain(libraryPath)
3536
assert(env.get(libraryPath).startsWith("libraryPathToB"))
3637
}
38+
39+
test("auth secret shouldn't appear in java opts") {
40+
val buildLocalCommand = PrivateMethod[Command]('buildLocalCommand)
41+
val conf = new SparkConf
42+
val secret = "This is the secret sauce"
43+
// set auth secret
44+
conf.set(SecurityManager.SPARK_AUTH_SECRET_CONF, secret)
45+
val command = new Command("mainClass", Seq(), Map(), Seq(), Seq("lib"),
46+
Seq("-D" + SecurityManager.SPARK_AUTH_SECRET_CONF + "=" + secret))
47+
48+
// auth is not set
49+
var cmd = CommandUtils invokePrivate buildLocalCommand(
50+
command, new SecurityManager(conf), (t: String) => t, Seq(), Map())
51+
assert(!cmd.javaOpts.exists(_.startsWith("-D" + SecurityManager.SPARK_AUTH_SECRET_CONF)))
52+
assert(!cmd.environment.contains(SecurityManager.ENV_AUTH_SECRET))
53+
54+
// auth is set to false
55+
conf.set(SecurityManager.SPARK_AUTH_CONF, "false")
56+
cmd = CommandUtils invokePrivate buildLocalCommand(
57+
command, new SecurityManager(conf), (t: String) => t, Seq(), Map())
58+
assert(!cmd.javaOpts.exists(_.startsWith("-D" + SecurityManager.SPARK_AUTH_SECRET_CONF)))
59+
assert(!cmd.environment.contains(SecurityManager.ENV_AUTH_SECRET))
60+
61+
// auth is set to true
62+
conf.set(SecurityManager.SPARK_AUTH_CONF, "true")
63+
cmd = CommandUtils invokePrivate buildLocalCommand(
64+
command, new SecurityManager(conf), (t: String) => t, Seq(), Map())
65+
assert(!cmd.javaOpts.exists(_.startsWith("-D" + SecurityManager.SPARK_AUTH_SECRET_CONF)))
66+
assert(cmd.environment(SecurityManager.ENV_AUTH_SECRET) === secret)
67+
}
3768
}

core/src/test/scala/org/apache/spark/deploy/worker/ExecutorRunnerTest.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,20 @@ import java.io.File
2222
import scala.collection.JavaConversions._
2323

2424
import org.apache.spark.deploy.{ApplicationDescription, Command, ExecutorState}
25-
import org.apache.spark.{SparkConf, SparkFunSuite}
25+
import org.apache.spark.{SecurityManager, SparkConf, SparkFunSuite}
2626

2727
class ExecutorRunnerTest extends SparkFunSuite {
2828
test("command includes appId") {
2929
val appId = "12345-worker321-9876"
30+
val conf = new SparkConf
3031
val sparkHome = sys.props.getOrElse("spark.test.home", fail("spark.test.home is not set!"))
3132
val appDesc = new ApplicationDescription("app name", Some(8), 500,
3233
Command("foo", Seq(appId), Map(), Seq(), Seq(), Seq()), "appUiUrl")
3334
val er = new ExecutorRunner(appId, 1, appDesc, 8, 500, null, "blah", "worker321", 123,
34-
"publicAddr", new File(sparkHome), new File("ooga"), "blah", new SparkConf, Seq("localDir"),
35+
"publicAddr", new File(sparkHome), new File("ooga"), "blah", conf, Seq("localDir"),
3536
ExecutorState.RUNNING)
3637
val builder = CommandUtils.buildProcessBuilder(
37-
appDesc.command, 512, sparkHome, er.substituteVariables)
38+
appDesc.command, new SecurityManager(conf), 512, sparkHome, er.substituteVariables)
3839
assert(builder.command().last === appId)
3940
}
4041
}

0 commit comments

Comments
 (0)