Fix NodeManager to verify the application's user-name.
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1390825 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
47dfcf45af
commit
40062e1aaa
|
@ -48,14 +48,16 @@ public class ContainerTokenIdentifier extends TokenIdentifier {
|
||||||
|
|
||||||
private ContainerId containerId;
|
private ContainerId containerId;
|
||||||
private String nmHostAddr;
|
private String nmHostAddr;
|
||||||
|
private String appSubmitter;
|
||||||
private Resource resource;
|
private Resource resource;
|
||||||
private long expiryTimeStamp;
|
private long expiryTimeStamp;
|
||||||
private int masterKeyId;
|
private int masterKeyId;
|
||||||
|
|
||||||
public ContainerTokenIdentifier(ContainerId containerID, String hostName,
|
public ContainerTokenIdentifier(ContainerId containerID, String hostName,
|
||||||
Resource r, long expiryTimeStamp, int masterKeyId) {
|
String appSubmitter, Resource r, long expiryTimeStamp, int masterKeyId) {
|
||||||
this.containerId = containerID;
|
this.containerId = containerID;
|
||||||
this.nmHostAddr = hostName;
|
this.nmHostAddr = hostName;
|
||||||
|
this.appSubmitter = appSubmitter;
|
||||||
this.resource = r;
|
this.resource = r;
|
||||||
this.expiryTimeStamp = expiryTimeStamp;
|
this.expiryTimeStamp = expiryTimeStamp;
|
||||||
this.masterKeyId = masterKeyId;
|
this.masterKeyId = masterKeyId;
|
||||||
|
@ -71,6 +73,10 @@ public class ContainerTokenIdentifier extends TokenIdentifier {
|
||||||
return this.containerId;
|
return this.containerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getApplicationSubmitter() {
|
||||||
|
return this.appSubmitter;
|
||||||
|
}
|
||||||
|
|
||||||
public String getNmHostAddress() {
|
public String getNmHostAddress() {
|
||||||
return this.nmHostAddr;
|
return this.nmHostAddr;
|
||||||
}
|
}
|
||||||
|
@ -98,6 +104,7 @@ public class ContainerTokenIdentifier extends TokenIdentifier {
|
||||||
out.writeInt(applicationAttemptId.getAttemptId());
|
out.writeInt(applicationAttemptId.getAttemptId());
|
||||||
out.writeInt(this.containerId.getId());
|
out.writeInt(this.containerId.getId());
|
||||||
out.writeUTF(this.nmHostAddr);
|
out.writeUTF(this.nmHostAddr);
|
||||||
|
out.writeUTF(this.appSubmitter);
|
||||||
out.writeInt(this.resource.getMemory());
|
out.writeInt(this.resource.getMemory());
|
||||||
out.writeLong(this.expiryTimeStamp);
|
out.writeLong(this.expiryTimeStamp);
|
||||||
out.writeInt(this.masterKeyId);
|
out.writeInt(this.masterKeyId);
|
||||||
|
@ -112,6 +119,7 @@ public class ContainerTokenIdentifier extends TokenIdentifier {
|
||||||
this.containerId = BuilderUtils.newContainerId(applicationAttemptId, in
|
this.containerId = BuilderUtils.newContainerId(applicationAttemptId, in
|
||||||
.readInt());
|
.readInt());
|
||||||
this.nmHostAddr = in.readUTF();
|
this.nmHostAddr = in.readUTF();
|
||||||
|
this.appSubmitter = in.readUTF();
|
||||||
this.resource = BuilderUtils.newResource(in.readInt());
|
this.resource = BuilderUtils.newResource(in.readInt());
|
||||||
this.expiryTimeStamp = in.readLong();
|
this.expiryTimeStamp = in.readLong();
|
||||||
this.masterKeyId = in.readInt();
|
this.masterKeyId = in.readInt();
|
||||||
|
|
|
@ -128,7 +128,8 @@ public class BaseContainerTokenSecretManager extends
|
||||||
public byte[] createPassword(ContainerTokenIdentifier identifier) {
|
public byte[] createPassword(ContainerTokenIdentifier identifier) {
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Creating password for " + identifier.getContainerID()
|
LOG.debug("Creating password for " + identifier.getContainerID()
|
||||||
+ " to be run on NM " + identifier.getNmHostAddress());
|
+ " for user " + identifier.getUser() + " to be run on NM "
|
||||||
|
+ identifier.getNmHostAddress());
|
||||||
}
|
}
|
||||||
this.readLock.lock();
|
this.readLock.lock();
|
||||||
try {
|
try {
|
||||||
|
@ -155,7 +156,8 @@ public class BaseContainerTokenSecretManager extends
|
||||||
throws org.apache.hadoop.security.token.SecretManager.InvalidToken {
|
throws org.apache.hadoop.security.token.SecretManager.InvalidToken {
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Retrieving password for " + identifier.getContainerID()
|
LOG.debug("Retrieving password for " + identifier.getContainerID()
|
||||||
+ " to be run on NM " + identifier.getNmHostAddress());
|
+ " for user " + identifier.getUser() + " to be run on NM "
|
||||||
|
+ identifier.getNmHostAddress());
|
||||||
}
|
}
|
||||||
return createPassword(identifier.getBytes(), masterKey.getSecretKey());
|
return createPassword(identifier.getBytes(), masterKey.getSecretKey());
|
||||||
}
|
}
|
||||||
|
@ -173,11 +175,12 @@ public class BaseContainerTokenSecretManager extends
|
||||||
*
|
*
|
||||||
* @param containerId
|
* @param containerId
|
||||||
* @param nodeId
|
* @param nodeId
|
||||||
|
* @param appSubmitter
|
||||||
* @param capability
|
* @param capability
|
||||||
* @return the container-token
|
* @return the container-token
|
||||||
*/
|
*/
|
||||||
public ContainerToken createContainerToken(ContainerId containerId,
|
public ContainerToken createContainerToken(ContainerId containerId,
|
||||||
NodeId nodeId, Resource capability) {
|
NodeId nodeId, String appSubmitter, Resource capability) {
|
||||||
byte[] password;
|
byte[] password;
|
||||||
ContainerTokenIdentifier tokenIdentifier;
|
ContainerTokenIdentifier tokenIdentifier;
|
||||||
long expiryTimeStamp =
|
long expiryTimeStamp =
|
||||||
|
@ -188,8 +191,8 @@ public class BaseContainerTokenSecretManager extends
|
||||||
try {
|
try {
|
||||||
tokenIdentifier =
|
tokenIdentifier =
|
||||||
new ContainerTokenIdentifier(containerId, nodeId.toString(),
|
new ContainerTokenIdentifier(containerId, nodeId.toString(),
|
||||||
capability, expiryTimeStamp, this.currentMasterKey.getMasterKey()
|
appSubmitter, capability, expiryTimeStamp, this.currentMasterKey
|
||||||
.getKeyId());
|
.getMasterKey().getKeyId());
|
||||||
password = this.createPassword(tokenIdentifier);
|
password = this.createPassword(tokenIdentifier);
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -329,7 +329,6 @@ public class ContainerManagerImpl extends CompositeService implements
|
||||||
+ remoteUgi.getTokenIdentifiers().size());
|
+ remoteUgi.getTokenIdentifiers().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Get the tokenId from the remote user ugi
|
// Get the tokenId from the remote user ugi
|
||||||
ContainerTokenIdentifier tokenId =
|
ContainerTokenIdentifier tokenId =
|
||||||
selectContainerTokenIdentifier(remoteUgi);
|
selectContainerTokenIdentifier(remoteUgi);
|
||||||
|
@ -341,8 +340,16 @@ public class ContainerManagerImpl extends CompositeService implements
|
||||||
+ containerIDStr);
|
+ containerIDStr);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
// Is the container coming in with correct user-name?
|
||||||
|
if (!tokenId.getApplicationSubmitter().equals(launchContext.getUser())) {
|
||||||
|
unauthorized = true;
|
||||||
|
messageBuilder.append("\n Expected user-name "
|
||||||
|
+ tokenId.getApplicationSubmitter() + " but found "
|
||||||
|
+ launchContext.getUser());
|
||||||
|
}
|
||||||
|
|
||||||
// Is the container being relaunched? Or RPC layer let startCall with
|
// Is the container being relaunched? Or RPC layer let startCall with
|
||||||
// tokens generated off old-secret through
|
// tokens generated off old-secret through?
|
||||||
if (!this.context.getContainerTokenSecretManager()
|
if (!this.context.getContainerTokenSecretManager()
|
||||||
.isValidStartContainerRequest(tokenId)) {
|
.isValidStartContainerRequest(tokenId)) {
|
||||||
unauthorized = true;
|
unauthorized = true;
|
||||||
|
|
|
@ -231,7 +231,6 @@ public class RMAppManager implements EventHandler<RMAppManagerEvent> {
|
||||||
RMApp application = null;
|
RMApp application = null;
|
||||||
try {
|
try {
|
||||||
String clientTokenStr = null;
|
String clientTokenStr = null;
|
||||||
String user = UserGroupInformation.getCurrentUser().getShortUserName();
|
|
||||||
if (UserGroupInformation.isSecurityEnabled()) {
|
if (UserGroupInformation.isSecurityEnabled()) {
|
||||||
Token<ClientTokenIdentifier> clientToken = new
|
Token<ClientTokenIdentifier> clientToken = new
|
||||||
Token<ClientTokenIdentifier>(
|
Token<ClientTokenIdentifier>(
|
||||||
|
@ -256,11 +255,12 @@ public class RMAppManager implements EventHandler<RMAppManagerEvent> {
|
||||||
submissionContext);
|
submissionContext);
|
||||||
|
|
||||||
// Create RMApp
|
// Create RMApp
|
||||||
application = new RMAppImpl(applicationId, rmContext,
|
application =
|
||||||
this.conf, submissionContext.getApplicationName(), user,
|
new RMAppImpl(applicationId, rmContext, this.conf,
|
||||||
submissionContext.getQueue(), submissionContext, clientTokenStr,
|
submissionContext.getApplicationName(),
|
||||||
appStore, this.scheduler,
|
submissionContext.getUser(), submissionContext.getQueue(),
|
||||||
this.masterService, submitTime);
|
submissionContext, clientTokenStr, appStore, this.scheduler,
|
||||||
|
this.masterService, submitTime);
|
||||||
|
|
||||||
// Sanity check - duplicate?
|
// Sanity check - duplicate?
|
||||||
if (rmContext.getRMApps().putIfAbsent(applicationId, application) !=
|
if (rmContext.getRMApps().putIfAbsent(applicationId, application) !=
|
||||||
|
|
|
@ -1197,7 +1197,7 @@ public class LeafQueue implements CSQueue {
|
||||||
if (UserGroupInformation.isSecurityEnabled()) {
|
if (UserGroupInformation.isSecurityEnabled()) {
|
||||||
containerToken =
|
containerToken =
|
||||||
containerTokenSecretManager.createContainerToken(containerId, nodeId,
|
containerTokenSecretManager.createContainerToken(containerId, nodeId,
|
||||||
capability);
|
application.getUser(), capability);
|
||||||
if (containerToken == null) {
|
if (containerToken == null) {
|
||||||
return null; // Try again later.
|
return null; // Try again later.
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,7 +161,7 @@ public class AppSchedulable extends Schedulable {
|
||||||
if (UserGroupInformation.isSecurityEnabled()) {
|
if (UserGroupInformation.isSecurityEnabled()) {
|
||||||
containerToken =
|
containerToken =
|
||||||
containerTokenSecretManager.createContainerToken(containerId, nodeId,
|
containerTokenSecretManager.createContainerToken(containerId, nodeId,
|
||||||
capability);
|
application.getUser(), capability);
|
||||||
if (containerToken == null) {
|
if (containerToken == null) {
|
||||||
return null; // Try again later.
|
return null; // Try again later.
|
||||||
}
|
}
|
||||||
|
|
|
@ -539,7 +539,8 @@ public class FifoScheduler implements ResourceScheduler, Configurable {
|
||||||
if (UserGroupInformation.isSecurityEnabled()) {
|
if (UserGroupInformation.isSecurityEnabled()) {
|
||||||
containerToken =
|
containerToken =
|
||||||
this.rmContext.getContainerTokenSecretManager()
|
this.rmContext.getContainerTokenSecretManager()
|
||||||
.createContainerToken(containerId, nodeId, capability);
|
.createContainerToken(containerId, nodeId,
|
||||||
|
application.getUser(), capability);
|
||||||
if (containerToken == null) {
|
if (containerToken == null) {
|
||||||
return i; // Try again later.
|
return i; // Try again later.
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,9 +219,10 @@ public class TestContainerManagerSecurity {
|
||||||
|
|
||||||
// Malice user modifies the resource amount
|
// Malice user modifies the resource amount
|
||||||
Resource modifiedResource = BuilderUtils.newResource(2048);
|
Resource modifiedResource = BuilderUtils.newResource(2048);
|
||||||
ContainerTokenIdentifier modifiedIdentifier = new ContainerTokenIdentifier(
|
ContainerTokenIdentifier modifiedIdentifier =
|
||||||
dummyIdentifier.getContainerID(), dummyIdentifier.getNmHostAddress(),
|
new ContainerTokenIdentifier(dummyIdentifier.getContainerID(),
|
||||||
modifiedResource, Long.MAX_VALUE, dummyIdentifier.getMasterKeyId());
|
dummyIdentifier.getNmHostAddress(), "testUser", modifiedResource,
|
||||||
|
Long.MAX_VALUE, dummyIdentifier.getMasterKeyId());
|
||||||
Token<ContainerTokenIdentifier> modifiedToken = new Token<ContainerTokenIdentifier>(
|
Token<ContainerTokenIdentifier> modifiedToken = new Token<ContainerTokenIdentifier>(
|
||||||
modifiedIdentifier.getBytes(), containerToken.getPassword().array(),
|
modifiedIdentifier.getBytes(), containerToken.getPassword().array(),
|
||||||
new Text(containerToken.getKind()), new Text(containerToken
|
new Text(containerToken.getKind()), new Text(containerToken
|
||||||
|
@ -320,12 +321,14 @@ public class TestContainerManagerSecurity {
|
||||||
|
|
||||||
callWithIllegalContainerID(client, tokenId);
|
callWithIllegalContainerID(client, tokenId);
|
||||||
callWithIllegalResource(client, tokenId);
|
callWithIllegalResource(client, tokenId);
|
||||||
|
callWithIllegalUserName(client, tokenId);
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/////////// End of testing for illegal containerIDs and illegal Resources
|
// ///////// End of testing for illegal containerIDs, illegal Resources and
|
||||||
|
// illegal users
|
||||||
|
|
||||||
/////////// Test calls with expired tokens
|
/////////// Test calls with expired tokens
|
||||||
RPC.stopProxy(client);
|
RPC.stopProxy(client);
|
||||||
|
@ -336,7 +339,7 @@ public class TestContainerManagerSecurity {
|
||||||
resourceManager.getRMContainerTokenSecretManager();
|
resourceManager.getRMContainerTokenSecretManager();
|
||||||
final ContainerTokenIdentifier newTokenId =
|
final ContainerTokenIdentifier newTokenId =
|
||||||
new ContainerTokenIdentifier(tokenId.getContainerID(),
|
new ContainerTokenIdentifier(tokenId.getContainerID(),
|
||||||
tokenId.getNmHostAddress(), tokenId.getResource(),
|
tokenId.getNmHostAddress(), "testUser", tokenId.getResource(),
|
||||||
System.currentTimeMillis() - 1,
|
System.currentTimeMillis() - 1,
|
||||||
containerTokenSecreteManager.getCurrentKey().getKeyId());
|
containerTokenSecreteManager.getCurrentKey().getKeyId());
|
||||||
byte[] passowrd =
|
byte[] passowrd =
|
||||||
|
@ -346,9 +349,7 @@ public class TestContainerManagerSecurity {
|
||||||
token = new Token<ContainerTokenIdentifier>(
|
token = new Token<ContainerTokenIdentifier>(
|
||||||
newTokenId.getBytes(), passowrd, new Text(
|
newTokenId.getBytes(), passowrd, new Text(
|
||||||
containerToken.getKind()), new Text(containerToken.getService()));
|
containerToken.getKind()), new Text(containerToken.getService()));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
unauthorizedUser.addToken(token);
|
unauthorizedUser.addToken(token);
|
||||||
unauthorizedUser.doAs(new PrivilegedAction<Void>() {
|
unauthorizedUser.doAs(new PrivilegedAction<Void>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -567,6 +568,29 @@ public class TestContainerManagerSecurity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void callWithIllegalUserName(ContainerManager client,
|
||||||
|
ContainerTokenIdentifier tokenId) {
|
||||||
|
StartContainerRequest request = recordFactory
|
||||||
|
.newRecordInstance(StartContainerRequest.class);
|
||||||
|
// Authenticated but unauthorized, due to wrong resource
|
||||||
|
ContainerLaunchContext context =
|
||||||
|
createContainerLaunchContextForTest(tokenId);
|
||||||
|
context.setUser("Saruman"); // Set a different user-name.
|
||||||
|
request.setContainerLaunchContext(context);
|
||||||
|
try {
|
||||||
|
client.startContainer(request);
|
||||||
|
fail("Connection initiation with unauthorized "
|
||||||
|
+ "access is expected to fail.");
|
||||||
|
} catch (YarnRemoteException e) {
|
||||||
|
LOG.info("Got exception : ", e);
|
||||||
|
Assert.assertTrue(e.getMessage().contains(
|
||||||
|
"Unauthorized request to start container. "));
|
||||||
|
Assert.assertTrue(e.getMessage().contains(
|
||||||
|
"Expected user-name " + tokenId.getApplicationSubmitter()
|
||||||
|
+ " but found " + context.getUser()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ContainerLaunchContext createContainerLaunchContextForTest(
|
private ContainerLaunchContext createContainerLaunchContextForTest(
|
||||||
ContainerTokenIdentifier tokenId) {
|
ContainerTokenIdentifier tokenId) {
|
||||||
ContainerLaunchContext context =
|
ContainerLaunchContext context =
|
||||||
|
|
Loading…
Reference in New Issue