diff --git a/core/src/main/scala/org/apache/spark/SecurityManager.scala b/core/src/main/scala/org/apache/spark/SecurityManager.scala index 2f05bf9f9a948..04da85067eb93 100644 --- a/core/src/main/scala/org/apache/spark/SecurityManager.scala +++ b/core/src/main/scala/org/apache/spark/SecurityManager.scala @@ -45,10 +45,14 @@ import org.apache.spark.deploy.SparkHadoopUtil * control the behavior of the acls. Note that the person who started the application * always has view access to the UI. * - * Spark has a set of modify acls that controls which users have permission to modify a single - * application. This would include things like killing the application. By default the person who - * started the application has modify access. For modify access through the UI, you must have a - * filter that does authentication in place for the modify acls to work properly. + * Spark has a set of modify acls (`spark.modify.acls`) that controls which users have permission + * to modify a single application. This would include things like killing the application. By + * default the person who started the application has modify access. For modify access through + * the UI, you must have a filter that does authentication in place for the modify acls to work + * properly. + * + * Spark also has a set of admin acls (`spark.admin.acls`) which is a set of users/administrators + * who always have permission to view or modify the Spark application. * * Spark does not currently support encryption after authentication. * @@ -146,6 +150,10 @@ private[spark] class SecurityManager(sparkConf: SparkConf) extends Logging { private var aclsOn = sparkConf.getOption("spark.acls.enable").getOrElse( sparkConf.get("spark.ui.acls.enable", "false")).toBoolean + // admin acls should be set before view or modify acls + private var adminAcls: Set[String] = + stringToSet(sparkConf.get("spark.admin.acls", "")) + private var viewAcls: Set[String] = _ // list of users who have permission to modify the application. This should @@ -153,8 +161,9 @@ private[spark] class SecurityManager(sparkConf: SparkConf) extends Logging { private var modifyAcls: Set[String] = _ // always add the current user and SPARK_USER to the viewAcls - private val defaultAclUsers = Seq[String](System.getProperty("user.name", ""), + private val defaultAclUsers = Set[String](System.getProperty("user.name", ""), Option(System.getenv("SPARK_USER")).getOrElse("")) + setViewAcls(defaultAclUsers, sparkConf.get("spark.ui.view.acls", "")) setModifyAcls(defaultAclUsers, sparkConf.get("spark.modify.acls", "")) @@ -183,24 +192,40 @@ private[spark] class SecurityManager(sparkConf: SparkConf) extends Logging { ) } - private[spark] def setViewAcls(defaultUsers: Seq[String], allowedUsers: String) { - viewAcls = (defaultUsers ++ allowedUsers.split(',')).map(_.trim()).filter(!_.isEmpty).toSet + /** + * Split a comma separated String, filter out any empty items, and return a Set of strings + */ + private def stringToSet(list: String): Set[String] = { + (list.split(',')).map(_.trim()).filter(!_.isEmpty).toSet + } + + private[spark] def setViewAcls(defaultUsers: Set[String], allowedUsers: String) { + viewAcls = (adminAcls ++ defaultUsers ++ stringToSet(allowedUsers)) logInfo("Changing view acls to: " + viewAcls.mkString(",")) } private[spark] def setViewAcls(defaultUser: String, allowedUsers: String) { - setViewAcls(Seq[String](defaultUser), allowedUsers) + setViewAcls(Set[String](defaultUser), allowedUsers) } private[spark] def getViewAcls: String = viewAcls.mkString(",") - private[spark] def setModifyAcls(defaultUsers: Seq[String], allowedUsers: String) { - modifyAcls = (defaultUsers ++ allowedUsers.split(',')).map(_.trim()).filter(!_.isEmpty).toSet + private[spark] def setModifyAcls(defaultUsers: Set[String], allowedUsers: String) { + modifyAcls = (adminAcls ++ defaultUsers ++ stringToSet(allowedUsers)) logInfo("Changing modify acls to: " + modifyAcls.mkString(",")) } private[spark] def getModifyAcls: String = modifyAcls.mkString(",") + /** + * Admin acls should be set before the view or modify acls. If you modify the admin + * acls you should also set the view and modify acls again to pick up the changes. + */ + private[spark] def setAdminAcls(adminUsers: String) { + adminAcls = stringToSet(adminUsers) + logInfo("Changing admin acls to: " + adminAcls.mkString(",")) + } + private[spark] def setAcls(aclSetting: Boolean) { aclsOn = aclSetting logInfo("Changing acls enabled to: " + aclsOn) @@ -252,7 +277,8 @@ private[spark] class SecurityManager(sparkConf: SparkConf) extends Logging { /** * Checks the given user against the view acl list to see if they have * authorization to view the UI. If the UI acls are disabled - * via spark.acls.enable, all users have view access. + * via spark.acls.enable, all users have view access. If the user is null + * it is assumed authentication isn't turned on and all users have access. * * @param user to see if is authorized * @return true is the user has permission, otherwise false @@ -266,7 +292,8 @@ private[spark] class SecurityManager(sparkConf: SparkConf) extends Logging { /** * Checks the given user against the modify acl list to see if they have * authorization to modify the application. If the UI acls are disabled - * via spark.acls.enable, all users have modify access. + * via spark.acls.enable, all users have modify access. If the user is null + * it is assumed authentication isn't turned on and all users have access. * * @param user to see if is authorized * @return true is the user has permission, otherwise false diff --git a/core/src/main/scala/org/apache/spark/deploy/history/FsHistoryProvider.scala b/core/src/main/scala/org/apache/spark/deploy/history/FsHistoryProvider.scala index a8c9ac072449f..2add4ea0f1cc2 100644 --- a/core/src/main/scala/org/apache/spark/deploy/history/FsHistoryProvider.scala +++ b/core/src/main/scala/org/apache/spark/deploy/history/FsHistoryProvider.scala @@ -186,7 +186,9 @@ private[history] class FsHistoryProvider(conf: SparkConf) extends ApplicationHis if (ui != null) { val uiAclsEnabled = conf.getBoolean("spark.history.ui.acls.enable", false) - ui.getSecurityManager.setUIAcls(uiAclsEnabled) + ui.getSecurityManager.setAcls(uiAclsEnabled) + // make sure to set admin acls before view acls so properly picked up + ui.getSecurityManager.setAdminAcls(appListener.adminAcls) ui.getSecurityManager.setViewAcls(appListener.sparkUser, appListener.viewAcls) } (appInfo, ui) diff --git a/core/src/main/scala/org/apache/spark/scheduler/ApplicationEventListener.scala b/core/src/main/scala/org/apache/spark/scheduler/ApplicationEventListener.scala index cd5d44ad4a7e6..aaa9b2192d312 100644 --- a/core/src/main/scala/org/apache/spark/scheduler/ApplicationEventListener.scala +++ b/core/src/main/scala/org/apache/spark/scheduler/ApplicationEventListener.scala @@ -29,6 +29,7 @@ private[spark] class ApplicationEventListener extends SparkListener { var startTime = -1L var endTime = -1L var viewAcls = "" + var adminAcls = "" var enableViewAcls = false def applicationStarted = startTime != -1 @@ -55,7 +56,9 @@ private[spark] class ApplicationEventListener extends SparkListener { val environmentDetails = environmentUpdate.environmentDetails val allProperties = environmentDetails("Spark Properties").toMap viewAcls = allProperties.getOrElse("spark.ui.view.acls", "") - enableViewAcls = allProperties.getOrElse("spark.ui.acls.enable", "false").toBoolean + adminAcls = allProperties.getOrElse("spark.admin.acls", "") + enableViewAcls = allProperties.getOrElse("spark.acls.enable", + allProperties.getOrElse("spark.ui.acls.enable", "false")).toBoolean } } } diff --git a/core/src/test/scala/org/apache/spark/SecurityManagerSuite.scala b/core/src/test/scala/org/apache/spark/SecurityManagerSuite.scala index 8e99998fe9cd9..fcca0867b8072 100644 --- a/core/src/test/scala/org/apache/spark/SecurityManagerSuite.scala +++ b/core/src/test/scala/org/apache/spark/SecurityManagerSuite.scala @@ -51,7 +51,7 @@ class SecurityManagerSuite extends FunSuite { securityManager.setAcls(true) assert(securityManager.aclsEnabled() === true) - securityManager.setViewAcls(ArrayBuffer[String]("user5"), "user6,user7") + securityManager.setViewAcls(Set[String]("user5"), "user6,user7") assert(securityManager.checkUIViewPermissions("user1") === false) assert(securityManager.checkUIViewPermissions("user5") === true) assert(securityManager.checkUIViewPermissions("user6") === true) @@ -63,6 +63,7 @@ class SecurityManagerSuite extends FunSuite { test("set security modify acls") { val conf = new SparkConf conf.set("spark.modify.acls", "user1,user2") + val securityManager = new SecurityManager(conf); securityManager.setAcls(true) assert(securityManager.aclsEnabled() === true) @@ -74,7 +75,7 @@ class SecurityManagerSuite extends FunSuite { securityManager.setAcls(true) assert(securityManager.aclsEnabled() === true) - securityManager.setModifyAcls(ArrayBuffer[String]("user5"), "user6,user7") + securityManager.setModifyAcls(Set("user5"), "user6,user7") assert(securityManager.checkModifyPermissions("user1") === false) assert(securityManager.checkModifyPermissions("user5") === true) assert(securityManager.checkModifyPermissions("user6") === true) @@ -83,6 +84,47 @@ class SecurityManagerSuite extends FunSuite { assert(securityManager.checkModifyPermissions(null) === true) } + test("set security admin acls") { + val conf = new SparkConf + conf.set("spark.admin.acls", "user1,user2") + conf.set("spark.ui.view.acls", "user3") + conf.set("spark.modify.acls", "user4") + + val securityManager = new SecurityManager(conf); + securityManager.setAcls(true) + assert(securityManager.aclsEnabled() === true) + + assert(securityManager.checkModifyPermissions("user1") === true) + assert(securityManager.checkModifyPermissions("user2") === true) + assert(securityManager.checkModifyPermissions("user4") === true) + assert(securityManager.checkModifyPermissions("user3") === false) + assert(securityManager.checkModifyPermissions("user5") === false) + assert(securityManager.checkModifyPermissions(null) === true) + assert(securityManager.checkUIViewPermissions("user1") === true) + assert(securityManager.checkUIViewPermissions("user2") === true) + assert(securityManager.checkUIViewPermissions("user3") === true) + assert(securityManager.checkUIViewPermissions("user4") === false) + assert(securityManager.checkUIViewPermissions("user5") === false) + assert(securityManager.checkUIViewPermissions(null) === true) + + securityManager.setAdminAcls("user6") + securityManager.setViewAcls(Set[String]("user8"), "user9") + securityManager.setModifyAcls(Set("user11"), "user9") + assert(securityManager.checkModifyPermissions("user6") === true) + assert(securityManager.checkModifyPermissions("user11") === true) + assert(securityManager.checkModifyPermissions("user9") === true) + assert(securityManager.checkModifyPermissions("user1") === false) + assert(securityManager.checkModifyPermissions("user4") === false) + assert(securityManager.checkModifyPermissions(null) === true) + assert(securityManager.checkUIViewPermissions("user6") === true) + assert(securityManager.checkUIViewPermissions("user8") === true) + assert(securityManager.checkUIViewPermissions("user9") === true) + assert(securityManager.checkUIViewPermissions("user1") === false) + assert(securityManager.checkUIViewPermissions("user3") === false) + assert(securityManager.checkUIViewPermissions(null) === true) + + } + } diff --git a/docs/configuration.md b/docs/configuration.md index 74e3e9fbfa1f2..3c11e8ee6a075 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -768,6 +768,15 @@ Apart from these, the following properties are also available, and may be useful user that started the Spark job has access to modify it (kill it for example). +
spark.admin.acls