SLIDER-491 some corrections to delegation retrieval code and ability to specify full keytab location in HDFS
Project: http://git-wip-us.apache.org/repos/asf/incubator-slider/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-slider/commit/6f0d9cb6 Tree: http://git-wip-us.apache.org/repos/asf/incubator-slider/tree/6f0d9cb6 Diff: http://git-wip-us.apache.org/repos/asf/incubator-slider/diff/6f0d9cb6 Branch: refs/heads/feature/SLIDER-481_allow_dedicated_handling_of_exports Commit: 6f0d9cb6537b7eb3498cab44ff4cfa1404507e7c Parents: da67c44 Author: Jon Maron <jma...@hortonworks.com> Authored: Fri Oct 10 18:27:46 2014 -0700 Committer: Jon Maron <jma...@hortonworks.com> Committed: Fri Oct 10 18:27:46 2014 -0700 ---------------------------------------------------------------------- .../apache/slider/common/SliderXmlConfKeys.java | 1 + .../slider/common/tools/CoreFileSystem.java | 18 ++++--- .../providers/agent/AgentProviderService.java | 30 ++++++----- .../slideram/SliderAMClientProvider.java | 49 +++++++++++++++++- .../server/appmaster/SliderAppMaster.java | 37 +++++++------- .../security/SecurityConfiguration.java | 52 ++++---------------- .../security/SecurityConfigurationTest.groovy | 17 +++++++ 7 files changed, 122 insertions(+), 82 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/6f0d9cb6/slider-core/src/main/java/org/apache/slider/common/SliderXmlConfKeys.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/common/SliderXmlConfKeys.java b/slider-core/src/main/java/org/apache/slider/common/SliderXmlConfKeys.java index cc2a03f..6bc007b 100644 --- a/slider-core/src/main/java/org/apache/slider/common/SliderXmlConfKeys.java +++ b/slider-core/src/main/java/org/apache/slider/common/SliderXmlConfKeys.java @@ -145,6 +145,7 @@ public interface SliderXmlConfKeys { "hadoop.http.filter.initializers"; String KEY_KEYSTORE_LOCATION = "ssl.server.keystore.location"; String KEY_AM_LOGIN_KEYTAB_NAME = "slider.am.login.keytab.name"; + String KEY_HDFS_KEYTAB_DIR = "slider.hdfs.keytab.dir"; String KEY_AM_KEYTAB_LOCAL_PATH = "slider.am.keytab.local.path"; String KEY_KEYTAB_PRINCIPAL = "slider.keytab.principal.name"; String KEY_SECURITY_ENABLED = "site.global.security_enabled"; http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/6f0d9cb6/slider-core/src/main/java/org/apache/slider/common/tools/CoreFileSystem.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/common/tools/CoreFileSystem.java b/slider-core/src/main/java/org/apache/slider/common/tools/CoreFileSystem.java index 2ea371b..9a96bd1 100644 --- a/slider-core/src/main/java/org/apache/slider/common/tools/CoreFileSystem.java +++ b/slider-core/src/main/java/org/apache/slider/common/tools/CoreFileSystem.java @@ -139,13 +139,17 @@ public class CoreFileSystem { * * @return the path for keytab installation location */ - public Path buildKeytabPath(String keytabName, String applicationName) { - Preconditions.checkNotNull(applicationName); - Path basePath = getBaseApplicationPath(); - Path baseKeytabDir = new Path(basePath, SliderKeys.KEYTAB_DIR); - Path appKeytabDir = new Path(baseKeytabDir, applicationName); - return keytabName == null ? appKeytabDir : - new Path(appKeytabDir, keytabName); + public Path buildKeytabPath(String keytabDir, String keytabName, String clusterName) { + Path homePath = getHomeDirectory(); + Path baseKeytabDir; + if (keytabDir != null) { + baseKeytabDir = new Path(homePath, keytabDir); + } else { + baseKeytabDir = new Path(buildClusterDirPath(clusterName), + SliderKeys.KEYTAB_DIR); + } + return keytabName == null ? baseKeytabDir : + new Path(baseKeytabDir, keytabName); } /** http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/6f0d9cb6/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 330ffa3..44777c3 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 @@ -357,20 +357,24 @@ public class AgentProviderService extends AbstractProviderService implements } if (SliderUtils.isHadoopClusterSecure(getConfig())) { - String keytabFullPath = instanceDefinition.getAppConfOperations() + String keytabPathOnHost = instanceDefinition.getAppConfOperations() .getComponent(SliderKeys.COMPONENT_AM).get( SliderXmlConfKeys.KEY_AM_KEYTAB_LOCAL_PATH); - String amKeytabName = instanceDefinition.getAppConfOperations() - .getComponent(SliderKeys.COMPONENT_AM).get( - SliderXmlConfKeys.KEY_AM_LOGIN_KEYTAB_NAME); - if (SliderUtils.isUnset(keytabFullPath)) { + if (SliderUtils.isUnset(keytabPathOnHost)) { + String amKeytabName = instanceDefinition.getAppConfOperations() + .getComponent(SliderKeys.COMPONENT_AM).get( + SliderXmlConfKeys.KEY_AM_LOGIN_KEYTAB_NAME); + String keytabDir = instanceDefinition.getAppConfOperations() + .getComponent(SliderKeys.COMPONENT_AM).get( + SliderXmlConfKeys.KEY_HDFS_KEYTAB_DIR); // we need to localize the keytab files in the directory - Path keytabDir = fileSystem.buildKeytabPath(null, - getAmState().getApplicationName()); - FileStatus[] keytabs = fileSystem.getFileSystem().listStatus(keytabDir); + Path keytabDirPath = fileSystem.buildKeytabPath(keytabDir, null, + clusterName); + FileStatus[] keytabs = fileSystem.getFileSystem().listStatus(keytabDirPath); LocalResource keytabRes; for (FileStatus keytab : keytabs) { - if (!amKeytabName.equals(keytab.getPath().getName())) { + if (!amKeytabName.equals(keytab.getPath().getName()) + && keytab.getPath().getName().endsWith(".keytab")) { log.info("Localizing keytab {}", keytab.getPath().getName()); keytabRes = fileSystem.createAmResource(keytab.getPath(), LocalResourceType.FILE); @@ -678,12 +682,12 @@ public class AgentProviderService extends AbstractProviderService implements serviceRecord.addInternalEndpoint( new Endpoint(CustomRegistryConstants.AGENT_SECURE_REST_API, - ProtocolTypes.PROTOCOL_REST, - restURL.toURI())); + ProtocolTypes.PROTOCOL_REST, + restURL.toURI())); serviceRecord.addInternalEndpoint( new Endpoint(CustomRegistryConstants.AGENT_ONEWAY_REST_API, - ProtocolTypes.PROTOCOL_REST, - agentStatusURL.toURI())); + ProtocolTypes.PROTOCOL_REST, + agentStatusURL.toURI())); } catch (URISyntaxException e) { throw new IOException(e); } http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/6f0d9cb6/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 991a6b1..5edf1bf 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 @@ -25,12 +25,15 @@ import org.apache.curator.CuratorZookeeperClient; import org.apache.curator.framework.CuratorFramework; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; +import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.yarn.api.records.LocalResource; +import org.apache.hadoop.yarn.api.records.LocalResourceType; import org.apache.hadoop.yarn.api.records.Resource; import org.apache.slider.api.InternalKeys; import org.apache.slider.api.ResourceKeys; import org.apache.slider.api.RoleKeys; import org.apache.slider.common.SliderKeys; +import org.apache.slider.common.SliderXmlConfKeys; import org.apache.slider.common.tools.SliderFileSystem; import org.apache.slider.common.tools.SliderUtils; import org.apache.slider.core.conf.AggregateConf; @@ -206,8 +209,12 @@ public class SliderAMClientProvider extends AbstractClientProvider ProviderUtils.addDependencyJars(providerResources, fileSystem, tempPath, libdir, jars, classes); - - launcher.addLocalResources(providerResources); + + addKeytabResourceIfNecessary(fileSystem, + launcher, + instanceDescription, + providerResources); + //also pick up all env variables from a map launcher.copyEnvVars( instanceDescription.getInternalOperations().getOrAddComponent( @@ -215,6 +222,44 @@ public class SliderAMClientProvider extends AbstractClientProvider } /** + * If the cluster is secure, and an HDFS installed keytab is available for AM + * 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 { + if (UserGroupInformation.isSecurityEnabled()) { + String keytabPathOnHost = instanceDescription.getAppConfOperations() + .getComponent(SliderKeys.COMPONENT_AM).get( + SliderXmlConfKeys.KEY_AM_KEYTAB_LOCAL_PATH); + if (SliderUtils.isUnset(keytabPathOnHost)) { + String amKeytabName = instanceDescription.getAppConfOperations() + .getComponent(SliderKeys.COMPONENT_AM).get( + SliderXmlConfKeys.KEY_AM_LOGIN_KEYTAB_NAME); + String keytabDir = instanceDescription.getAppConfOperations() + .getComponent(SliderKeys.COMPONENT_AM).get( + SliderXmlConfKeys.KEY_HDFS_KEYTAB_DIR); + Path keytabPath = fileSystem.buildKeytabPath(keytabDir, amKeytabName, + instanceDescription.getName()); + LocalResource keytabRes = fileSystem.createAmResource(keytabPath, + LocalResourceType.FILE); + + providerResources.put(SliderKeys.KEYTAB_DIR + "/" + + amKeytabName, keytabRes); + } + } + launcher.addLocalResources(providerResources); + } + + /** * Update the AM resource with any local needs * @param capability capability to update */ http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/6f0d9cb6/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 4baa11c..a887cf8 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 @@ -34,6 +34,7 @@ import org.apache.hadoop.security.Credentials; import org.apache.hadoop.security.SaslRpcServer; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.token.Token; +import org.apache.hadoop.security.token.TokenIdentifier; import org.apache.hadoop.service.Service; import org.apache.hadoop.service.ServiceStateChangeListener; import org.apache.hadoop.yarn.api.ApplicationConstants; @@ -234,7 +235,7 @@ public class SliderAppMaster extends AbstractSliderLaunchedService /** * token blob */ - private Credentials containerTokens; + private Credentials containerCredentials; private WorkflowRpcService rpcService; @@ -716,12 +717,13 @@ public class SliderAppMaster extends AbstractSliderLaunchedService // 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 (getContainerTokens()). + // up to date token for container launches (getContainerCredentials()). UserGroupInformation currentUser = UserGroupInformation.getCurrentUser(); Credentials credentials = currentUser.getCredentials(); - Iterator<Token<?>> iter = credentials.getAllTokens().iterator(); + Iterator<Token<? extends TokenIdentifier>> iter = + credentials.getAllTokens().iterator(); while (iter.hasNext()) { - Token<?> token = iter.next(); + 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)) { @@ -730,7 +732,7 @@ public class SliderAppMaster extends AbstractSliderLaunchedService } // at this point this credentials map is probably clear, but leaving this // code to allow for future tokens... - containerTokens = credentials; + containerCredentials = credentials; if (securityEnabled) { secretManager.setMasterKey( @@ -744,8 +746,8 @@ public class SliderAppMaster extends AbstractSliderLaunchedService // principal. Can do so now since AM registration with RM above required // tokens associated to principal String principal = securityConfiguration.getPrincipal(); - File localKeytabFile = securityConfiguration.getKeytabFile( - fs, instanceDefinition, principal); + File localKeytabFile = + securityConfiguration.getKeytabFile(instanceDefinition); // Now log in... login(principal, localKeytabFile); // obtain new FS reference that should be kerberos based and different @@ -898,13 +900,14 @@ public class SliderAppMaster extends AbstractSliderLaunchedService } Credentials credentials = user.getCredentials(); - Iterator<Token<?>> iter = credentials.getAllTokens().iterator(); + Iterator<Token<? extends TokenIdentifier>> iter = + credentials.getAllTokens().iterator(); while (iter.hasNext()) { - Token<?> token = iter.next(); + Token<? extends TokenIdentifier> token = iter.next(); log.info("Token {}", token.getKind()); if (token.getKind().equals( DelegationTokenIdentifier.HDFS_DELEGATION_KIND)) { - log.info("Unexpected HDFS delegation token. Removing..."); + log.info("HDFS delegation token {}. Removing...", token); iter.remove(); } } @@ -1907,7 +1910,7 @@ public class SliderAppMaster extends AbstractSliderLaunchedService // inside the distributed shell. // add current HDFS delegation token with an up to date token - ByteBuffer tokens = getContainerTokens(); + ByteBuffer tokens = getContainerCredentials(); if (tokens != null) { ctx.setTokens(tokens); @@ -1918,15 +1921,15 @@ public class SliderAppMaster extends AbstractSliderLaunchedService nmClientAsync.startContainerAsync(container, ctx); } - private ByteBuffer getContainerTokens() throws IOException { + private ByteBuffer getContainerCredentials() throws IOException { // a delegation token can be retrieved from filesystem since // the login is via a keytab (see above) + Credentials credentials = new Credentials(containerCredentials); ByteBuffer tokens = null; - Token hdfsToken = getClusterFS().getFileSystem().getDelegationToken - (UserGroupInformation.getLoginUser().getShortUserName()); - if (hdfsToken != null) { - Credentials credentials = new Credentials(containerTokens); - credentials.addToken(hdfsToken.getKind(), hdfsToken); + Token<? extends TokenIdentifier> hdfsTokens[] = + getClusterFS().getFileSystem().addDelegationTokens( + UserGroupInformation.getLoginUser().getShortUserName(), credentials); + if (hdfsTokens.length > 0) { DataOutputBuffer dob = new DataOutputBuffer(); credentials.writeTokenStorageToStream(dob); dob.close(); http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/6f0d9cb6/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 e5cdad2..63a7543 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 @@ -141,8 +141,7 @@ public class SecurityConfiguration { return principal; } - public File getKeytabFile(SliderFileSystem fs, - AggregateConf instanceDefinition, String principal) + public File getKeytabFile(AggregateConf instanceDefinition) throws SliderException, IOException { String keytabFullPath = instanceDefinition.getAppConfOperations() .getComponent(SliderKeys.COMPONENT_AM) @@ -151,52 +150,19 @@ public class SecurityConfiguration { if (SliderUtils.isUnset(keytabFullPath)) { // get the keytab String keytabName = instanceDefinition.getAppConfOperations() - .getComponent(SliderKeys.COMPONENT_AM).get(SliderXmlConfKeys.KEY_AM_LOGIN_KEYTAB_NAME); - log.info("No host keytab file path specified. Downloading keytab {}" - + " from HDFS to perform login of using principal {}", - keytabName, principal); + .getComponent(SliderKeys.COMPONENT_AM). + get(SliderXmlConfKeys.KEY_AM_LOGIN_KEYTAB_NAME); + log.info("No host keytab file path specified. Will attempt to retrieve" + + " keytab file {} as a local resource for the container", + keytabName); // download keytab to local, protected directory - localKeytabFile = getFileFromFileSystem(fs, keytabName); + localKeytabFile = new File(SliderKeys.KEYTAB_DIR, keytabName); } else { - log.info("Leveraging host keytab file {} to login principal {}", - keytabFullPath, principal); + log.info("Leveraging host keytab file {} for login", + keytabFullPath); localKeytabFile = new File(keytabFullPath); } return localKeytabFile; } - /** - * Download the keytab file from FileSystem to local file. - * @param fs - * @param keytabName - * @return - * @throws SliderException - * @throws IOException - */ - protected File getFileFromFileSystem(SliderFileSystem fs, String keytabName) - throws SliderException, IOException { - File keytabDestinationDir = new File( - FileUtils.getTempDirectory().getAbsolutePath() + - "/keytab" + System.currentTimeMillis()); - if (!keytabDestinationDir.mkdirs()) { - throw new SliderException("Unable to create local keytab directory"); - } - RawLocalFileSystem fileSystem = new RawLocalFileSystem(); - // allow app user to access local keytab dir - FsPermission permissions = new FsPermission(FsAction.ALL, FsAction.NONE, - FsAction.NONE); - fileSystem.setPermission(new Path(keytabDestinationDir.getAbsolutePath()), - permissions); - - Path keytabPath = fs.buildKeytabPath(keytabName, clusterName); - File localKeytabFile = new File(keytabDestinationDir, keytabName); - FileUtil.copy(fs.getFileSystem(), keytabPath, - localKeytabFile, - false, configuration); - // set permissions on actual keytab file to be read-only for user - permissions = new FsPermission(FsAction.READ, FsAction.NONE, FsAction.NONE); - fileSystem.setPermission(new Path(localKeytabFile.getAbsolutePath()), - permissions); - return localKeytabFile; - } } http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/6f0d9cb6/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 4ef142a..4b60ead 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 @@ -157,4 +157,21 @@ public class SecurityConfigurationTest { SecurityConfiguration securityConfiguration = new SecurityConfiguration(config, aggregateConf, "testCluster") } + + @Test + public void testKeypathLocationOnceLocalized() 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_AM_LOGIN_KEYTAB_NAME, "some.keytab") + + SecurityConfiguration securityConfiguration = + new SecurityConfiguration(config, aggregateConf, "testCluster") + + assert new File(SliderKeys.KEYTAB_DIR, "some.keytab").getAbsolutePath() == + securityConfiguration.getKeytabFile(aggregateConf).getAbsolutePath() + } + }