diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/YarnAuthorizationProvider.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/YarnAuthorizationProvider.java index dd81ebd..7973963 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/YarnAuthorizationProvider.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/YarnAuthorizationProvider.java @@ -28,6 +28,8 @@ import org.apache.hadoop.util.ReflectionUtils; import org.apache.hadoop.yarn.conf.YarnConfiguration; +import com.google.common.annotations.VisibleForTesting; + import java.util.List; /** @@ -61,6 +63,20 @@ public static YarnAuthorizationProvider getInstance(Configuration conf) { } /** + * Destroy the {@link YarnAuthorizationProvider} instance. + * This method is called only in Tests. + */ + @VisibleForTesting + public static void destroy() { + synchronized (YarnAuthorizationProvider.class) { + if (authorizer != null) { + LOG.debug(authorizer.getClass().getName() + " is destroyed."); + authorizer = null; + } + } + } + + /** * Initialize the provider. Invoked on daemon startup. DefaultYarnAuthorizer is * initialized based on configurations. */ diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/AllocationConfiguration.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/AllocationConfiguration.java index 5cf110f..ed7d49b 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/AllocationConfiguration.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/AllocationConfiguration.java @@ -23,13 +23,14 @@ import java.util.Set; import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.authorize.AccessControlList; import org.apache.hadoop.yarn.api.records.QueueACL; import org.apache.hadoop.yarn.api.records.ReservationACL; import org.apache.hadoop.yarn.api.records.Resource; +import org.apache.hadoop.yarn.security.AccessType; import org.apache.hadoop.yarn.server.resourcemanager.reservation.ReservationSchedulerConfiguration; import org.apache.hadoop.yarn.server.resourcemanager.resource.ResourceWeights; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.SchedulerUtils; import org.apache.hadoop.yarn.util.resource.DefaultResourceCalculator; import org.apache.hadoop.yarn.util.resource.ResourceCalculator; import org.apache.hadoop.yarn.util.resource.Resources; @@ -66,7 +67,7 @@ private final float queueMaxAMShareDefault; // ACL's for each queue. Only specifies non-default ACL's from configuration. - private final Map> queueAcls; + private final Map> queueAcls; // Reservation ACL's for each queue. Only specifies non-default ACL's from // configuration. @@ -120,7 +121,7 @@ public AllocationConfiguration(Map minQueueResources, Map minSharePreemptionTimeouts, Map fairSharePreemptionTimeouts, Map fairSharePreemptionThresholds, - Map> queueAcls, + Map> queueAcls, Map> resAcls, QueuePlacementPolicy placementPolicy, Map> configuredQueues, @@ -188,9 +189,10 @@ public AllocationConfiguration(Configuration conf) { * nobody ("") */ public AccessControlList getQueueAcl(String queue, QueueACL operation) { - Map queueAcls = this.queueAcls.get(queue); - if (queueAcls != null) { - AccessControlList operationAcl = queueAcls.get(operation); + Map acls = this.queueAcls.get(queue); + if (acls != null) { + AccessControlList operationAcl = + acls.get(SchedulerUtils.toAccessType(operation)); if (operationAcl != null) { return operationAcl; } @@ -198,6 +200,14 @@ public AccessControlList getQueueAcl(String queue, QueueACL operation) { return (queue.equals("root")) ? EVERYBODY_ACL : NOBODY_ACL; } + /** + * Get the map of ACLs of all queues. + * @return the map of ACLs of all queues + */ + public Map> getQueueAcls() { + return this.queueAcls; + } + @Override /** * Get the map of reservation ACLs to {@link AccessControlList} for the @@ -332,21 +342,6 @@ void setMaxChildResources(String queue, Resource maxResource) { maxChildQueueResources.put(queue, maxResource); } - public boolean hasAccess(String queueName, QueueACL acl, - UserGroupInformation user) { - int lastPeriodIndex = queueName.length(); - while (lastPeriodIndex != -1) { - String queue = queueName.substring(0, lastPeriodIndex); - if (getQueueAcl(queue, acl).isUserAllowed(user)) { - return true; - } - - lastPeriodIndex = queueName.lastIndexOf('.', lastPeriodIndex - 1); - } - - return false; - } - public SchedulingPolicy getSchedulingPolicy(String queueName) { SchedulingPolicy policy = schedulingPolicies.get(queueName); return (policy == null) ? defaultSchedulingPolicy : policy; diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/AllocationFileLoaderService.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/AllocationFileLoaderService.java index ee71981..78562e2 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/AllocationFileLoaderService.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/AllocationFileLoaderService.java @@ -41,7 +41,12 @@ import org.apache.hadoop.yarn.api.records.QueueACL; import org.apache.hadoop.yarn.api.records.ReservationACL; import org.apache.hadoop.yarn.api.records.Resource; +import org.apache.hadoop.yarn.security.AccessType; +import org.apache.hadoop.yarn.security.Permission; +import org.apache.hadoop.yarn.security.PrivilegedEntity; +import org.apache.hadoop.yarn.security.PrivilegedEntity.EntityType; import org.apache.hadoop.yarn.server.resourcemanager.resource.ResourceWeights; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.SchedulerUtils; import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.policies.FifoPolicy; import org.apache.hadoop.yarn.util.Clock; import org.apache.hadoop.yarn.util.SystemClock; @@ -74,6 +79,12 @@ public static final long THREAD_JOIN_TIMEOUT_MS = 1000; + private static final String ROOT = "root"; + private static final AccessControlList EVERYBODY_ACL = + new AccessControlList("*"); + private static final AccessControlList NOBODY_ACL = + new AccessControlList(" "); + private final Clock clock; private long lastSuccessfulReload; // Last time we successfully reloaded queues @@ -81,7 +92,7 @@ // Path to XML file containing allocations. private File allocFile; - + private Listener reloadListener; @VisibleForTesting @@ -90,6 +101,8 @@ private Thread reloadThread; private volatile boolean running = true; + private List defaultPermissions; + public AllocationFileLoaderService() { this(SystemClock.getInstance()); } @@ -208,6 +221,7 @@ public synchronized void reloadAllocations() throws IOException, ParserConfigurationException, SAXException, AllocationConfigurationException { if (allocFile == null) { + reloadListener.onReload(null); return; } LOG.info("Loading allocation file " + allocFile); @@ -224,9 +238,10 @@ public synchronized void reloadAllocations() throws IOException, Map minSharePreemptionTimeouts = new HashMap<>(); Map fairSharePreemptionTimeouts = new HashMap<>(); Map fairSharePreemptionThresholds = new HashMap<>(); - Map> queueAcls = new HashMap<>(); + Map> queueAcls = + new HashMap<>(); Map> reservationAcls = - new HashMap<>(); + new HashMap<>(); Set reservableQueues = new HashSet<>(); Set nonPreemptableQueues = new HashSet<>(); int userMaxAppsDefault = Integer.MAX_VALUE; @@ -444,7 +459,7 @@ private void loadQueue(String parentName, Element element, Map minSharePreemptionTimeouts, Map fairSharePreemptionTimeouts, Map fairSharePreemptionThresholds, - Map> queueAcls, + Map> queueAcls, Map> resAcls, Map> configuredQueues, Set reservableQueues, @@ -468,7 +483,7 @@ private void loadQueue(String parentName, Element element, queueName = parentName + "." + queueName; } - Map acls = new HashMap<>(); + Map acls = new HashMap<>(); Map racls = new HashMap<>(); NodeList fields = element.getChildNodes(); boolean isLeaf = true; @@ -526,10 +541,10 @@ private void loadQueue(String parentName, Element element, queuePolicies.put(queueName, policy); } else if ("aclSubmitApps".equals(field.getTagName())) { String text = ((Text)field.getFirstChild()).getData(); - acls.put(QueueACL.SUBMIT_APPLICATIONS, new AccessControlList(text)); + acls.put(AccessType.SUBMIT_APP, new AccessControlList(text)); } else if ("aclAdministerApps".equals(field.getTagName())) { String text = ((Text)field.getFirstChild()).getData(); - acls.put(QueueACL.ADMINISTER_QUEUE, new AccessControlList(text)); + acls.put(AccessType.ADMINISTER_QUEUE, new AccessControlList(text)); } else if ("aclAdministerReservations".equals(field.getTagName())) { String text = ((Text)field.getFirstChild()).getData(); racls.put(ReservationACL.ADMINISTER_RESERVATIONS, @@ -578,6 +593,18 @@ private void loadQueue(String parentName, Element element, } configuredQueues.get(FSQueueType.PARENT).add(queueName); } + + // Set default acls if not defined + // The root queue defaults to all access + for (QueueACL acl : QueueACL.values()) { + AccessType accessType = SchedulerUtils.toAccessType(acl); + if (acls.get(accessType) == null){ + AccessControlList defaultAcl = queueName.equals(ROOT) ? + EVERYBODY_ACL : NOBODY_ACL; + acls.put(accessType, defaultAcl); + } + } + queueAcls.put(queueName, acls); resAcls.put(queueName, racls); if (maxQueueResources.containsKey(queueName) && @@ -590,8 +617,29 @@ private void loadQueue(String parentName, Element element, minQueueResources.get(queueName))); } } - - public interface Listener { - public void onReload(AllocationConfiguration info); + + /** + * The default permission for the root queue is everybody ("*") + * and the default permission for all other queues is nobody ("") + * The default permission list would be loaded before the permissions + * from allocation file. + * @return default permission list + */ + protected List getDefaultPermissions() { + if (defaultPermissions == null) { + defaultPermissions = new ArrayList<>(); + Map acls = + new HashMap<>(); + for (QueueACL acl : QueueACL.values()) { + acls.put(SchedulerUtils.toAccessType(acl), EVERYBODY_ACL); + } + defaultPermissions.add(new Permission( + new PrivilegedEntity(EntityType.QUEUE, ROOT), acls)); + } + return defaultPermissions; + } + + interface Listener { + void onReload(AllocationConfiguration info) throws IOException; } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FSQueue.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FSQueue.java index 25554dd..dc3b58a 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FSQueue.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FSQueue.java @@ -27,6 +27,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.hadoop.classification.InterfaceAudience.Private; import org.apache.hadoop.classification.InterfaceStability.Unstable; +import org.apache.hadoop.ipc.Server; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; import org.apache.hadoop.yarn.api.records.Priority; @@ -37,8 +38,13 @@ import org.apache.hadoop.yarn.api.records.Resource; import org.apache.hadoop.yarn.factories.RecordFactory; import org.apache.hadoop.yarn.factory.providers.RecordFactoryProvider; +import org.apache.hadoop.yarn.security.AccessRequest; +import org.apache.hadoop.yarn.security.PrivilegedEntity; +import org.apache.hadoop.yarn.security.PrivilegedEntity.EntityType; +import org.apache.hadoop.yarn.security.YarnAuthorizationProvider; import org.apache.hadoop.yarn.server.resourcemanager.resource.ResourceWeights; import org.apache.hadoop.yarn.server.resourcemanager.scheduler.Queue; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.SchedulerUtils; import org.apache.hadoop.yarn.util.resource.Resources; @Private @@ -51,6 +57,8 @@ private Resource steadyFairShare = Resources.createResource(0, 0); private final String name; protected final FairScheduler scheduler; + private final YarnAuthorizationProvider authorizer; + private final PrivilegedEntity queueEntity; private final FSQueueMetrics metrics; protected final FSParentQueue parent; @@ -67,6 +75,9 @@ public FSQueue(String name, FairScheduler scheduler, FSParentQueue parent) { this.name = name; this.scheduler = scheduler; + this.authorizer = + YarnAuthorizationProvider.getInstance(scheduler.getConf()); + this.queueEntity = new PrivilegedEntity(EntityType.QUEUE, name); this.metrics = FSQueueMetrics.forQueue(getName(), parent, true, scheduler.getConf()); metrics.setMinShare(getMinShare()); metrics.setMaxShare(getMaxShare()); @@ -76,20 +87,20 @@ public FSQueue(String name, FairScheduler scheduler, FSParentQueue parent) { metrics.setSchedulingPolicy(allocConf.getSchedulingPolicy(name).getName()); this.parent = parent; } - + public String getName() { return name; } - + @Override public String getQueueName() { return name; } - + public SchedulingPolicy getPolicy() { return policy; } - + public FSParentQueue getParent() { return parent; } @@ -213,7 +224,10 @@ public void setSteadyFairShare(Resource steadyFairShare) { } public boolean hasAccess(QueueACL acl, UserGroupInformation user) { - return scheduler.getAllocationConfiguration().hasAccess(name, acl, user); + return authorizer.checkPermission( + new AccessRequest(queueEntity, user, + SchedulerUtils.toAccessType(acl), null, null, + Server.getRemoteAddress(), null)); } public long getFairSharePreemptionTimeout() { diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairScheduler.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairScheduler.java index 140b4f7..1aad7fd 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairScheduler.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairScheduler.java @@ -26,6 +26,8 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -35,6 +37,7 @@ import org.apache.hadoop.classification.InterfaceStability.Unstable; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.security.authorize.AccessControlList; import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; import org.apache.hadoop.yarn.api.records.ApplicationId; import org.apache.hadoop.yarn.api.records.Container; @@ -54,6 +57,11 @@ import org.apache.hadoop.yarn.exceptions.YarnException; import org.apache.hadoop.yarn.exceptions.YarnRuntimeException; import org.apache.hadoop.yarn.proto.YarnServiceProtos.SchedulerResourceTypes; +import org.apache.hadoop.yarn.security.AccessType; +import org.apache.hadoop.yarn.security.Permission; +import org.apache.hadoop.yarn.security.PrivilegedEntity; +import org.apache.hadoop.yarn.security.PrivilegedEntity.EntityType; +import org.apache.hadoop.yarn.security.YarnAuthorizationProvider; import org.apache.hadoop.yarn.server.api.protocolrecords.NMContainerStatus; import org.apache.hadoop.yarn.server.resourcemanager.RMContext; import org.apache.hadoop.yarn.server.resourcemanager.recovery.RMStateStore.RMState; @@ -128,6 +136,7 @@ AbstractYarnScheduler { private FairSchedulerConfiguration conf; + private YarnAuthorizationProvider authorizer; private Resource incrAllocation; private QueueManager queueMgr; private volatile Clock clock; @@ -1384,6 +1393,7 @@ private void initScheduler(Configuration conf) throws IOException { synchronized (this) { this.conf = new FairSchedulerConfiguration(conf); validateConf(this.conf); + authorizer = YarnAuthorizationProvider.getInstance(conf); minimumAllocation = this.conf.getMinimumAllocation(); initMaximumResourceCapability(this.conf.getMaximumAllocation()); incrAllocation = this.conf.getIncrementAllocation(); @@ -1567,19 +1577,42 @@ public AllocationConfiguration getAllocationConfiguration() { AllocationFileLoaderService.Listener { @Override - public void onReload(AllocationConfiguration queueInfo) { + public void onReload(AllocationConfiguration queueInfo) + throws IOException { // Commit the reload; also create any queue defined in the alloc file // if it does not already exist, so it can be displayed on the web UI. synchronized (FairScheduler.this) { - allocConf = queueInfo; - allocConf.getDefaultSchedulingPolicy().initialize(getClusterResource()); - queueMgr.updateAllocationConfiguration(allocConf); - applyChildDefaults(); - maxRunningEnforcer.updateRunnabilityOnReload(); + if (queueInfo == null) { + authorizer.setPermission(allocsLoader.getDefaultPermissions(), + UserGroupInformation.getCurrentUser()); + } else { + allocConf = queueInfo; + setQueueAcls(allocConf.getQueueAcls()); + allocConf.getDefaultSchedulingPolicy().initialize( + getClusterResource()); + queueMgr.updateAllocationConfiguration(allocConf); + applyChildDefaults(); + maxRunningEnforcer.updateRunnabilityOnReload(); + } } } } + private void setQueueAcls( + Map> queueAcls) + throws IOException { + authorizer.setPermission(allocsLoader.getDefaultPermissions(), + UserGroupInformation.getCurrentUser()); + List permissions = new ArrayList<>(); + for (Entry> queueAcl : + queueAcls.entrySet()) { + permissions.add(new Permission(new PrivilegedEntity(EntityType.QUEUE, + queueAcl.getKey()), queueAcl.getValue())); + } + authorizer.setPermission(permissions, + UserGroupInformation.getCurrentUser()); + } + /** * After reloading the allocation config, the max resource settings for any * ad hoc queues will be missing. This method goes through the queue manager's diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/TestFairScheduler.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/TestFairScheduler.java index c348572..c29e47a 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/TestFairScheduler.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/TestFairScheduler.java @@ -74,6 +74,7 @@ import org.apache.hadoop.yarn.event.EventHandler; import org.apache.hadoop.yarn.exceptions.YarnException; import org.apache.hadoop.yarn.exceptions.YarnRuntimeException; +import org.apache.hadoop.yarn.security.YarnAuthorizationProvider; import org.apache.hadoop.yarn.server.resourcemanager.ApplicationMasterService; import org.apache.hadoop.yarn.server.resourcemanager.MockAM; import org.apache.hadoop.yarn.server.resourcemanager.MockNM; @@ -98,11 +99,9 @@ import org.apache.hadoop.yarn.server.resourcemanager.scheduler.AbstractYarnScheduler; import org.apache.hadoop.yarn.server.resourcemanager.scheduler.QueueMetrics; import org.apache.hadoop.yarn.server.resourcemanager.scheduler.SchedulerApplicationAttempt; - import org.apache.hadoop.yarn.server.resourcemanager.scheduler.SchedulerRequestKey; import org.apache.hadoop.yarn.server.resourcemanager.scheduler.SchedulerNode; import org.apache.hadoop.yarn.server.resourcemanager.scheduler.TestSchedulerUtils; - import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity .TestUtils; import org.apache.hadoop.yarn.server.resourcemanager.scheduler.event.AppAddedSchedulerEvent; @@ -160,6 +159,7 @@ public void tearDown() { } QueueMetrics.clearQueueMetrics(); DefaultMetricsSystem.shutdown(); + YarnAuthorizationProvider.destroy(); }