YARN-8236. Invalid kerberos principal file name cause NPE in native service. Contributed by Gour Saha.

This commit is contained in:
Sunil G 2018-05-15 12:17:35 +05:30
parent ffb9210ded
commit 58b97c79e3
4 changed files with 93 additions and 34 deletions

View File

@ -1065,7 +1065,7 @@ private void addKeytabResourceIfSecure(SliderFileSystem fileSystem,
LOG.warn("No Kerberos principal name specified for " + service.getName()); LOG.warn("No Kerberos principal name specified for " + service.getName());
return; return;
} }
if(StringUtils.isEmpty(service.getKerberosPrincipal().getKeytab())) { if (StringUtils.isEmpty(service.getKerberosPrincipal().getKeytab())) {
LOG.warn("No Kerberos keytab specified for " + service.getName()); LOG.warn("No Kerberos keytab specified for " + service.getName());
return; return;
} }
@ -1077,27 +1077,31 @@ private void addKeytabResourceIfSecure(SliderFileSystem fileSystem,
throw new YarnException(e); throw new YarnException(e);
} }
switch (keytabURI.getScheme()) { if (keytabURI.getScheme() != null) {
case "hdfs": switch (keytabURI.getScheme()) {
Path keytabOnhdfs = new Path(keytabURI); case "hdfs":
if (!fileSystem.getFileSystem().exists(keytabOnhdfs)) { Path keytabOnhdfs = new Path(keytabURI);
LOG.warn(service.getName() + "'s keytab (principalName = " + if (!fileSystem.getFileSystem().exists(keytabOnhdfs)) {
principalName + ") doesn't exist at: " + keytabOnhdfs); LOG.warn(service.getName() + "'s keytab (principalName = "
return; + principalName + ") doesn't exist at: " + keytabOnhdfs);
return;
}
LocalResource keytabRes = fileSystem.createAmResource(keytabOnhdfs,
LocalResourceType.FILE);
localResource.put(String.format(YarnServiceConstants.KEYTAB_LOCATION,
service.getName()), keytabRes);
LOG.info("Adding " + service.getName() + "'s keytab for "
+ "localization, uri = " + keytabOnhdfs);
break;
case "file":
LOG.info("Using a keytab from localhost: " + keytabURI);
break;
default:
LOG.warn("Unsupported keytab URI scheme " + keytabURI);
break;
} }
LocalResource keytabRes = } else {
fileSystem.createAmResource(keytabOnhdfs, LocalResourceType.FILE); LOG.warn("Unsupported keytab URI scheme " + keytabURI);
localResource.put(String.format(YarnServiceConstants.KEYTAB_LOCATION,
service.getName()), keytabRes);
LOG.debug("Adding " + service.getName() + "'s keytab for " +
"localization, uri = " + keytabOnhdfs);
break;
case "file":
LOG.debug("Using a keytab from localhost: " + keytabURI);
break;
default:
LOG.warn("Unsupported URI scheme " + keytabURI);
break;
} }
} }

View File

@ -103,4 +103,6 @@ public interface RestApiErrorMessages {
+ "expression element name %s specified in placement policy of component " + "expression element name %s specified in placement policy of component "
+ "%s. Expression element names should be a valid constraint name or an " + "%s. Expression element names should be a valid constraint name or an "
+ "expression name defined for this component only."; + "expression name defined for this component only.";
String ERROR_KEYTAB_URI_SCHEME_INVALID = "Unsupported keytab URI scheme: %s";
String ERROR_KEYTAB_URI_INVALID = "Invalid keytab URI: %s";
} }

View File

@ -29,13 +29,14 @@
import org.apache.hadoop.registry.client.binding.RegistryUtils; import org.apache.hadoop.registry.client.binding.RegistryUtils;
import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.yarn.exceptions.YarnException; import org.apache.hadoop.yarn.exceptions.YarnException;
import org.apache.hadoop.yarn.service.api.records.Container;
import org.apache.hadoop.yarn.service.api.records.Service;
import org.apache.hadoop.yarn.service.api.records.Artifact; import org.apache.hadoop.yarn.service.api.records.Artifact;
import org.apache.hadoop.yarn.service.api.records.Component; import org.apache.hadoop.yarn.service.api.records.Component;
import org.apache.hadoop.yarn.service.api.records.Configuration; import org.apache.hadoop.yarn.service.api.records.Configuration;
import org.apache.hadoop.yarn.service.api.records.Container;
import org.apache.hadoop.yarn.service.api.records.KerberosPrincipal;
import org.apache.hadoop.yarn.service.api.records.PlacementConstraint; import org.apache.hadoop.yarn.service.api.records.PlacementConstraint;
import org.apache.hadoop.yarn.service.api.records.Resource; import org.apache.hadoop.yarn.service.api.records.Resource;
import org.apache.hadoop.yarn.service.api.records.Service;
import org.apache.hadoop.yarn.service.exceptions.SliderException; import org.apache.hadoop.yarn.service.exceptions.SliderException;
import org.apache.hadoop.yarn.service.conf.RestApiConstants; import org.apache.hadoop.yarn.service.conf.RestApiConstants;
import org.apache.hadoop.yarn.service.exceptions.RestApiErrorMessages; import org.apache.hadoop.yarn.service.exceptions.RestApiErrorMessages;
@ -111,14 +112,7 @@ public static void validateAndResolveService(Service service,
} }
if (UserGroupInformation.isSecurityEnabled()) { if (UserGroupInformation.isSecurityEnabled()) {
if (!StringUtils.isEmpty(service.getKerberosPrincipal().getKeytab())) { validateKerberosPrincipal(service.getKerberosPrincipal());
try {
// validate URI format
new URI(service.getKerberosPrincipal().getKeytab());
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}
}
} }
// Validate the Docker client config. // Validate the Docker client config.
@ -145,9 +139,8 @@ public static void validateAndResolveService(Service service,
throw new IllegalArgumentException("Component name collision: " + throw new IllegalArgumentException("Component name collision: " +
comp.getName()); comp.getName());
} }
// If artifact is of type SERVICE (which cannot be filled from // If artifact is of type SERVICE (which cannot be filled from global),
// global), read external service and add its components to this // read external service and add its components to this service
// service
if (comp.getArtifact() != null && comp.getArtifact().getType() == if (comp.getArtifact() != null && comp.getArtifact().getType() ==
Artifact.TypeEnum.SERVICE) { Artifact.TypeEnum.SERVICE) {
if (StringUtils.isEmpty(comp.getArtifact().getId())) { if (StringUtils.isEmpty(comp.getArtifact().getId())) {
@ -226,6 +219,25 @@ public static void validateAndResolveService(Service service,
} }
} }
public static void validateKerberosPrincipal(
KerberosPrincipal kerberosPrincipal) throws IOException {
if (!StringUtils.isEmpty(kerberosPrincipal.getKeytab())) {
try {
// validate URI format
URI keytabURI = new URI(kerberosPrincipal.getKeytab());
if (keytabURI.getScheme() == null) {
throw new IllegalArgumentException(String.format(
RestApiErrorMessages.ERROR_KEYTAB_URI_SCHEME_INVALID,
kerberosPrincipal.getKeytab()));
}
} catch (URISyntaxException e) {
throw new IllegalArgumentException(
String.format(RestApiErrorMessages.ERROR_KEYTAB_URI_INVALID,
e.getLocalizedMessage()));
}
}
}
private static void validateDockerClientConfiguration(Service service, private static void validateDockerClientConfiguration(Service service,
org.apache.hadoop.conf.Configuration conf) throws IOException { org.apache.hadoop.conf.Configuration conf) throws IOException {
String dockerClientConfig = service.getDockerClientConfig(); String dockerClientConfig = service.getDockerClientConfig();

View File

@ -22,6 +22,7 @@
import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.service.api.records.Artifact; import org.apache.hadoop.yarn.service.api.records.Artifact;
import org.apache.hadoop.yarn.service.api.records.Component; import org.apache.hadoop.yarn.service.api.records.Component;
import org.apache.hadoop.yarn.service.api.records.KerberosPrincipal;
import org.apache.hadoop.yarn.service.api.records.PlacementConstraint; import org.apache.hadoop.yarn.service.api.records.PlacementConstraint;
import org.apache.hadoop.yarn.service.api.records.PlacementPolicy; import org.apache.hadoop.yarn.service.api.records.PlacementPolicy;
import org.apache.hadoop.yarn.service.api.records.Resource; import org.apache.hadoop.yarn.service.api.records.Resource;
@ -45,6 +46,7 @@
import static org.apache.hadoop.yarn.service.exceptions.RestApiErrorMessages.*; import static org.apache.hadoop.yarn.service.exceptions.RestApiErrorMessages.*;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/** /**
* Test for ServiceApiUtil helper methods. * Test for ServiceApiUtil helper methods.
@ -525,4 +527,43 @@ public void testPlacementPolicy() throws IOException {
Assert.fail(NO_EXCEPTION_PREFIX + e.getMessage()); Assert.fail(NO_EXCEPTION_PREFIX + e.getMessage());
} }
} }
@Test
public void testKerberosPrincipal() throws IOException {
SliderFileSystem sfs = ServiceTestUtils.initMockFs();
Service app = createValidApplication("comp-a");
KerberosPrincipal kp = new KerberosPrincipal();
kp.setKeytab("/some/path");
kp.setPrincipalName("user/_HOST@domain.com");
app.setKerberosPrincipal(kp);
try {
ServiceApiUtil.validateKerberosPrincipal(app.getKerberosPrincipal());
Assert.fail(EXCEPTION_PREFIX + "service with invalid keytab URI scheme");
} catch (IllegalArgumentException e) {
assertEquals(
String.format(RestApiErrorMessages.ERROR_KEYTAB_URI_SCHEME_INVALID,
kp.getKeytab()),
e.getMessage());
}
kp.setKeytab("/ blank / in / paths");
try {
ServiceApiUtil.validateKerberosPrincipal(app.getKerberosPrincipal());
Assert.fail(EXCEPTION_PREFIX + "service with invalid keytab");
} catch (IllegalArgumentException e) {
// strip out the %s at the end of the RestApiErrorMessages string constant
assertTrue(e.getMessage().contains(
RestApiErrorMessages.ERROR_KEYTAB_URI_INVALID.substring(0,
RestApiErrorMessages.ERROR_KEYTAB_URI_INVALID.length() - 2)));
}
kp.setKeytab("file:///tmp/a.keytab");
// now it should succeed
try {
ServiceApiUtil.validateKerberosPrincipal(app.getKerberosPrincipal());
} catch (IllegalArgumentException e) {
Assert.fail(NO_EXCEPTION_PREFIX + e.getMessage());
}
}
} }