blob: 3e1ede9cd4681e78307f9c0c032a530786cfae36 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.yarn.server.resourcemanager;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.security.PrivilegedExceptionAction;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.security.SaslRpcServer.AuthMethod;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
import org.apache.hadoop.security.authentication.util.KerberosName;
import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.yarn.api.protocolrecords.CancelDelegationTokenRequest;
import org.apache.hadoop.yarn.api.protocolrecords.RenewDelegationTokenRequest;
import org.apache.hadoop.yarn.exceptions.YarnException;
import org.apache.hadoop.yarn.security.client.RMDelegationTokenIdentifier;
import org.apache.hadoop.yarn.server.resourcemanager.recovery.NullRMStateStore;
import org.apache.hadoop.yarn.server.resourcemanager.security.RMDelegationTokenSecretManager;
import org.apache.hadoop.yarn.server.utils.BuilderUtils;
import org.apache.hadoop.yarn.util.Records;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
public class TestTokenClientRMService {
private final static String kerberosRule =
"RULE:[1:$1@$0](.*@EXAMPLE.COM)s/@.*//\nDEFAULT";
private static RMDelegationTokenSecretManager dtsm;
static {
KerberosName.setRules(kerberosRule);
}
private static final UserGroupInformation owner = UserGroupInformation
.createRemoteUser("owner", AuthMethod.KERBEROS);
private static final UserGroupInformation other = UserGroupInformation
.createRemoteUser("other", AuthMethod.KERBEROS);
private static final UserGroupInformation tester = UserGroupInformation
.createRemoteUser("tester", AuthMethod.KERBEROS);
private static final String testerPrincipal = "tester@EXAMPLE.COM";
private static final String ownerPrincipal = "owner@EXAMPLE.COM";
private static final String otherPrincipal = "other@EXAMPLE.COM";
private static final UserGroupInformation testerKerb = UserGroupInformation
.createRemoteUser(testerPrincipal, AuthMethod.KERBEROS);
private static final UserGroupInformation ownerKerb = UserGroupInformation
.createRemoteUser(ownerPrincipal, AuthMethod.KERBEROS);
private static final UserGroupInformation otherKerb = UserGroupInformation
.createRemoteUser(otherPrincipal, AuthMethod.KERBEROS);
@BeforeClass
public static void setupSecretManager() throws IOException {
ResourceManager rm = mock(ResourceManager.class);
RMContext rmContext = mock(RMContext.class);
when(rmContext.getStateStore()).thenReturn(new NullRMStateStore());
when(rm.getRMContext()).thenReturn(rmContext);
when(rmContext.getResourceManager()).thenReturn(rm);
dtsm =
new RMDelegationTokenSecretManager(60000, 60000, 60000, 60000,
rmContext);
dtsm.startThreads();
Configuration conf = new Configuration();
conf.set("hadoop.security.authentication", "kerberos");
conf.set("hadoop.security.auth_to_local", kerberosRule);
UserGroupInformation.setConfiguration(conf);
UserGroupInformation.getLoginUser()
.setAuthenticationMethod(AuthenticationMethod.KERBEROS);
}
@AfterClass
public static void teardownSecretManager() {
if (dtsm != null) {
dtsm.stopThreads();
}
}
@Test
public void testTokenCancellationByOwner() throws Exception {
// two tests required - one with a kerberos name
// and with a short name
RMContext rmContext = mock(RMContext.class);
final ClientRMService rmService =
new ClientRMService(rmContext, null, null, null, null, dtsm);
testerKerb.doAs(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
checkTokenCancellation(rmService, testerKerb, other);
return null;
}
});
owner.doAs(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
checkTokenCancellation(owner, other);
return null;
}
});
}
@Test
public void testTokenRenewalWrongUser() throws Exception {
try {
owner.doAs(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
try {
checkTokenRenewal(owner, other);
return null;
} catch (YarnException ex) {
Assert.assertTrue(ex.getMessage().contains(
owner.getUserName() + " tries to renew a token"));
Assert.assertTrue(ex.getMessage().contains(
"with non-matching renewer " + other.getUserName()));
throw ex;
}
}
});
} catch (Exception e) {
return;
}
Assert.fail("renew should have failed");
}
@Test
public void testTokenRenewalByLoginUser() throws Exception {
UserGroupInformation.getLoginUser().doAs(
new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
checkTokenRenewal(owner, owner);
checkTokenRenewal(owner, other);
return null;
}
});
}
private void checkTokenRenewal(UserGroupInformation owner,
UserGroupInformation renewer) throws IOException, YarnException {
RMDelegationTokenIdentifier tokenIdentifier =
new RMDelegationTokenIdentifier(new Text(owner.getUserName()),
new Text(renewer.getUserName()), null);
Token<?> token =
new Token<RMDelegationTokenIdentifier>(tokenIdentifier, dtsm);
org.apache.hadoop.yarn.api.records.Token dToken =
BuilderUtils.newDelegationToken(token.getIdentifier(), token.getKind()
.toString(), token.getPassword(), token.getService().toString());
RenewDelegationTokenRequest request =
Records.newRecord(RenewDelegationTokenRequest.class);
request.setDelegationToken(dToken);
RMContext rmContext = mock(RMContext.class);
ClientRMService rmService =
new ClientRMService(rmContext, null, null, null, null, dtsm);
rmService.renewDelegationToken(request);
}
@Test
public void testTokenCancellationByRenewer() throws Exception {
// two tests required - one with a kerberos name
// and with a short name
RMContext rmContext = mock(RMContext.class);
final ClientRMService rmService =
new ClientRMService(rmContext, null, null, null, null, dtsm);
testerKerb.doAs(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
checkTokenCancellation(rmService, owner, testerKerb);
return null;
}
});
other.doAs(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
checkTokenCancellation(owner, other);
return null;
}
});
}
@Test
public void testTokenCancellationByWrongUser() {
// two sets to test -
// 1. try to cancel tokens of short and kerberos users as a kerberos UGI
// 2. try to cancel tokens of short and kerberos users as a simple auth UGI
RMContext rmContext = mock(RMContext.class);
final ClientRMService rmService =
new ClientRMService(rmContext, null, null, null, null, dtsm);
UserGroupInformation[] kerbTestOwners =
{ owner, other, tester, ownerKerb, otherKerb };
UserGroupInformation[] kerbTestRenewers =
{ owner, other, ownerKerb, otherKerb };
for (final UserGroupInformation tokOwner : kerbTestOwners) {
for (final UserGroupInformation tokRenewer : kerbTestRenewers) {
try {
testerKerb.doAs(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
try {
checkTokenCancellation(rmService, tokOwner, tokRenewer);
Assert.fail("We should not reach here; token owner = "
+ tokOwner.getUserName() + ", renewer = "
+ tokRenewer.getUserName());
return null;
} catch (YarnException e) {
Assert.assertTrue(e.getMessage().contains(
testerKerb.getUserName()
+ " is not authorized to cancel the token"));
return null;
}
}
});
} catch (Exception e) {
Assert.fail("Unexpected exception; " + e.getMessage());
}
}
}
UserGroupInformation[] simpleTestOwners =
{ owner, other, ownerKerb, otherKerb, testerKerb };
UserGroupInformation[] simpleTestRenewers =
{ owner, other, ownerKerb, otherKerb };
for (final UserGroupInformation tokOwner : simpleTestOwners) {
for (final UserGroupInformation tokRenewer : simpleTestRenewers) {
try {
tester.doAs(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
try {
checkTokenCancellation(tokOwner, tokRenewer);
Assert.fail("We should not reach here; token owner = "
+ tokOwner.getUserName() + ", renewer = "
+ tokRenewer.getUserName());
return null;
} catch (YarnException ex) {
Assert.assertTrue(ex.getMessage().contains(
tester.getUserName()
+ " is not authorized to cancel the token"));
return null;
}
}
});
} catch (Exception e) {
Assert.fail("Unexpected exception; " + e.getMessage());
}
}
}
}
private void checkTokenCancellation(UserGroupInformation owner,
UserGroupInformation renewer) throws IOException, YarnException {
RMContext rmContext = mock(RMContext.class);
final ClientRMService rmService =
new ClientRMService(rmContext, null, null, null, null, dtsm);
checkTokenCancellation(rmService, owner, renewer);
}
private void checkTokenCancellation(ClientRMService rmService,
UserGroupInformation owner, UserGroupInformation renewer)
throws IOException, YarnException {
RMDelegationTokenIdentifier tokenIdentifier =
new RMDelegationTokenIdentifier(new Text(owner.getUserName()),
new Text(renewer.getUserName()), null);
Token<?> token =
new Token<RMDelegationTokenIdentifier>(tokenIdentifier, dtsm);
org.apache.hadoop.yarn.api.records.Token dToken =
BuilderUtils.newDelegationToken(token.getIdentifier(), token.getKind()
.toString(), token.getPassword(), token.getService().toString());
CancelDelegationTokenRequest request =
Records.newRecord(CancelDelegationTokenRequest.class);
request.setDelegationToken(dToken);
rmService.cancelDelegationToken(request);
}
@Test
public void testTokenRenewalByOwner() throws Exception {
owner.doAs(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
checkTokenRenewal(owner, owner);
return null;
}
});
}
}