aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorThomas Graves <tgraves@apache.org>2014-08-05 12:52:52 -0500
committerThomas Graves <tgraves@apache.org>2014-08-05 12:52:52 -0500
commit1c5555a23d3aa40423d658cfbf2c956ad415a6b1 (patch)
treeb66cee1204610fca7374300b5229520613a1474b /core
parent2c0f705e26ca3dfc43a1e9a0722c0e57f67c970a (diff)
downloadspark-1c5555a23d3aa40423d658cfbf2c956ad415a6b1.tar.gz
spark-1c5555a23d3aa40423d658cfbf2c956ad415a6b1.tar.bz2
spark-1c5555a23d3aa40423d658cfbf2c956ad415a6b1.zip
SPARK-1890 and SPARK-1891- add admin and modify acls
It was easier to combine these 2 jira since they touch many of the same places. This pr adds the following: - adds modify acls - adds admin acls (list of admins/users that get added to both view and modify acls) - modify Kill button on UI to take modify acls into account - changes config name of spark.ui.acls.enable to spark.acls.enable since I choose poorly in original name. We keep backwards compatibility so people can still use spark.ui.acls.enable. The acls should apply to any web ui as well as any CLI interfaces. - send view and modify acls information on to YARN so that YARN interfaces can use (yarn cli for killing applications for example). Author: Thomas Graves <tgraves@apache.org> Closes #1196 from tgravescs/SPARK-1890 and squashes the following commits: 8292eb1 [Thomas Graves] review comments b92ec89 [Thomas Graves] remove unneeded variable from applistener 4c765f4 [Thomas Graves] Add in admin acls 72eb0ac [Thomas Graves] Add modify acls
Diffstat (limited to 'core')
-rw-r--r--core/src/main/scala/org/apache/spark/SecurityManager.scala107
-rw-r--r--core/src/main/scala/org/apache/spark/deploy/history/FsHistoryProvider.scala4
-rw-r--r--core/src/main/scala/org/apache/spark/scheduler/ApplicationEventListener.scala4
-rw-r--r--core/src/main/scala/org/apache/spark/ui/jobs/JobProgressTab.scala2
-rw-r--r--core/src/test/scala/org/apache/spark/SecurityManagerSuite.scala83
5 files changed, 171 insertions, 29 deletions
diff --git a/core/src/main/scala/org/apache/spark/SecurityManager.scala b/core/src/main/scala/org/apache/spark/SecurityManager.scala
index 74aa441619..25c2c9fc6a 100644
--- a/core/src/main/scala/org/apache/spark/SecurityManager.scala
+++ b/core/src/main/scala/org/apache/spark/SecurityManager.scala
@@ -41,10 +41,19 @@ import org.apache.spark.deploy.SparkHadoopUtil
* secure the UI if it has data that other users should not be allowed to see. The javax
* servlet filter specified by the user can authenticate the user and then once the user
* is logged in, Spark can compare that user versus the view acls to make sure they are
- * authorized to view the UI. The configs 'spark.ui.acls.enable' and 'spark.ui.view.acls'
+ * authorized to view the UI. The configs 'spark.acls.enable' and 'spark.ui.view.acls'
* 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 (`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.
*
* At this point spark has multiple communication protocols that need to be secured and
@@ -137,18 +146,32 @@ private[spark] class SecurityManager(sparkConf: SparkConf) extends Logging {
private val sparkSecretLookupKey = "sparkCookie"
private val authOn = sparkConf.getBoolean("spark.authenticate", false)
- private var uiAclsOn = sparkConf.getBoolean("spark.ui.acls.enable", false)
+ // keep spark.ui.acls.enable for backwards compatibility with 1.0
+ 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
+ // apply to both UI and CLI for things like killing the application.
+ 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", ""))
private val secretKey = generateSecretKey()
logInfo("SecurityManager: authentication " + (if (authOn) "enabled" else "disabled") +
- "; ui acls " + (if (uiAclsOn) "enabled" else "disabled") +
- "; users with view permissions: " + viewAcls.toString())
+ "; ui acls " + (if (aclsOn) "enabled" else "disabled") +
+ "; users with view permissions: " + viewAcls.toString() +
+ "; users with modify permissions: " + modifyAcls.toString())
// Set our own authenticator to properly negotiate user/password for HTTP connections.
// This is needed by the HTTP client fetching from the HttpServer. Put here so its
@@ -169,18 +192,51 @@ 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
+ }
+
+ /**
+ * 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.
+ */
+ 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)
+ def setViewAcls(defaultUser: String, allowedUsers: String) {
+ setViewAcls(Set[String](defaultUser), allowedUsers)
+ }
+
+ def getViewAcls: String = viewAcls.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.
+ */
+ def setModifyAcls(defaultUsers: Set[String], allowedUsers: String) {
+ modifyAcls = (adminAcls ++ defaultUsers ++ stringToSet(allowedUsers))
+ logInfo("Changing modify acls to: " + modifyAcls.mkString(","))
+ }
+
+ 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.
+ */
+ def setAdminAcls(adminUsers: String) {
+ adminAcls = stringToSet(adminUsers)
+ logInfo("Changing admin acls to: " + adminAcls.mkString(","))
}
- private[spark] def setUIAcls(aclSetting: Boolean) {
- uiAclsOn = aclSetting
- logInfo("Changing acls enabled to: " + uiAclsOn)
+ def setAcls(aclSetting: Boolean) {
+ aclsOn = aclSetting
+ logInfo("Changing acls enabled to: " + aclsOn)
}
/**
@@ -224,23 +280,40 @@ private[spark] class SecurityManager(sparkConf: SparkConf) extends Logging {
* Check to see if Acls for the UI are enabled
* @return true if UI authentication is enabled, otherwise false
*/
- def uiAclsEnabled(): Boolean = uiAclsOn
+ def aclsEnabled(): Boolean = aclsOn
/**
* Checks the given user against the view acl list to see if they have
- * authorization to view the UI. If the UI acls must are disabled
- * via spark.ui.acls.enable, all users have view access.
+ * authorization to view the UI. If the UI acls are disabled
+ * via spark.acls.enable, all users have view access. If the user is null
+ * it is assumed authentication is off and all users have access.
*
* @param user to see if is authorized
* @return true is the user has permission, otherwise false
*/
def checkUIViewPermissions(user: String): Boolean = {
- logDebug("user=" + user + " uiAclsEnabled=" + uiAclsEnabled() + " viewAcls=" +
+ logDebug("user=" + user + " aclsEnabled=" + aclsEnabled() + " viewAcls=" +
viewAcls.mkString(","))
- if (uiAclsEnabled() && (user != null) && (!viewAcls.contains(user))) false else true
+ if (aclsEnabled() && (user != null) && (!viewAcls.contains(user))) false else true
}
/**
+ * 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. 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
+ */
+ def checkModifyPermissions(user: String): Boolean = {
+ logDebug("user=" + user + " aclsEnabled=" + aclsEnabled() + " modifyAcls=" +
+ modifyAcls.mkString(","))
+ if (aclsEnabled() && (user != null) && (!modifyAcls.contains(user))) false else true
+ }
+
+
+ /**
* Check to see if authentication for the Spark communication protocols is enabled
* @return true if authentication is enabled, 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 6d2d4cef1e..cc06540ee0 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
@@ -189,7 +189,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 cd5d44ad4a..162158babc 100644
--- a/core/src/main/scala/org/apache/spark/scheduler/ApplicationEventListener.scala
+++ b/core/src/main/scala/org/apache/spark/scheduler/ApplicationEventListener.scala
@@ -29,7 +29,7 @@ private[spark] class ApplicationEventListener extends SparkListener {
var startTime = -1L
var endTime = -1L
var viewAcls = ""
- var enableViewAcls = false
+ var adminAcls = ""
def applicationStarted = startTime != -1
@@ -55,7 +55,7 @@ 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", "")
}
}
}
diff --git a/core/src/main/scala/org/apache/spark/ui/jobs/JobProgressTab.scala b/core/src/main/scala/org/apache/spark/ui/jobs/JobProgressTab.scala
index 3308c8c8a3..8a01ec80c9 100644
--- a/core/src/main/scala/org/apache/spark/ui/jobs/JobProgressTab.scala
+++ b/core/src/main/scala/org/apache/spark/ui/jobs/JobProgressTab.scala
@@ -41,7 +41,7 @@ private[ui] class JobProgressTab(parent: SparkUI) extends WebUITab(parent, "stag
def isFairScheduler = listener.schedulingMode.exists(_ == SchedulingMode.FAIR)
def handleKillRequest(request: HttpServletRequest) = {
- if (killEnabled) {
+ if ((killEnabled) && (parent.securityManager.checkModifyPermissions(request.getRemoteUser))) {
val killFlag = Option(request.getParameter("terminate")).getOrElse("false").toBoolean
val stageId = Option(request.getParameter("id")).getOrElse("-1").toInt
if (stageId >= 0 && killFlag && listener.activeStages.contains(stageId)) {
diff --git a/core/src/test/scala/org/apache/spark/SecurityManagerSuite.scala b/core/src/test/scala/org/apache/spark/SecurityManagerSuite.scala
index e39093e24d..fcca0867b8 100644
--- a/core/src/test/scala/org/apache/spark/SecurityManagerSuite.scala
+++ b/core/src/test/scala/org/apache/spark/SecurityManagerSuite.scala
@@ -31,7 +31,7 @@ class SecurityManagerSuite extends FunSuite {
conf.set("spark.ui.view.acls", "user1,user2")
val securityManager = new SecurityManager(conf);
assert(securityManager.isAuthenticationEnabled() === true)
- assert(securityManager.uiAclsEnabled() === true)
+ assert(securityManager.aclsEnabled() === true)
assert(securityManager.checkUIViewPermissions("user1") === true)
assert(securityManager.checkUIViewPermissions("user2") === true)
assert(securityManager.checkUIViewPermissions("user3") === false)
@@ -41,17 +41,17 @@ class SecurityManagerSuite extends FunSuite {
val conf = new SparkConf
conf.set("spark.ui.view.acls", "user1,user2")
val securityManager = new SecurityManager(conf);
- securityManager.setUIAcls(true)
- assert(securityManager.uiAclsEnabled() === true)
- securityManager.setUIAcls(false)
- assert(securityManager.uiAclsEnabled() === false)
+ securityManager.setAcls(true)
+ assert(securityManager.aclsEnabled() === true)
+ securityManager.setAcls(false)
+ assert(securityManager.aclsEnabled() === false)
// acls are off so doesn't matter what view acls set to
assert(securityManager.checkUIViewPermissions("user4") === true)
- securityManager.setUIAcls(true)
- assert(securityManager.uiAclsEnabled() === true)
- securityManager.setViewAcls(ArrayBuffer[String]("user5"), "user6,user7")
+ securityManager.setAcls(true)
+ assert(securityManager.aclsEnabled() === true)
+ securityManager.setViewAcls(Set[String]("user5"), "user6,user7")
assert(securityManager.checkUIViewPermissions("user1") === false)
assert(securityManager.checkUIViewPermissions("user5") === true)
assert(securityManager.checkUIViewPermissions("user6") === true)
@@ -59,5 +59,72 @@ class SecurityManagerSuite extends FunSuite {
assert(securityManager.checkUIViewPermissions("user8") === false)
assert(securityManager.checkUIViewPermissions(null) === true)
}
+
+ 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)
+ securityManager.setAcls(false)
+ assert(securityManager.aclsEnabled() === false)
+
+ // acls are off so doesn't matter what view acls set to
+ assert(securityManager.checkModifyPermissions("user4") === true)
+
+ securityManager.setAcls(true)
+ assert(securityManager.aclsEnabled() === true)
+ securityManager.setModifyAcls(Set("user5"), "user6,user7")
+ assert(securityManager.checkModifyPermissions("user1") === false)
+ assert(securityManager.checkModifyPermissions("user5") === true)
+ assert(securityManager.checkModifyPermissions("user6") === true)
+ assert(securityManager.checkModifyPermissions("user7") === true)
+ assert(securityManager.checkModifyPermissions("user8") === false)
+ 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)
+
+ }
+
+
}