HADOOP-6580. UGI should contain authentication method.
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@933810 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
87c788d2e1
commit
b802476d9d
@ -68,6 +68,8 @@ Trunk (unreleased changes)
|
|||||||
HADOOP-6586. Log authentication and authorization failures and successes
|
HADOOP-6586. Log authentication and authorization failures and successes
|
||||||
for RPC (boryas)
|
for RPC (boryas)
|
||||||
|
|
||||||
|
HADOOP-6580. UGI should contain authentication method. (jnp via boryas)
|
||||||
|
|
||||||
IMPROVEMENTS
|
IMPROVEMENTS
|
||||||
|
|
||||||
HADOOP-6283. Improve the exception messages thrown by
|
HADOOP-6283. Improve the exception messages thrown by
|
||||||
|
@ -72,6 +72,7 @@
|
|||||||
import org.apache.hadoop.security.SaslRpcServer.SaslStatus;
|
import org.apache.hadoop.security.SaslRpcServer.SaslStatus;
|
||||||
import org.apache.hadoop.security.SaslRpcServer.SaslDigestCallbackHandler;
|
import org.apache.hadoop.security.SaslRpcServer.SaslDigestCallbackHandler;
|
||||||
import org.apache.hadoop.security.SaslRpcServer.SaslGssCallbackHandler;
|
import org.apache.hadoop.security.SaslRpcServer.SaslGssCallbackHandler;
|
||||||
|
import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
|
||||||
import org.apache.hadoop.security.UserGroupInformation;
|
import org.apache.hadoop.security.UserGroupInformation;
|
||||||
import org.apache.hadoop.security.authorize.ProxyUsers;
|
import org.apache.hadoop.security.authorize.ProxyUsers;
|
||||||
import org.apache.hadoop.security.authorize.AuthorizationException;
|
import org.apache.hadoop.security.authorize.AuthorizationException;
|
||||||
@ -1084,18 +1085,32 @@ private void processHeader(byte[] buf) throws IOException {
|
|||||||
UserGroupInformation protocolUser = header.getUgi();
|
UserGroupInformation protocolUser = header.getUgi();
|
||||||
if (!useSasl) {
|
if (!useSasl) {
|
||||||
user = protocolUser;
|
user = protocolUser;
|
||||||
} else if ((protocolUser != null)
|
if (user != null) {
|
||||||
|
user.setAuthenticationMethod(AuthMethod.SIMPLE.authenticationMethod);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// user is authenticated
|
||||||
|
user.setAuthenticationMethod(authMethod.authenticationMethod);
|
||||||
|
//Now we check if this is a proxy user case. If the protocol user is
|
||||||
|
//different from the 'user', it is a proxy user scenario. However,
|
||||||
|
//this is not allowed if user authenticated with DIGEST.
|
||||||
|
if ((protocolUser != null)
|
||||||
&& (!protocolUser.getUserName().equals(user.getUserName()))) {
|
&& (!protocolUser.getUserName().equals(user.getUserName()))) {
|
||||||
if (authMethod == AuthMethod.DIGEST) {
|
if (authMethod == AuthMethod.DIGEST) {
|
||||||
// Not allowed to doAs if token authentication is used
|
// Not allowed to doAs if token authentication is used
|
||||||
throw new AccessControlException("Authenticated user (" + user
|
throw new AccessControlException("Authenticated user (" + user
|
||||||
+ ") doesn't match what the client claims to be (" + protocolUser
|
+ ") doesn't match what the client claims to be ("
|
||||||
+ ")");
|
+ protocolUser + ")");
|
||||||
} else {
|
} else {
|
||||||
// Effective user can be different from authenticated user
|
// Effective user can be different from authenticated user
|
||||||
// for simple auth or kerberos auth
|
// for simple auth or kerberos auth
|
||||||
|
// The user is the real user. Now we create a proxy user
|
||||||
|
UserGroupInformation realUser = user;
|
||||||
user = UserGroupInformation.createProxyUser(protocolUser
|
user = UserGroupInformation.createProxyUser(protocolUser
|
||||||
.getUserName(), user);
|
.getUserName(), realUser);
|
||||||
|
// Now the user is a proxy user, set Authentication method Proxy.
|
||||||
|
user.setAuthenticationMethod(AuthenticationMethod.PROXY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
import org.apache.hadoop.ipc.Server;
|
import org.apache.hadoop.ipc.Server;
|
||||||
import org.apache.hadoop.security.token.SecretManager;
|
import org.apache.hadoop.security.token.SecretManager;
|
||||||
import org.apache.hadoop.security.token.TokenIdentifier;
|
import org.apache.hadoop.security.token.TokenIdentifier;
|
||||||
|
import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
|
||||||
import org.apache.hadoop.security.token.SecretManager.InvalidToken;
|
import org.apache.hadoop.security.token.SecretManager.InvalidToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -102,17 +103,20 @@ private SaslStatus(int state) {
|
|||||||
|
|
||||||
/** Authentication method */
|
/** Authentication method */
|
||||||
public static enum AuthMethod {
|
public static enum AuthMethod {
|
||||||
SIMPLE((byte) 80, ""), // no authentication
|
SIMPLE((byte) 80, "", AuthenticationMethod.SIMPLE),
|
||||||
KERBEROS((byte) 81, "GSSAPI"), // SASL Kerberos authentication
|
KERBEROS((byte) 81, "GSSAPI", AuthenticationMethod.KERBEROS),
|
||||||
DIGEST((byte) 82, "DIGEST-MD5"); // SASL DIGEST-MD5 authentication
|
DIGEST((byte) 82, "DIGEST-MD5", AuthenticationMethod.TOKEN);
|
||||||
|
|
||||||
/** The code for this method. */
|
/** The code for this method. */
|
||||||
public final byte code;
|
public final byte code;
|
||||||
public final String mechanismName;
|
public final String mechanismName;
|
||||||
|
public final AuthenticationMethod authenticationMethod;
|
||||||
|
|
||||||
private AuthMethod(byte code, String mechanismName) {
|
private AuthMethod(byte code, String mechanismName,
|
||||||
|
AuthenticationMethod authMethod) {
|
||||||
this.code = code;
|
this.code = code;
|
||||||
this.mechanismName = mechanismName;
|
this.mechanismName = mechanismName;
|
||||||
|
this.authenticationMethod = authMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int FIRST_CODE = values()[0].code;
|
private static final int FIRST_CODE = values()[0].code;
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
|
|
||||||
|
import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the full and short name of the user as a principal. This allows us to
|
* Save the full and short name of the user as a principal. This allows us to
|
||||||
* have a single type that we always look for when picking up user names.
|
* have a single type that we always look for when picking up user names.
|
||||||
@ -26,8 +28,13 @@
|
|||||||
class User implements Principal {
|
class User implements Principal {
|
||||||
private final String fullName;
|
private final String fullName;
|
||||||
private final String shortName;
|
private final String shortName;
|
||||||
|
private AuthenticationMethod authMethod = null;
|
||||||
|
|
||||||
public User(String name) {
|
public User(String name) {
|
||||||
|
this(name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User(String name, AuthenticationMethod authMethod) {
|
||||||
fullName = name;
|
fullName = name;
|
||||||
int atIdx = name.indexOf('@');
|
int atIdx = name.indexOf('@');
|
||||||
if (atIdx == -1) {
|
if (atIdx == -1) {
|
||||||
@ -40,6 +47,7 @@ public User(String name) {
|
|||||||
shortName = name.substring(0, slashIdx);
|
shortName = name.substring(0, slashIdx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.authMethod = authMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -65,7 +73,7 @@ public boolean equals(Object o) {
|
|||||||
} else if (o == null || getClass() != o.getClass()) {
|
} else if (o == null || getClass() != o.getClass()) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return fullName.equals(((User) o).fullName);
|
return ((fullName.equals(((User) o).fullName)) && (authMethod == ((User) o).authMethod));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,4 +86,12 @@ public int hashCode() {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return fullName;
|
return fullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAuthenticationMethod(AuthenticationMethod authMethod) {
|
||||||
|
this.authMethod = authMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationMethod getAuthenticationMethod() {
|
||||||
|
return authMethod;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.apache.hadoop.classification.InterfaceAudience;
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.security.SaslRpcServer.AuthMethod;
|
||||||
import org.apache.hadoop.security.token.Token;
|
import org.apache.hadoop.security.token.Token;
|
||||||
import org.apache.hadoop.security.token.TokenIdentifier;
|
import org.apache.hadoop.security.token.TokenIdentifier;
|
||||||
|
|
||||||
@ -470,6 +471,15 @@ public static UserGroupInformation createRemoteUser(String user) {
|
|||||||
return new UserGroupInformation(subject);
|
return new UserGroupInformation(subject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static enum AuthenticationMethod {
|
||||||
|
SIMPLE,
|
||||||
|
KERBEROS,
|
||||||
|
TOKEN,
|
||||||
|
CERTIFICATE,
|
||||||
|
KERBEROS_SSL,
|
||||||
|
PROXY;
|
||||||
|
}
|
||||||
|
|
||||||
/* Create a proxy user using username of the effective user and the ugi of the
|
/* Create a proxy user using username of the effective user and the ugi of the
|
||||||
* real user.
|
* real user.
|
||||||
*
|
*
|
||||||
@ -649,6 +659,30 @@ public String toString() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the authentication method in the subject
|
||||||
|
*
|
||||||
|
* @param authMethod
|
||||||
|
*/
|
||||||
|
public synchronized
|
||||||
|
void setAuthenticationMethod(AuthenticationMethod authMethod) {
|
||||||
|
for (User p : subject.getPrincipals(User.class)) {
|
||||||
|
p.setAuthenticationMethod(authMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the authentication method from the subject
|
||||||
|
*
|
||||||
|
* @return AuthenticationMethod in the subject, null if not present.
|
||||||
|
*/
|
||||||
|
public synchronized AuthenticationMethod getAuthenticationMethod() {
|
||||||
|
for (User p: subject.getPrincipals(User.class)) {
|
||||||
|
return p.getAuthenticationMethod();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compare the subjects to see if they are equal to each other.
|
* Compare the subjects to see if they are equal to each other.
|
||||||
*/
|
*/
|
||||||
|
@ -25,8 +25,11 @@
|
|||||||
import java.io.DataOutput;
|
import java.io.DataOutput;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.security.PrivilegedExceptionAction;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import junit.framework.Assert;
|
||||||
|
|
||||||
import org.apache.commons.logging.*;
|
import org.apache.commons.logging.*;
|
||||||
import org.apache.commons.logging.impl.Log4JLogger;
|
import org.apache.commons.logging.impl.Log4JLogger;
|
||||||
|
|
||||||
@ -44,6 +47,7 @@
|
|||||||
import org.apache.hadoop.security.SaslRpcClient;
|
import org.apache.hadoop.security.SaslRpcClient;
|
||||||
import org.apache.hadoop.security.SaslRpcServer;
|
import org.apache.hadoop.security.SaslRpcServer;
|
||||||
import org.apache.hadoop.security.UserGroupInformation;
|
import org.apache.hadoop.security.UserGroupInformation;
|
||||||
|
import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
|
||||||
|
|
||||||
import org.apache.log4j.Level;
|
import org.apache.log4j.Level;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -161,10 +165,14 @@ public Token<TestTokenIdentifier> selectToken(Text service,
|
|||||||
@KerberosInfo(SERVER_PRINCIPAL_KEY)
|
@KerberosInfo(SERVER_PRINCIPAL_KEY)
|
||||||
@TokenInfo(TestTokenSelector.class)
|
@TokenInfo(TestTokenSelector.class)
|
||||||
public interface TestSaslProtocol extends TestRPC.TestProtocol {
|
public interface TestSaslProtocol extends TestRPC.TestProtocol {
|
||||||
|
public AuthenticationMethod getAuthMethod() throws IOException;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TestSaslImpl extends TestRPC.TestImpl implements
|
public static class TestSaslImpl extends TestRPC.TestImpl implements
|
||||||
TestSaslProtocol {
|
TestSaslProtocol {
|
||||||
|
public AuthenticationMethod getAuthMethod() throws IOException {
|
||||||
|
return UserGroupInformation.getCurrentUser().getAuthenticationMethod();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -258,6 +266,43 @@ static void testKerberosRpc(String principal, String keytab) throws Exception {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDigestAuthMethod() throws Exception {
|
||||||
|
TestTokenSecretManager sm = new TestTokenSecretManager();
|
||||||
|
Server server = RPC.getServer(TestSaslProtocol.class,
|
||||||
|
new TestSaslImpl(), ADDRESS, 0, 5, true, conf, sm);
|
||||||
|
server.start();
|
||||||
|
|
||||||
|
final UserGroupInformation current = UserGroupInformation.getCurrentUser();
|
||||||
|
final InetSocketAddress addr = NetUtils.getConnectAddress(server);
|
||||||
|
TestTokenIdentifier tokenId = new TestTokenIdentifier(new Text(current
|
||||||
|
.getUserName()));
|
||||||
|
Token<TestTokenIdentifier> token = new Token<TestTokenIdentifier>(tokenId,
|
||||||
|
sm);
|
||||||
|
Text host = new Text(addr.getAddress().getHostAddress() + ":"
|
||||||
|
+ addr.getPort());
|
||||||
|
token.setService(host);
|
||||||
|
LOG.info("Service IP address for token is " + host);
|
||||||
|
current.addToken(token);
|
||||||
|
|
||||||
|
current.doAs(new PrivilegedExceptionAction<Object>() {
|
||||||
|
public Object run() throws IOException {
|
||||||
|
TestSaslProtocol proxy = null;
|
||||||
|
try {
|
||||||
|
proxy = (TestSaslProtocol) RPC.getProxy(TestSaslProtocol.class,
|
||||||
|
TestSaslProtocol.versionID, addr, conf);
|
||||||
|
Assert.assertEquals(AuthenticationMethod.TOKEN, proxy.getAuthMethod());
|
||||||
|
} finally {
|
||||||
|
if (proxy != null) {
|
||||||
|
RPC.stopProxy(proxy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
server.stop();
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
System.out.println("Testing Kerberos authentication over RPC");
|
System.out.println("Testing Kerberos authentication over RPC");
|
||||||
if (args.length != 2) {
|
if (args.length != 2) {
|
||||||
|
@ -27,13 +27,15 @@
|
|||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.security.PrivilegedAction;
|
|
||||||
import java.security.PrivilegedExceptionAction;
|
import java.security.PrivilegedExceptionAction;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import junit.framework.Assert;
|
||||||
|
|
||||||
|
import org.apache.hadoop.security.SaslRpcServer.AuthMethod;
|
||||||
|
import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
|
||||||
import org.apache.hadoop.security.token.Token;
|
import org.apache.hadoop.security.token.Token;
|
||||||
import org.apache.hadoop.security.token.TokenIdentifier;
|
import org.apache.hadoop.security.token.TokenIdentifier;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -212,4 +214,51 @@ public Collection<Token<?>> run() throws IOException {
|
|||||||
assertTrue(otherSet.contains(t1));
|
assertTrue(otherSet.contains(t1));
|
||||||
assertTrue(otherSet.contains(t2));
|
assertTrue(otherSet.contains(t2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUGIAuthMethod() throws Exception {
|
||||||
|
final UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
|
||||||
|
final AuthenticationMethod am = AuthenticationMethod.KERBEROS;
|
||||||
|
ugi.setAuthenticationMethod(am);
|
||||||
|
Assert.assertEquals(am, ugi.getAuthenticationMethod());
|
||||||
|
ugi.doAs(new PrivilegedExceptionAction<Object>() {
|
||||||
|
public Object run() throws IOException {
|
||||||
|
Assert.assertEquals(am, UserGroupInformation.getCurrentUser()
|
||||||
|
.getAuthenticationMethod());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUGIAuthMethodInRealUser() throws Exception {
|
||||||
|
final UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
|
||||||
|
UserGroupInformation proxyUgi = UserGroupInformation.createProxyUser(
|
||||||
|
"proxy", ugi);
|
||||||
|
final AuthenticationMethod am = AuthenticationMethod.KERBEROS;
|
||||||
|
ugi.setAuthenticationMethod(am);
|
||||||
|
Assert.assertEquals(am, ugi.getAuthenticationMethod());
|
||||||
|
Assert.assertEquals(null, proxyUgi.getAuthenticationMethod());
|
||||||
|
proxyUgi.setAuthenticationMethod(AuthenticationMethod.PROXY);
|
||||||
|
proxyUgi.doAs(new PrivilegedExceptionAction<Object>() {
|
||||||
|
public Object run() throws IOException {
|
||||||
|
Assert.assertEquals(AuthenticationMethod.PROXY, UserGroupInformation
|
||||||
|
.getCurrentUser().getAuthenticationMethod());
|
||||||
|
Assert.assertEquals(am, UserGroupInformation.getCurrentUser()
|
||||||
|
.getRealUser().getAuthenticationMethod());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
UserGroupInformation proxyUgi2 = UserGroupInformation.createProxyUser(
|
||||||
|
"proxy", ugi);
|
||||||
|
proxyUgi2.setAuthenticationMethod(AuthenticationMethod.PROXY);
|
||||||
|
Assert.assertEquals(proxyUgi, proxyUgi2);
|
||||||
|
// Equality should work if authMethod is null
|
||||||
|
UserGroupInformation realugi = UserGroupInformation.getCurrentUser();
|
||||||
|
UserGroupInformation proxyUgi3 = UserGroupInformation.createProxyUser(
|
||||||
|
"proxyAnother", realugi);
|
||||||
|
UserGroupInformation proxyUgi4 = UserGroupInformation.createProxyUser(
|
||||||
|
"proxyAnother", realugi);
|
||||||
|
Assert.assertEquals(proxyUgi3, proxyUgi4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user