Skip to content

Commit 4a4c3dc

Browse files
jerryshaoTom Graves
authored andcommitted
[SPARK-19033][CORE] Add admin acls for history server
## What changes were proposed in this pull request? Current HistoryServer's ACLs is derived from application event-log, which means the newly changed ACLs cannot be applied to the old data, this will become a problem where newly added admin cannot access the old application history UI, only the new application can be affected. So here propose to add admin ACLs for history server, any configured user/group could have the view access to all the applications, while the view ACLs derived from application run-time still take effect. ## How was this patch tested? Unit test added. Author: jerryshao <[email protected]> Closes #16470 from jerryshao/SPARK-19033.
1 parent 903bb8e commit 4a4c3dc

File tree

3 files changed

+146
-7
lines changed

3 files changed

+146
-7
lines changed

core/src/main/scala/org/apache/spark/deploy/history/FsHistoryProvider.scala

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ private[history] class FsHistoryProvider(conf: SparkConf, clock: Clock)
9797
.map { d => Utils.resolveURI(d).toString }
9898
.getOrElse(DEFAULT_LOG_DIR)
9999

100+
private val HISTORY_UI_ACLS_ENABLE = conf.getBoolean("spark.history.ui.acls.enable", false)
101+
private val HISTORY_UI_ADMIN_ACLS = conf.get("spark.history.ui.admin.acls", "")
102+
private val HISTORY_UI_ADMIN_ACLS_GROUPS = conf.get("spark.history.ui.admin.acls.groups", "")
103+
logInfo(s"History server ui acls " + (if (HISTORY_UI_ACLS_ENABLE) "enabled" else "disabled") +
104+
"; users with admin permissions: " + HISTORY_UI_ADMIN_ACLS.toString +
105+
"; groups with admin permissions" + HISTORY_UI_ADMIN_ACLS_GROUPS.toString)
106+
100107
private val hadoopConf = SparkHadoopUtil.get.newConfiguration(conf)
101108
private val fs = Utils.getHadoopFileSystem(logDir, hadoopConf)
102109

@@ -250,13 +257,14 @@ private[history] class FsHistoryProvider(conf: SparkConf, clock: Clock)
250257
val appListener = replay(fileStatus, isApplicationCompleted(fileStatus), replayBus)
251258

252259
if (appListener.appId.isDefined) {
253-
val uiAclsEnabled = conf.getBoolean("spark.history.ui.acls.enable", false)
254-
ui.getSecurityManager.setAcls(uiAclsEnabled)
260+
ui.getSecurityManager.setAcls(HISTORY_UI_ACLS_ENABLE)
255261
// make sure to set admin acls before view acls so they are properly picked up
256-
ui.getSecurityManager.setAdminAcls(appListener.adminAcls.getOrElse(""))
257-
ui.getSecurityManager.setViewAcls(attempt.sparkUser,
258-
appListener.viewAcls.getOrElse(""))
259-
ui.getSecurityManager.setAdminAclsGroups(appListener.adminAclsGroups.getOrElse(""))
262+
val adminAcls = HISTORY_UI_ADMIN_ACLS + "," + appListener.adminAcls.getOrElse("")
263+
ui.getSecurityManager.setAdminAcls(adminAcls)
264+
ui.getSecurityManager.setViewAcls(attempt.sparkUser, appListener.viewAcls.getOrElse(""))
265+
val adminAclsGroups = HISTORY_UI_ADMIN_ACLS_GROUPS + "," +
266+
appListener.adminAclsGroups.getOrElse("")
267+
ui.getSecurityManager.setAdminAclsGroups(adminAclsGroups)
260268
ui.getSecurityManager.setViewAclsGroups(appListener.viewAclsGroups.getOrElse(""))
261269
Some(LoadedAppUI(ui, updateProbe(appId, attemptId, attempt.fileSize)))
262270
} else {

core/src/test/scala/org/apache/spark/deploy/history/FsHistoryProviderSuite.scala

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@ import org.scalatest.BeforeAndAfter
3535
import org.scalatest.Matchers
3636
import org.scalatest.concurrent.Eventually._
3737

38-
import org.apache.spark.{SparkConf, SparkFunSuite}
38+
import org.apache.spark.{SecurityManager, SparkConf, SparkFunSuite}
3939
import org.apache.spark.internal.Logging
4040
import org.apache.spark.io._
4141
import org.apache.spark.scheduler._
42+
import org.apache.spark.security.GroupMappingServiceProvider
4243
import org.apache.spark.util.{Clock, JsonProtocol, ManualClock, Utils}
4344

4445
class FsHistoryProviderSuite extends SparkFunSuite with BeforeAndAfter with Matchers with Logging {
@@ -474,6 +475,102 @@ class FsHistoryProviderSuite extends SparkFunSuite with BeforeAndAfter with Matc
474475
}
475476
}
476477

478+
test("support history server ui admin acls") {
479+
def createAndCheck(conf: SparkConf, properties: (String, String)*)
480+
(checkFn: SecurityManager => Unit): Unit = {
481+
// Empty the testDir for each test.
482+
if (testDir.exists() && testDir.isDirectory) {
483+
testDir.listFiles().foreach { f => if (f.isFile) f.delete() }
484+
}
485+
486+
var provider: FsHistoryProvider = null
487+
try {
488+
provider = new FsHistoryProvider(conf)
489+
val log = newLogFile("app1", Some("attempt1"), inProgress = false)
490+
writeFile(log, true, None,
491+
SparkListenerApplicationStart("app1", Some("app1"), System.currentTimeMillis(),
492+
"test", Some("attempt1")),
493+
SparkListenerEnvironmentUpdate(Map(
494+
"Spark Properties" -> properties.toSeq,
495+
"JVM Information" -> Seq.empty,
496+
"System Properties" -> Seq.empty,
497+
"Classpath Entries" -> Seq.empty
498+
)),
499+
SparkListenerApplicationEnd(System.currentTimeMillis()))
500+
501+
provider.checkForLogs()
502+
val appUi = provider.getAppUI("app1", Some("attempt1"))
503+
504+
assert(appUi.nonEmpty)
505+
val securityManager = appUi.get.ui.securityManager
506+
checkFn(securityManager)
507+
} finally {
508+
if (provider != null) {
509+
provider.stop()
510+
}
511+
}
512+
}
513+
514+
// Test both history ui admin acls and application acls are configured.
515+
val conf1 = createTestConf()
516+
.set("spark.history.ui.acls.enable", "true")
517+
.set("spark.history.ui.admin.acls", "user1,user2")
518+
.set("spark.history.ui.admin.acls.groups", "group1")
519+
.set("spark.user.groups.mapping", classOf[TestGroupsMappingProvider].getName)
520+
521+
createAndCheck(conf1, ("spark.admin.acls", "user"), ("spark.admin.acls.groups", "group")) {
522+
securityManager =>
523+
// Test whether user has permission to access UI.
524+
securityManager.checkUIViewPermissions("user1") should be (true)
525+
securityManager.checkUIViewPermissions("user2") should be (true)
526+
securityManager.checkUIViewPermissions("user") should be (true)
527+
securityManager.checkUIViewPermissions("abc") should be (false)
528+
529+
// Test whether user with admin group has permission to access UI.
530+
securityManager.checkUIViewPermissions("user3") should be (true)
531+
securityManager.checkUIViewPermissions("user4") should be (true)
532+
securityManager.checkUIViewPermissions("user5") should be (true)
533+
securityManager.checkUIViewPermissions("user6") should be (false)
534+
}
535+
536+
// Test only history ui admin acls are configured.
537+
val conf2 = createTestConf()
538+
.set("spark.history.ui.acls.enable", "true")
539+
.set("spark.history.ui.admin.acls", "user1,user2")
540+
.set("spark.history.ui.admin.acls.groups", "group1")
541+
.set("spark.user.groups.mapping", classOf[TestGroupsMappingProvider].getName)
542+
createAndCheck(conf2) { securityManager =>
543+
// Test whether user has permission to access UI.
544+
securityManager.checkUIViewPermissions("user1") should be (true)
545+
securityManager.checkUIViewPermissions("user2") should be (true)
546+
// Check the unknown "user" should return false
547+
securityManager.checkUIViewPermissions("user") should be (false)
548+
549+
// Test whether user with admin group has permission to access UI.
550+
securityManager.checkUIViewPermissions("user3") should be (true)
551+
securityManager.checkUIViewPermissions("user4") should be (true)
552+
// Check the "user5" without mapping relation should return false
553+
securityManager.checkUIViewPermissions("user5") should be (false)
554+
}
555+
556+
// Test neither history ui admin acls nor application acls are configured.
557+
val conf3 = createTestConf()
558+
.set("spark.history.ui.acls.enable", "true")
559+
.set("spark.user.groups.mapping", classOf[TestGroupsMappingProvider].getName)
560+
createAndCheck(conf3) { securityManager =>
561+
// Test whether user has permission to access UI.
562+
securityManager.checkUIViewPermissions("user1") should be (false)
563+
securityManager.checkUIViewPermissions("user2") should be (false)
564+
securityManager.checkUIViewPermissions("user") should be (false)
565+
566+
// Test whether user with admin group has permission to access UI.
567+
// Check should be failed since we don't have acl group settings.
568+
securityManager.checkUIViewPermissions("user3") should be (false)
569+
securityManager.checkUIViewPermissions("user4") should be (false)
570+
securityManager.checkUIViewPermissions("user5") should be (false)
571+
}
572+
}
573+
477574
/**
478575
* Asks the provider to check for logs and calls a function to perform checks on the updated
479576
* app list. Example:
@@ -532,3 +629,15 @@ class FsHistoryProviderSuite extends SparkFunSuite with BeforeAndAfter with Matc
532629
}
533630

534631
}
632+
633+
class TestGroupsMappingProvider extends GroupMappingServiceProvider {
634+
private val mappings = Map(
635+
"user3" -> "group1",
636+
"user4" -> "group1",
637+
"user5" -> "group")
638+
639+
override def getGroups(username: String): Set[String] = {
640+
mappings.get(username).map(Set(_)).getOrElse(Set.empty)
641+
}
642+
}
643+

docs/monitoring.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,28 @@ The history server can be configured as follows:
169169
If disabled, no access control checks are made.
170170
</td>
171171
</tr>
172+
<tr>
173+
<td>spark.history.ui.admin.acls</td>
174+
<td>empty</td>
175+
<td>
176+
Comma separated list of users/administrators that have view access to all the Spark applications in
177+
history server. By default only the users permitted to view the application at run-time could
178+
access the related application history, with this, configured users/administrators could also
179+
have the permission to access it.
180+
Putting a "*" in the list means any user can have the privilege of admin.
181+
</td>
182+
</tr>
183+
<tr>
184+
<td>spark.history.ui.admin.acls.groups</td>
185+
<td>empty</td>
186+
<td>
187+
Comma separated list of groups that have view access to all the Spark applications in
188+
history server. By default only the groups permitted to view the application at run-time could
189+
access the related application history, with this, configured groups could also
190+
have the permission to access it.
191+
Putting a "*" in the list means any group can have the privilege of admin.
192+
</td>
193+
</tr>
172194
<tr>
173195
<td>spark.history.fs.cleaner.enabled</td>
174196
<td>false</td>

0 commit comments

Comments
 (0)