Skip to content

Commit 72eb0ac

Browse files
committed
Add modify acls
1 parent 420c1c3 commit 72eb0ac

File tree

6 files changed

+110
-29
lines changed

6 files changed

+110
-29
lines changed

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

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,15 @@ import org.apache.spark.deploy.SparkHadoopUtil
4141
* secure the UI if it has data that other users should not be allowed to see. The javax
4242
* servlet filter specified by the user can authenticate the user and then once the user
4343
* is logged in, Spark can compare that user versus the view acls to make sure they are
44-
* authorized to view the UI. The configs 'spark.ui.acls.enable' and 'spark.ui.view.acls'
44+
* authorized to view the UI. The configs 'spark.acls.enable' and 'spark.ui.view.acls'
4545
* control the behavior of the acls. Note that the person who started the application
4646
* always has view access to the UI.
4747
*
48+
* Spark has a set of modify acls that controls which users have permission to modify a single
49+
* application. This would include things like killing the application. By default the person who
50+
* started the application has modify access. For modify access through the UI, you must have a
51+
* filter that does authentication in place for the modify acls to work properly.
52+
*
4853
* Spark does not currently support encryption after authentication.
4954
*
5055
* At this point spark has multiple communication protocols that need to be secured and
@@ -137,18 +142,27 @@ private[spark] class SecurityManager(sparkConf: SparkConf) extends Logging {
137142
private val sparkSecretLookupKey = "sparkCookie"
138143

139144
private val authOn = sparkConf.getBoolean("spark.authenticate", false)
140-
private var uiAclsOn = sparkConf.getBoolean("spark.ui.acls.enable", false)
145+
// keep spark.ui.acls.enable for backwards compatibility with 1.0
146+
private var aclsOn = sparkConf.getOption("spark.acls.enable").getOrElse(
147+
sparkConf.get("spark.ui.acls.enable", "false")).toBoolean
141148

142149
private var viewAcls: Set[String] = _
150+
151+
// list of users who have permission to modify the application. This should
152+
// apply to both UI and CLI for things like killing the application.
153+
private var modifyAcls: Set[String] = _
154+
143155
// always add the current user and SPARK_USER to the viewAcls
144156
private val defaultAclUsers = Seq[String](System.getProperty("user.name", ""),
145157
Option(System.getenv("SPARK_USER")).getOrElse(""))
146158
setViewAcls(defaultAclUsers, sparkConf.get("spark.ui.view.acls", ""))
159+
setModifyAcls(defaultAclUsers, sparkConf.get("spark.modify.acls", ""))
147160

148161
private val secretKey = generateSecretKey()
149162
logInfo("SecurityManager: authentication " + (if (authOn) "enabled" else "disabled") +
150-
"; ui acls " + (if (uiAclsOn) "enabled" else "disabled") +
151-
"; users with view permissions: " + viewAcls.toString())
163+
"; ui acls " + (if (aclsOn) "enabled" else "disabled") +
164+
"; users with view permissions: " + viewAcls.toString() +
165+
"; users with modify permissions: " + modifyAcls.toString())
152166

153167
// Set our own authenticator to properly negotiate user/password for HTTP connections.
154168
// This is needed by the HTTP client fetching from the HttpServer. Put here so its
@@ -170,17 +184,26 @@ private[spark] class SecurityManager(sparkConf: SparkConf) extends Logging {
170184
}
171185

172186
private[spark] def setViewAcls(defaultUsers: Seq[String], allowedUsers: String) {
173-
viewAcls = (defaultUsers ++ allowedUsers.split(',')).map(_.trim()).filter(!_.isEmpty).toSet
187+
viewAcls = (defaultUsers ++ allowedUsers.split(',')).map(_.trim()).filter(!_.isEmpty).toSet
174188
logInfo("Changing view acls to: " + viewAcls.mkString(","))
175189
}
176190

177191
private[spark] def setViewAcls(defaultUser: String, allowedUsers: String) {
178192
setViewAcls(Seq[String](defaultUser), allowedUsers)
179193
}
180194

181-
private[spark] def setUIAcls(aclSetting: Boolean) {
182-
uiAclsOn = aclSetting
183-
logInfo("Changing acls enabled to: " + uiAclsOn)
195+
private[spark] def getViewAcls: String = viewAcls.mkString(",")
196+
197+
private[spark] def setModifyAcls(defaultUsers: Seq[String], allowedUsers: String) {
198+
modifyAcls = (defaultUsers ++ allowedUsers.split(',')).map(_.trim()).filter(!_.isEmpty).toSet
199+
logInfo("Changing modify acls to: " + modifyAcls.mkString(","))
200+
}
201+
202+
private[spark] def getModifyAcls: String = modifyAcls.mkString(",")
203+
204+
private[spark] def setAcls(aclSetting: Boolean) {
205+
aclsOn = aclSetting
206+
logInfo("Changing acls enabled to: " + aclsOn)
184207
}
185208

186209
/**
@@ -224,22 +247,37 @@ private[spark] class SecurityManager(sparkConf: SparkConf) extends Logging {
224247
* Check to see if Acls for the UI are enabled
225248
* @return true if UI authentication is enabled, otherwise false
226249
*/
227-
def uiAclsEnabled(): Boolean = uiAclsOn
250+
def aclsEnabled(): Boolean = aclsOn
228251

229252
/**
230253
* Checks the given user against the view acl list to see if they have
231-
* authorization to view the UI. If the UI acls must are disabled
232-
* via spark.ui.acls.enable, all users have view access.
254+
* authorization to view the UI. If the UI acls are disabled
255+
* via spark.acls.enable, all users have view access.
233256
*
234257
* @param user to see if is authorized
235258
* @return true is the user has permission, otherwise false
236259
*/
237260
def checkUIViewPermissions(user: String): Boolean = {
238-
logDebug("user=" + user + " uiAclsEnabled=" + uiAclsEnabled() + " viewAcls=" +
261+
logDebug("user=" + user + " aclsEnabled=" + aclsEnabled() + " viewAcls=" +
239262
viewAcls.mkString(","))
240-
if (uiAclsEnabled() && (user != null) && (!viewAcls.contains(user))) false else true
263+
if (aclsEnabled() && (user != null) && (!viewAcls.contains(user))) false else true
241264
}
242265

266+
/**
267+
* Checks the given user against the modify acl list to see if they have
268+
* authorization to modify the application. If the UI acls are disabled
269+
* via spark.acls.enable, all users have modify access.
270+
*
271+
* @param user to see if is authorized
272+
* @return true is the user has permission, otherwise false
273+
*/
274+
def checkModifyPermissions(user: String): Boolean = {
275+
logDebug("user=" + user + " aclsEnabled=" + aclsEnabled() + " modifyAcls=" +
276+
modifyAcls.mkString(","))
277+
if (aclsEnabled() && (user != null) && (!modifyAcls.contains(user))) false else true
278+
}
279+
280+
243281
/**
244282
* Check to see if authentication for the Spark communication protocols is enabled
245283
* @return true if authentication is enabled, otherwise false

core/src/main/scala/org/apache/spark/ui/jobs/JobProgressTab.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ private[ui] class JobProgressTab(parent: SparkUI) extends WebUITab(parent, "stag
4141
def isFairScheduler = listener.schedulingMode.exists(_ == SchedulingMode.FAIR)
4242

4343
def handleKillRequest(request: HttpServletRequest) = {
44-
if (killEnabled) {
44+
if ((killEnabled) && (parent.securityManager.checkModifyPermissions(request.getRemoteUser))) {
4545
val killFlag = Option(request.getParameter("terminate")).getOrElse("false").toBoolean
4646
val stageId = Option(request.getParameter("id")).getOrElse("-1").toInt
4747
if (stageId >= 0 && killFlag && listener.activeStages.contains(stageId)) {

core/src/test/scala/org/apache/spark/SecurityManagerSuite.scala

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class SecurityManagerSuite extends FunSuite {
3131
conf.set("spark.ui.view.acls", "user1,user2")
3232
val securityManager = new SecurityManager(conf);
3333
assert(securityManager.isAuthenticationEnabled() === true)
34-
assert(securityManager.uiAclsEnabled() === true)
34+
assert(securityManager.aclsEnabled() === true)
3535
assert(securityManager.checkUIViewPermissions("user1") === true)
3636
assert(securityManager.checkUIViewPermissions("user2") === true)
3737
assert(securityManager.checkUIViewPermissions("user3") === false)
@@ -41,16 +41,16 @@ class SecurityManagerSuite extends FunSuite {
4141
val conf = new SparkConf
4242
conf.set("spark.ui.view.acls", "user1,user2")
4343
val securityManager = new SecurityManager(conf);
44-
securityManager.setUIAcls(true)
45-
assert(securityManager.uiAclsEnabled() === true)
46-
securityManager.setUIAcls(false)
47-
assert(securityManager.uiAclsEnabled() === false)
44+
securityManager.setAcls(true)
45+
assert(securityManager.aclsEnabled() === true)
46+
securityManager.setAcls(false)
47+
assert(securityManager.aclsEnabled() === false)
4848

4949
// acls are off so doesn't matter what view acls set to
5050
assert(securityManager.checkUIViewPermissions("user4") === true)
5151

52-
securityManager.setUIAcls(true)
53-
assert(securityManager.uiAclsEnabled() === true)
52+
securityManager.setAcls(true)
53+
assert(securityManager.aclsEnabled() === true)
5454
securityManager.setViewAcls(ArrayBuffer[String]("user5"), "user6,user7")
5555
assert(securityManager.checkUIViewPermissions("user1") === false)
5656
assert(securityManager.checkUIViewPermissions("user5") === true)
@@ -59,5 +59,30 @@ class SecurityManagerSuite extends FunSuite {
5959
assert(securityManager.checkUIViewPermissions("user8") === false)
6060
assert(securityManager.checkUIViewPermissions(null) === true)
6161
}
62+
63+
test("set security modify acls") {
64+
val conf = new SparkConf
65+
conf.set("spark.modify.acls", "user1,user2")
66+
val securityManager = new SecurityManager(conf);
67+
securityManager.setAcls(true)
68+
assert(securityManager.aclsEnabled() === true)
69+
securityManager.setAcls(false)
70+
assert(securityManager.aclsEnabled() === false)
71+
72+
// acls are off so doesn't matter what view acls set to
73+
assert(securityManager.checkModifyPermissions("user4") === true)
74+
75+
securityManager.setAcls(true)
76+
assert(securityManager.aclsEnabled() === true)
77+
securityManager.setModifyAcls(ArrayBuffer[String]("user5"), "user6,user7")
78+
assert(securityManager.checkModifyPermissions("user1") === false)
79+
assert(securityManager.checkModifyPermissions("user5") === true)
80+
assert(securityManager.checkModifyPermissions("user6") === true)
81+
assert(securityManager.checkModifyPermissions("user7") === true)
82+
assert(securityManager.checkModifyPermissions("user8") === false)
83+
assert(securityManager.checkModifyPermissions(null) === true)
84+
}
85+
86+
6287
}
6388

docs/configuration.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -743,13 +743,13 @@ Apart from these, the following properties are also available, and may be useful
743743
</td>
744744
</tr>
745745
<tr>
746-
<td><code>spark.ui.acls.enable</code></td>
746+
<td><code>spark.acls.enable</code></td>
747747
<td>false</td>
748748
<td>
749-
Whether Spark web ui acls should are enabled. If enabled, this checks to see if the user has
750-
access permissions to view the web ui. See <code>spark.ui.view.acls</code> for more details.
751-
Also note this requires the user to be known, if the user comes across as null no checks
752-
are done. Filters can be used to authenticate and set the user.
749+
Whether Spark acls should are enabled. If enabled, this checks to see if the user has
750+
access permissions to view or modify the job. Note this requires the user to be known,
751+
so if the user comes across as null no checks are done. Filters can be used with the UI
752+
to authenticate and set the user.
753753
</td>
754754
</tr>
755755
<tr>
@@ -760,6 +760,14 @@ Apart from these, the following properties are also available, and may be useful
760760
user that started the Spark job has view access.
761761
</td>
762762
</tr>
763+
<tr>
764+
<td><code>spark.modify.acls</code></td>
765+
<td>Empty</td>
766+
<td>
767+
Comma separated list of users that have modify access to the Spark job. By default only the
768+
user that started the Spark job has access to modify it (kill it for example).
769+
</td>
770+
</tr>
763771
</table>
764772

765773
#### Spark Streaming

docs/security.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ Spark currently supports authentication via a shared secret. Authentication can
88
* For Spark on [YARN](running-on-yarn.html) deployments, configuring `spark.authenticate` to `true` will automatically handle generating and distributing the shared secret. Each application will use a unique shared secret.
99
* For other types of Spark deployments, the Spark parameter `spark.authenticate.secret` should be configured on each of the nodes. This secret will be used by all the Master/Workers and applications.
1010

11-
The Spark UI can also be secured by using [javax servlet filters](http://docs.oracle.com/javaee/6/api/javax/servlet/Filter.html) via the `spark.ui.filters` setting. A user may want to 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` control the behavior of the ACLs. Note that the user who started the application always has view access to the UI.
12-
On YARN, the Spark UI uses the standard YARN web application proxy mechanism and will authenticate via any installed Hadoop filters.
11+
The Spark UI can also be secured by using [javax servlet filters](http://docs.oracle.com/javaee/6/api/javax/servlet/Filter.html) via the `spark.ui.filters` setting. A user may want to 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.acls.enable` and `spark.ui.view.acls` control the behavior of the ACLs. Note that the user who started the application always has view access to the UI. On YARN, the Spark UI uses the standard YARN web application proxy mechanism and will authenticate via any installed Hadoop filters.
12+
13+
Spark also supports modify ACLs to control who has access to modify a running Spark application. This includes things like killing the application or a task. This is controlled by the configs `spark.acls.enable` and `spark.modify.acls`.
1314

1415
If your applications are using event logging, the directory where the event logs go (`spark.eventLog.dir`) should be manually created and have the proper permissions set on it. If you want those log files secured, the permissions should be set to `drwxrwxrwxt` for that directory. The owner of the directory should be the super user who is running the history server and the group permissions should be restricted to super user group. This will allow all users to write to the directory but will prevent unprivileged users from removing or renaming a file unless they own the file or directory. The event log files will be created by Spark with permissions such that only the user and group have read and write access.
1516

yarn/common/src/main/scala/org/apache/spark/deploy/yarn/ClientBase.scala

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ import org.apache.hadoop.yarn.api.protocolrecords._
3737
import org.apache.hadoop.yarn.api.records._
3838
import org.apache.hadoop.yarn.conf.YarnConfiguration
3939
import org.apache.hadoop.yarn.util.Records
40-
import org.apache.spark.{SparkException, Logging, SparkConf, SparkContext}
40+
import org.apache.spark._
41+
import scala.util.Failure
42+
import scala.Some
43+
import scala.util.Success
4144

4245
/**
4346
* The entry point (starting in Client#main() and Client#run()) for launching Spark on YARN. The
@@ -416,6 +419,12 @@ trait ClientBase extends Logging {
416419
amContainer.setCommands(printableCommands)
417420

418421
setupSecurityToken(amContainer)
422+
423+
val securityManager = new SecurityManager(sparkConf)
424+
val acls = Map[ApplicationAccessType, String] (
425+
ApplicationAccessType.VIEW_APP -> securityManager.getViewAcls,
426+
ApplicationAccessType.MODIFY_APP -> securityManager.getModifyAcls)
427+
amContainer.setApplicationACLs(acls)
419428
amContainer
420429
}
421430
}

0 commit comments

Comments
 (0)