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());
return;
}
if(StringUtils.isEmpty(service.getKerberosPrincipal().getKeytab())) {
if (StringUtils.isEmpty(service.getKerberosPrincipal().getKeytab())) {
LOG.warn("No Kerberos keytab specified for " + service.getName());
return;
}
@ -1077,27 +1077,31 @@ private void addKeytabResourceIfSecure(SliderFileSystem fileSystem,
throw new YarnException(e);
}
switch (keytabURI.getScheme()) {
case "hdfs":
Path keytabOnhdfs = new Path(keytabURI);
if (!fileSystem.getFileSystem().exists(keytabOnhdfs)) {
LOG.warn(service.getName() + "'s keytab (principalName = " +
principalName + ") doesn't exist at: " + keytabOnhdfs);
return;
if (keytabURI.getScheme() != null) {
switch (keytabURI.getScheme()) {
case "hdfs":
Path keytabOnhdfs = new Path(keytabURI);
if (!fileSystem.getFileSystem().exists(keytabOnhdfs)) {
LOG.warn(service.getName() + "'s keytab (principalName = "
+ 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 =
fileSystem.createAmResource(keytabOnhdfs, LocalResourceType.FILE);
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;
} else {
LOG.warn("Unsupported keytab URI scheme " + keytabURI);
}
}

View File

@ -103,4 +103,6 @@ public interface RestApiErrorMessages {
+ "expression element name %s specified in placement policy of component "
+ "%s. Expression element names should be a valid constraint name or an "
+ "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.security.UserGroupInformation;
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.Component;
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.Resource;
import org.apache.hadoop.yarn.service.api.records.Service;
import org.apache.hadoop.yarn.service.exceptions.SliderException;
import org.apache.hadoop.yarn.service.conf.RestApiConstants;
import org.apache.hadoop.yarn.service.exceptions.RestApiErrorMessages;
@ -111,14 +112,7 @@ public static void validateAndResolveService(Service service,
}
if (UserGroupInformation.isSecurityEnabled()) {
if (!StringUtils.isEmpty(service.getKerberosPrincipal().getKeytab())) {
try {
// validate URI format
new URI(service.getKerberosPrincipal().getKeytab());
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}
}
validateKerberosPrincipal(service.getKerberosPrincipal());
}
// Validate the Docker client config.
@ -145,9 +139,8 @@ public static void validateAndResolveService(Service service,
throw new IllegalArgumentException("Component name collision: " +
comp.getName());
}
// If artifact is of type SERVICE (which cannot be filled from
// global), read external service and add its components to this
// service
// If artifact is of type SERVICE (which cannot be filled from global),
// read external service and add its components to this service
if (comp.getArtifact() != null && comp.getArtifact().getType() ==
Artifact.TypeEnum.SERVICE) {
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,
org.apache.hadoop.conf.Configuration conf) throws IOException {
String dockerClientConfig = service.getDockerClientConfig();

View File

@ -22,6 +22,7 @@
import org.apache.hadoop.yarn.conf.YarnConfiguration;
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.KerberosPrincipal;
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.Resource;
@ -45,6 +46,7 @@
import static org.apache.hadoop.yarn.service.exceptions.RestApiErrorMessages.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* Test for ServiceApiUtil helper methods.
@ -525,4 +527,43 @@ public void testPlacementPolicy() throws IOException {
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());
}
}
}