SLIDER-677 enable operation without keytabs for short lived applications
Project: http://git-wip-us.apache.org/repos/asf/incubator-slider/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-slider/commit/1bdf80cd Tree: http://git-wip-us.apache.org/repos/asf/incubator-slider/tree/1bdf80cd Diff: http://git-wip-us.apache.org/repos/asf/incubator-slider/diff/1bdf80cd Branch: refs/heads/feature/SLIDER-151_REST_API Commit: 1bdf80cdf0dcc085e9195bfe80b2ebed35f1b2f9 Parents: 3fe140c Author: Jon Maron <[email protected]> Authored: Mon Dec 1 15:32:15 2014 -0500 Committer: Jon Maron <[email protected]> Committed: Mon Dec 1 15:32:15 2014 -0500 ---------------------------------------------------------------------- .../slider/core/launch/AppMasterLauncher.java | 22 ++++- .../providers/agent/AgentProviderService.java | 29 +++--- .../slideram/SliderAMClientProvider.java | 22 +++-- .../server/appmaster/SliderAppMaster.java | 97 ++++++++++++-------- .../security/SecurityConfiguration.java | 20 ++-- .../security/SecurityConfigurationTest.groovy | 15 --- 6 files changed, 118 insertions(+), 87 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/1bdf80cd/slider-core/src/main/java/org/apache/slider/core/launch/AppMasterLauncher.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/core/launch/AppMasterLauncher.java b/slider-core/src/main/java/org/apache/slider/core/launch/AppMasterLauncher.java index 7023c80..c5526ed 100644 --- a/slider-core/src/main/java/org/apache/slider/core/launch/AppMasterLauncher.java +++ b/slider-core/src/main/java/org/apache/slider/core/launch/AppMasterLauncher.java @@ -21,6 +21,9 @@ package org.apache.slider.core.launch; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.security.SecurityUtil; +import org.apache.hadoop.security.token.Token; +import org.apache.hadoop.security.token.TokenIdentifier; +import org.apache.hadoop.security.token.delegation.AbstractDelegationTokenIdentifier; import org.apache.hadoop.yarn.api.records.ApplicationId; import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext; import org.apache.hadoop.yarn.api.records.Priority; @@ -37,6 +40,8 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.InetAddress; +import java.text.DateFormat; +import java.util.Date; import java.util.Map; import java.util.Set; @@ -218,8 +223,21 @@ public class AppMasterLauncher extends AbstractLauncher { // For now, only getting tokens for the default file-system. FileSystem fs = coreFileSystem.getFileSystem(); - fs.addDelegationTokens(tokenRenewer, credentials); - } + Token<? extends TokenIdentifier>[] tokens = fs.addDelegationTokens(tokenRenewer, + credentials); + // obtain the token expiry from the first token - should be the same for all + // HDFS tokens + if (tokens != null && tokens.length > 0) { + AbstractDelegationTokenIdentifier id = + (AbstractDelegationTokenIdentifier)tokens[0].decodeIdentifier(); + Date d = new Date(id.getIssueDate() + 24*60*60*1000); + log.info("HDFS delegation tokens for AM launch context require renewal by {}", + DateFormat.getDateTimeInstance().format(d)); + } else { + log.warn("No HDFS delegation tokens obtained for AM launch context"); + } + + } /** * Submit the application. http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/1bdf80cd/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java b/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java index 4f981c7..808b15c 100644 --- a/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java +++ b/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java @@ -523,27 +523,28 @@ public class AgentProviderService extends AbstractProviderService implements // we need to localize the keytab files in the directory Path keytabDirPath = fileSystem.buildKeytabPath(keytabDir, null, getClusterName()); - FileStatus[] keytabs = fileSystem.getFileSystem().listStatus(keytabDirPath); - LocalResource keytabRes; boolean serviceKeytabsDeployed = false; - for (FileStatus keytab : keytabs) { - if (!amKeytabName.equals(keytab.getPath().getName()) - && keytab.getPath().getName().endsWith(".keytab")) { - serviceKeytabsDeployed = true; - log.info("Localizing keytab {}", keytab.getPath().getName()); - keytabRes = fileSystem.createAmResource(keytab.getPath(), - LocalResourceType.FILE); - launcher.addLocalResource(SliderKeys.KEYTAB_DIR + "/" + - keytab.getPath().getName(), - keytabRes); + if (fileSystem.getFileSystem().exists(keytabDirPath)) { + FileStatus[] keytabs = fileSystem.getFileSystem().listStatus(keytabDirPath); + LocalResource keytabRes; + for (FileStatus keytab : keytabs) { + if (!amKeytabName.equals(keytab.getPath().getName()) + && keytab.getPath().getName().endsWith(".keytab")) { + serviceKeytabsDeployed = true; + log.info("Localizing keytab {}", keytab.getPath().getName()); + keytabRes = fileSystem.createAmResource(keytab.getPath(), + LocalResourceType.FILE); + launcher.addLocalResource(SliderKeys.KEYTAB_DIR + "/" + + keytab.getPath().getName(), + keytabRes); + } } } if (!serviceKeytabsDeployed) { log.warn("No service keytabs for the application have been localized. " + "If the application requires keytabs for secure operation, " + "please ensure that the required keytabs have been uploaded " - + "to the folder designated by the property {}: {}", - SliderXmlConfKeys.KEY_HDFS_KEYTAB_DIR, keytabDirPath); + + "to the folder {}", keytabDirPath); } } } http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/1bdf80cd/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMClientProvider.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMClientProvider.java b/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMClientProvider.java index 5ce0a78..b790713 100644 --- a/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMClientProvider.java +++ b/slider-core/src/main/java/org/apache/slider/providers/slideram/SliderAMClientProvider.java @@ -175,7 +175,6 @@ public class SliderAMClientProvider extends AbstractClientProvider Map<String, LocalResource> providerResources = new HashMap<String, LocalResource>(); - ProviderUtils.addProviderJar(providerResources, this, SLIDER_JAR, @@ -193,10 +192,11 @@ public class SliderAMClientProvider extends AbstractClientProvider libdir, libDirProp); addKeytabResourceIfNecessary(fileSystem, - launcher, instanceDescription, providerResources); + launcher.addLocalResources(providerResources); + //also pick up all env variables from a map launcher.copyEnvVars( instanceDescription.getInternalOperations().getOrAddComponent( @@ -208,13 +208,11 @@ public class SliderAMClientProvider extends AbstractClientProvider * authentication, add this keytab as a local resource for the AM launch. * * @param fileSystem - * @param launcher * @param instanceDescription * @param providerResources * @throws IOException */ protected void addKeytabResourceIfNecessary(SliderFileSystem fileSystem, - AbstractLauncher launcher, AggregateConf instanceDescription, Map<String, LocalResource> providerResources) throws IOException { @@ -231,14 +229,20 @@ public class SliderAMClientProvider extends AbstractClientProvider SliderXmlConfKeys.KEY_HDFS_KEYTAB_DIR); Path keytabPath = fileSystem.buildKeytabPath(keytabDir, amKeytabName, instanceDescription.getName()); - LocalResource keytabRes = fileSystem.createAmResource(keytabPath, - LocalResourceType.FILE); + if (fileSystem.getFileSystem().exists(keytabPath)) { + LocalResource keytabRes = fileSystem.createAmResource(keytabPath, + LocalResourceType.FILE); - providerResources.put(SliderKeys.KEYTAB_DIR + "/" + - amKeytabName, keytabRes); + providerResources.put(SliderKeys.KEYTAB_DIR + "/" + + amKeytabName, keytabRes); + } else { + log.warn("No keytab file was found at {}. The AM will be " + + "started without a kerberos authenticated identity. " + + "The application is therefore not guaranteed to remain " + + "operational beyond 24 hours.", keytabPath); + } } } - launcher.addLocalResources(providerResources); } /** http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/1bdf80cd/slider-core/src/main/java/org/apache/slider/server/appmaster/SliderAppMaster.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/SliderAppMaster.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/SliderAppMaster.java index 1bafe4b..a84cf52 100644 --- a/slider-core/src/main/java/org/apache/slider/server/appmaster/SliderAppMaster.java +++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/SliderAppMaster.java @@ -33,6 +33,7 @@ import org.apache.hadoop.hdfs.HdfsConfiguration; import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier; import org.apache.hadoop.http.HttpConfig; import org.apache.hadoop.io.DataOutputBuffer; +import org.apache.hadoop.io.Text; import org.apache.hadoop.ipc.ProtocolSignature; import org.apache.hadoop.registry.client.binding.RegistryUtils; import org.apache.hadoop.security.Credentials; @@ -150,7 +151,7 @@ import org.apache.slider.server.appmaster.web.WebAppApi; import org.apache.slider.server.appmaster.web.WebAppApiImpl; import org.apache.slider.server.appmaster.web.rest.RestPaths; import org.apache.slider.server.services.security.CertificateManager; -import org.apache.slider.server.services.security.FsDelegationTokenManager; +//import org.apache.slider.server.services.security.FsDelegationTokenManager; import org.apache.slider.server.services.utility.AbstractSliderLaunchedService; import org.apache.slider.server.appmaster.management.MetricsBindingService; import org.apache.slider.server.services.utility.WebAppService; @@ -376,9 +377,10 @@ public class SliderAppMaster extends AbstractSliderLaunchedService private String agentOpsUrl; private String agentStatusUrl; private YarnRegistryViewForProviders yarnRegistryOperations; - private FsDelegationTokenManager fsDelegationTokenManager; + //private FsDelegationTokenManager fsDelegationTokenManager; private RegisterApplicationMasterResponse amRegistrationData; private PortScanner portScanner; + private SecurityConfiguration securityConfiguration; /** * Service Constructor @@ -584,7 +586,7 @@ public class SliderAppMaster extends AbstractSliderLaunchedService Configuration serviceConf = getConfig(); - SecurityConfiguration securityConfiguration = new SecurityConfiguration( + securityConfiguration = new SecurityConfiguration( serviceConf, instanceDefinition, clustername); // obtain security state boolean securityEnabled = securityConfiguration.isSecurityEnabled(); @@ -765,25 +767,7 @@ public class SliderAppMaster extends AbstractSliderLaunchedService appInformation.put(ResourceKeys.YARN_CORES, Integer.toString(containerMaxCores)); appInformation.put(ResourceKeys.YARN_MEMORY, Integer.toString(containerMaxMemory)); - // process the initial user to obtain the set of user - // supplied credentials (tokens were passed in by client). Remove AMRM - // token and HDFS delegation token, the latter because we will provide an - // up to date token for container launches (getContainerCredentials()). - UserGroupInformation currentUser = UserGroupInformation.getCurrentUser(); - Credentials credentials = currentUser.getCredentials(); - Iterator<Token<? extends TokenIdentifier>> iter = - credentials.getAllTokens().iterator(); - while (iter.hasNext()) { - Token<? extends TokenIdentifier> token = iter.next(); - log.info("Token {}", token.getKind()); - if (token.getKind().equals(AMRMTokenIdentifier.KIND_NAME) || - token.getKind().equals(DelegationTokenIdentifier.HDFS_DELEGATION_KIND)) { - iter.remove(); - } - } - // at this point this credentials map is probably clear, but leaving this - // code to allow for future tokens... - containerCredentials = credentials; + processAMCredentials(securityConfiguration); if (securityEnabled) { secretManager.setMasterKey( @@ -793,17 +777,19 @@ public class SliderAppMaster extends AbstractSliderLaunchedService //tell the server what the ACLs are rpcService.getServer().refreshServiceAcl(serviceConf, new SliderAMPolicyProvider()); - // perform keytab based login to establish kerberos authenticated - // principal. Can do so now since AM registration with RM above required - // tokens associated to principal - String principal = securityConfiguration.getPrincipal(); - File localKeytabFile = - securityConfiguration.getKeytabFile(instanceDefinition); - // Now log in... - login(principal, localKeytabFile); - // obtain new FS reference that should be kerberos based and different - // than the previously cached reference - fs = getClusterFS(); + if (securityConfiguration.isKeytabProvided()) { + // perform keytab based login to establish kerberos authenticated + // principal. Can do so now since AM registration with RM above required + // tokens associated to principal + String principal = securityConfiguration.getPrincipal(); + File localKeytabFile = + securityConfiguration.getKeytabFile(instanceDefinition); + // Now log in... + login(principal, localKeytabFile); + // obtain new FS reference that should be kerberos based and different + // than the previously cached reference + fs = getClusterFS(); + } } // extract container list @@ -881,7 +867,8 @@ public class SliderAppMaster extends AbstractSliderLaunchedService maybeStartMonkey(); // setup token renewal and expiry handling for long lived apps -// if (SliderUtils.isHadoopClusterSecure(getConfig())) { +// if (!securityConfiguration.isKeytabProvided() && +// SliderUtils.isHadoopClusterSecure(getConfig())) { // fsDelegationTokenManager = new FsDelegationTokenManager(actionQueues); // fsDelegationTokenManager.acquireDelegationToken(getConfig()); // } @@ -929,6 +916,37 @@ public class SliderAppMaster extends AbstractSliderLaunchedService return finish(); } + private void processAMCredentials(SecurityConfiguration securityConfiguration) + throws IOException { + // process the initial user to obtain the set of user + // supplied credentials (tokens were passed in by client). Remove AMRM + // token and HDFS delegation token, the latter because we will provide an + // up to date token for container launches (getContainerCredentials()). + UserGroupInformation currentUser = UserGroupInformation.getCurrentUser(); + Credentials credentials = currentUser.getCredentials(); + List<Text> filteredTokens = new ArrayList<Text>(); + filteredTokens.add(AMRMTokenIdentifier.KIND_NAME); + + boolean keytabProvided = securityConfiguration.isKeytabProvided(); + log.info("Slider AM Security Mode: {}", keytabProvided ? "KEYTAB" : "TOKEN"); + if (keytabProvided) { + filteredTokens.add(DelegationTokenIdentifier.HDFS_DELEGATION_KIND); + } + Iterator<Token<? extends TokenIdentifier>> iter = + credentials.getAllTokens().iterator(); + while (iter.hasNext()) { + Token<? extends TokenIdentifier> token = iter.next(); + log.info("Token {}", token.getKind()); + if (filteredTokens.contains(token.getKind())) { + log.debug("Filtering token {} from AM tokens", token.getKind()); + iter.remove(); + } + } + // at this point this credentials map is probably clear, but leaving this + // code to allow for future tokens... + containerCredentials = credentials; + } + private int getPortToRequest(AggregateConf instanceDefinition) throws SliderException { int portToRequest = 0; @@ -2034,10 +2052,13 @@ the registry with/without the new record format // the login is via a keytab (see above) Credentials credentials = new Credentials(containerCredentials); ByteBuffer tokens = null; - Token<? extends TokenIdentifier>[] hdfsTokens = - getClusterFS().getFileSystem().addDelegationTokens( - UserGroupInformation.getLoginUser().getShortUserName(), credentials); - if (hdfsTokens.length > 0) { + if (securityConfiguration.isKeytabProvided()) { + Token<? extends TokenIdentifier>[] hdfsTokens = + getClusterFS().getFileSystem().addDelegationTokens( + UserGroupInformation.getLoginUser().getShortUserName(), + credentials); + } + if (credentials.getAllTokens().size() > 0) { DataOutputBuffer dob = new DataOutputBuffer(); credentials.writeTokenStorageToStream(dob); dob.close(); http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/1bdf80cd/slider-core/src/main/java/org/apache/slider/server/appmaster/security/SecurityConfiguration.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/security/SecurityConfiguration.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/security/SecurityConfiguration.java index 63a7543..4ff6916 100644 --- a/slider-core/src/main/java/org/apache/slider/server/appmaster/security/SecurityConfiguration.java +++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/security/SecurityConfiguration.java @@ -99,15 +99,6 @@ public class SecurityConfiguration { String keytabName = instanceDefinition.getAppConfOperations() .getComponent(SliderKeys.COMPONENT_AM) .get(SliderXmlConfKeys.KEY_AM_LOGIN_KEYTAB_NAME); - if (SliderUtils.isUnset(keytabFullPath) && SliderUtils.isUnset(keytabName)) { - throw new SliderException(SliderExitCodes.EXIT_BAD_CONFIGURATION, - "Either a keytab path on the cluster host (%s) or a" - + " keytab to be retrieved from HDFS (%s) are" - + " required. Please configure one of the keytab" - + " retrieval mechanisms.", - SliderXmlConfKeys.KEY_AM_KEYTAB_LOCAL_PATH, - SliderXmlConfKeys.KEY_AM_LOGIN_KEYTAB_NAME); - } if (SliderUtils.isSet(keytabFullPath) && SliderUtils.isSet(keytabName)) { throw new SliderException(SliderExitCodes.EXIT_BAD_CONFIGURATION, "Both a keytab on the cluster host (%s) and a" @@ -141,6 +132,17 @@ public class SecurityConfiguration { return principal; } + public boolean isKeytabProvided() { + boolean keytabProvided = instanceDefinition.getAppConfOperations() + .getComponent(SliderKeys.COMPONENT_AM) + .get(SliderXmlConfKeys.KEY_AM_KEYTAB_LOCAL_PATH) != null || + instanceDefinition.getAppConfOperations() + .getComponent(SliderKeys.COMPONENT_AM). + get(SliderXmlConfKeys.KEY_AM_LOGIN_KEYTAB_NAME) != null; + return keytabProvided; + + } + public File getKeytabFile(AggregateConf instanceDefinition) throws SliderException, IOException { String keytabFullPath = instanceDefinition.getAppConfOperations() http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/1bdf80cd/slider-core/src/test/groovy/org/apache/slider/server/appmaster/security/SecurityConfigurationTest.groovy ---------------------------------------------------------------------- diff --git a/slider-core/src/test/groovy/org/apache/slider/server/appmaster/security/SecurityConfigurationTest.groovy b/slider-core/src/test/groovy/org/apache/slider/server/appmaster/security/SecurityConfigurationTest.groovy index 4b60ead..e543b7c 100644 --- a/slider-core/src/test/groovy/org/apache/slider/server/appmaster/security/SecurityConfigurationTest.groovy +++ b/slider-core/src/test/groovy/org/apache/slider/server/appmaster/security/SecurityConfigurationTest.groovy @@ -118,21 +118,6 @@ public class SecurityConfigurationTest { } @Test - public void testNoKeytabMechanismConfigured() throws Throwable { - Configuration config = new Configuration() - config.set(CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION, "kerberos") - AggregateConf aggregateConf = new AggregateConf(); - MapOperations compOps = - aggregateConf.appConfOperations.getOrAddComponent(SliderKeys.COMPONENT_AM) - compOps.put(SliderXmlConfKeys.KEY_KEYTAB_PRINCIPAL, "test") - - shouldFail(SliderException) { - SecurityConfiguration securityConfiguration = - new SecurityConfiguration(config, aggregateConf, "testCluster") - } - } - - @Test public void testMissingPrincipalButLoginWithDistributedConfig() throws Throwable { Configuration config = new Configuration() config.set(CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION, "kerberos")
