YARN-8236. Invalid kerberos principal file name cause NPE in native service. Contributed by Gour Saha.
(cherry picked from commit 58b97c79e3
)
This commit is contained in:
parent
d997959560
commit
0a30a548f1
|
@ -1065,7 +1065,7 @@ public class ServiceClient extends AppAdminClient implements SliderExitCodes,
|
||||||
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 @@ public class ServiceClient extends AppAdminClient implements SliderExitCodes,
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,13 +29,14 @@ import org.apache.hadoop.registry.client.api.RegistryConstants;
|
||||||
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 class ServiceApiUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
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 class ServiceApiUtil {
|
||||||
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 class ServiceApiUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.apache.hadoop.registry.client.api.RegistryConstants;
|
||||||
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.conf.RestApiConstants.DEFAULT_UNLIM
|
||||||
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 class TestServiceApiUtil {
|
||||||
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue