HDFS-14434. Ignore user.name query parameter in secure WebHDFS.

Contributed by KWON BYUNGCHANG
This commit is contained in:
Eric Yang 2019-05-28 17:31:35 -04:00
parent fb0b39f4bf
commit d78854b928
5 changed files with 235 additions and 139 deletions

View File

@ -170,6 +170,7 @@ public class WebHdfsFileSystem extends FileSystem
private InetSocketAddress nnAddrs[]; private InetSocketAddress nnAddrs[];
private int currentNNAddrIndex; private int currentNNAddrIndex;
private boolean disallowFallbackToInsecureCluster; private boolean disallowFallbackToInsecureCluster;
private boolean isInsecureCluster;
private String restCsrfCustomHeader; private String restCsrfCustomHeader;
private Set<String> restCsrfMethodsToIgnore; private Set<String> restCsrfMethodsToIgnore;
@ -282,6 +283,7 @@ public class WebHdfsFileSystem extends FileSystem
this.workingDir = makeQualified(new Path(getHomeDirectoryString(ugi))); this.workingDir = makeQualified(new Path(getHomeDirectoryString(ugi)));
this.canRefreshDelegationToken = UserGroupInformation.isSecurityEnabled(); this.canRefreshDelegationToken = UserGroupInformation.isSecurityEnabled();
this.isInsecureCluster = !this.canRefreshDelegationToken;
this.disallowFallbackToInsecureCluster = !conf.getBoolean( this.disallowFallbackToInsecureCluster = !conf.getBoolean(
CommonConfigurationKeys.IPC_CLIENT_FALLBACK_TO_SIMPLE_AUTH_ALLOWED_KEY, CommonConfigurationKeys.IPC_CLIENT_FALLBACK_TO_SIMPLE_AUTH_ALLOWED_KEY,
CommonConfigurationKeys.IPC_CLIENT_FALLBACK_TO_SIMPLE_AUTH_ALLOWED_DEFAULT); CommonConfigurationKeys.IPC_CLIENT_FALLBACK_TO_SIMPLE_AUTH_ALLOWED_DEFAULT);
@ -367,6 +369,7 @@ public class WebHdfsFileSystem extends FileSystem
LOG.debug("Fetched new token: {}", token); LOG.debug("Fetched new token: {}", token);
} else { // security is disabled } else { // security is disabled
canRefreshDelegationToken = false; canRefreshDelegationToken = false;
isInsecureCluster = true;
} }
} }
} }
@ -413,8 +416,7 @@ public class WebHdfsFileSystem extends FileSystem
if (cachedHomeDirectory == null) { if (cachedHomeDirectory == null) {
final HttpOpParam.Op op = GetOpParam.Op.GETHOMEDIRECTORY; final HttpOpParam.Op op = GetOpParam.Op.GETHOMEDIRECTORY;
try { try {
String pathFromDelegatedFS = new FsPathResponseRunner<String>(op, null, String pathFromDelegatedFS = new FsPathResponseRunner<String>(op, null){
new UserParam(ugi)) {
@Override @Override
String decodeResponse(Map<?, ?> json) throws IOException { String decodeResponse(Map<?, ?> json) throws IOException {
return JsonUtilClient.getPath(json); return JsonUtilClient.getPath(json);
@ -576,7 +578,8 @@ public class WebHdfsFileSystem extends FileSystem
return url; return url;
} }
Param<?,?>[] getAuthParameters(final HttpOpParam.Op op) throws IOException { private synchronized Param<?, ?>[] getAuthParameters(final HttpOpParam.Op op)
throws IOException {
List<Param<?,?>> authParams = Lists.newArrayList(); List<Param<?,?>> authParams = Lists.newArrayList();
// Skip adding delegation token for token operations because these // Skip adding delegation token for token operations because these
// operations require authentication. // operations require authentication.
@ -593,7 +596,12 @@ public class WebHdfsFileSystem extends FileSystem
authParams.add(new DoAsParam(userUgi.getShortUserName())); authParams.add(new DoAsParam(userUgi.getShortUserName()));
userUgi = realUgi; userUgi = realUgi;
} }
authParams.add(new UserParam(userUgi.getShortUserName())); UserParam userParam = new UserParam((userUgi.getShortUserName()));
//in insecure, use user.name parameter, in secure, use spnego auth
if(isInsecureCluster) {
authParams.add(userParam);
}
} }
return authParams.toArray(new Param<?,?>[0]); return authParams.toArray(new Param<?,?>[0]);
} }

View File

@ -118,12 +118,9 @@ public class JspHelper {
remoteUser = request.getRemoteUser(); remoteUser = request.getRemoteUser();
final String tokenString = request.getParameter(DELEGATION_PARAMETER_NAME); final String tokenString = request.getParameter(DELEGATION_PARAMETER_NAME);
if (tokenString != null) { if (tokenString != null) {
// Token-based connections need only verify the effective user, and
// disallow proxying to different user. Proxy authorization checks // user.name, doas param is ignored in the token-based auth
// are not required since the checks apply to issuing a token.
ugi = getTokenUGI(context, request, tokenString, conf); ugi = getTokenUGI(context, request, tokenString, conf);
checkUsername(ugi.getShortUserName(), usernameFromQuery);
checkUsername(ugi.getShortUserName(), doAsUserFromQuery);
} else if (remoteUser == null) { } else if (remoteUser == null) {
throw new IOException( throw new IOException(
"Security enabled but user not authenticated by filter"); "Security enabled but user not authenticated by filter");
@ -137,7 +134,6 @@ public class JspHelper {
if (ugi == null) { // security is off, or there's no token if (ugi == null) { // security is off, or there's no token
ugi = UserGroupInformation.createRemoteUser(remoteUser); ugi = UserGroupInformation.createRemoteUser(remoteUser);
checkUsername(ugi.getShortUserName(), usernameFromQuery);
if (UserGroupInformation.isSecurityEnabled()) { if (UserGroupInformation.isSecurityEnabled()) {
// This is not necessarily true, could have been auth'ed by user-facing // This is not necessarily true, could have been auth'ed by user-facing
// filter // filter

View File

@ -199,31 +199,39 @@ public class TestJspHelper {
Assert.assertEquals(ugi.getShortUserName(), user); Assert.assertEquals(ugi.getShortUserName(), user);
checkUgiFromToken(ugi); checkUgiFromToken(ugi);
// can't proxy with a token! // if present token, ignore doas parameter
request = getMockRequest(null, null, "rogue"); request = getMockRequest(null, null, "rogue");
when(request.getParameter(JspHelper.DELEGATION_PARAMETER_NAME)).thenReturn( when(request.getParameter(JspHelper.DELEGATION_PARAMETER_NAME)).thenReturn(
tokenString); tokenString);
try {
JspHelper.getUGI(context, request, conf);
Assert.fail("bad request allowed");
} catch (IOException ioe) {
Assert.assertEquals(
"Usernames not matched: name=rogue != expected="+user,
ioe.getMessage());
}
// can't proxy with a token! ugi = JspHelper.getUGI(context, request, conf);
Assert.assertNotNull(ugi.getRealUser());
Assert.assertEquals(ugi.getRealUser().getShortUserName(), realUser);
Assert.assertEquals(ugi.getShortUserName(), user);
checkUgiFromToken(ugi);
// if present token, ignore user.name parameter
request = getMockRequest(null, "rogue", null);
when(request.getParameter(JspHelper.DELEGATION_PARAMETER_NAME)).thenReturn(
tokenString);
ugi = JspHelper.getUGI(context, request, conf);
Assert.assertNotNull(ugi.getRealUser());
Assert.assertEquals(ugi.getRealUser().getShortUserName(), realUser);
Assert.assertEquals(ugi.getShortUserName(), user);
checkUgiFromToken(ugi);
// if present token, ignore user.name and doas parameter
request = getMockRequest(null, user, "rogue"); request = getMockRequest(null, user, "rogue");
when(request.getParameter(JspHelper.DELEGATION_PARAMETER_NAME)).thenReturn( when(request.getParameter(JspHelper.DELEGATION_PARAMETER_NAME)).thenReturn(
tokenString); tokenString);
try {
JspHelper.getUGI(context, request, conf); ugi = JspHelper.getUGI(context, request, conf);
Assert.fail("bad request allowed"); Assert.assertNotNull(ugi.getRealUser());
} catch (IOException ioe) { Assert.assertEquals(ugi.getRealUser().getShortUserName(), realUser);
Assert.assertEquals( Assert.assertEquals(ugi.getShortUserName(), user);
"Usernames not matched: name=rogue != expected="+user, checkUgiFromToken(ugi);
ioe.getMessage());
}
} }
@Test @Test
@ -271,16 +279,12 @@ public class TestJspHelper {
Assert.assertEquals(ugi.getShortUserName(), realUser); Assert.assertEquals(ugi.getShortUserName(), realUser);
checkUgiFromAuth(ugi); checkUgiFromAuth(ugi);
// ugi for remote user != real user // if there is remote user via SPNEGO, ignore user.name param
request = getMockRequest(realUser, user, null); request = getMockRequest(realUser, user, null);
try { ugi = JspHelper.getUGI(context, request, conf);
JspHelper.getUGI(context, request, conf); Assert.assertNull(ugi.getRealUser());
Assert.fail("bad request allowed"); Assert.assertEquals(ugi.getShortUserName(), realUser);
} catch (IOException ioe) { checkUgiFromAuth(ugi);
Assert.assertEquals(
"Usernames not matched: name="+user+" != expected="+realUser,
ioe.getMessage());
}
} }
@Test @Test
@ -336,16 +340,15 @@ public class TestJspHelper {
Assert.assertEquals(ugi.getShortUserName(), user); Assert.assertEquals(ugi.getShortUserName(), user);
checkUgiFromAuth(ugi); checkUgiFromAuth(ugi);
// proxy ugi for user via remote user != real user // if there is remote user via SPNEGO, ignore user.name, doas param
request = getMockRequest(realUser, user, user); request = getMockRequest(realUser, user, user);
try { ugi = JspHelper.getUGI(context, request, conf);
JspHelper.getUGI(context, request, conf); Assert.assertNotNull(ugi.getRealUser());
Assert.fail("bad request allowed"); Assert.assertEquals(ugi.getRealUser().getShortUserName(), realUser);
} catch (IOException ioe) { Assert.assertEquals(ugi.getShortUserName(), user);
Assert.assertEquals( checkUgiFromAuth(ugi);
"Usernames not matched: name="+user+" != expected="+realUser,
ioe.getMessage());
}
// try to get get a proxy user with unauthorized user // try to get get a proxy user with unauthorized user
try { try {
@ -368,6 +371,9 @@ public class TestJspHelper {
} }
} }
private HttpServletRequest getMockRequest(String remoteUser, String user, String doAs) { private HttpServletRequest getMockRequest(String remoteUser, String user, String doAs) {
HttpServletRequest request = mock(HttpServletRequest.class); HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getParameter(UserParam.NAME)).thenReturn(user); when(request.getParameter(UserParam.NAME)).thenReturn(user);

View File

@ -20,20 +20,37 @@ package org.apache.hadoop.hdfs.web;
import static org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod.KERBEROS; import static org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod.KERBEROS;
import static org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod.SIMPLE; import static org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod.SIMPLE;
import static org.apache.hadoop.hdfs.client.HdfsClientConfigKeys.DFS_DATA_TRANSFER_PROTECTION_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_HTTPS_KEYSTORE_RESOURCE_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_SERVER_HTTPS_KEYSTORE_RESOURCE_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_BLOCK_ACCESS_TOKEN_ENABLE_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_DELEGATION_TOKEN_ALWAYS_USE_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_HTTP_ADDRESS_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_HTTPS_ADDRESS_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_KERBEROS_PRINCIPAL_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_KEYTAB_FILE_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_HTTP_POLICY_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_HTTP_ADDRESS_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_HTTPS_ADDRESS_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_KERBEROS_PRINCIPAL_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_KEYTAB_FILE_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_WEB_AUTHENTICATION_KERBEROS_PRINCIPAL_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.IGNORE_SECURE_PORTS_FOR_TESTING_KEY;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.URI; import java.net.URI;
import java.net.URL; import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
import java.security.PrivilegedExceptionAction; import java.security.PrivilegedExceptionAction;
import java.util.Map; import java.util.Map;
import java.util.Properties;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.FileSystem;
@ -46,17 +63,18 @@ import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier; import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
import org.apache.hadoop.hdfs.web.resources.*; import org.apache.hadoop.hdfs.web.resources.*;
import org.apache.hadoop.http.HttpConfig; import org.apache.hadoop.http.HttpConfig;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.Text; import org.apache.hadoop.io.Text;
import org.apache.hadoop.net.NetUtils; import org.apache.hadoop.minikdc.MiniKdc;
import org.apache.hadoop.security.SecurityUtil; import org.apache.hadoop.security.SecurityUtil;
import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.authentication.client.ConnectionConfigurator; import org.apache.hadoop.security.authentication.client.ConnectionConfigurator;
import org.apache.hadoop.security.authentication.util.KerberosName;
import org.apache.hadoop.security.ssl.KeyStoreTestUtil; import org.apache.hadoop.security.ssl.KeyStoreTestUtil;
import org.apache.hadoop.security.token.SecretManager.InvalidToken; import org.apache.hadoop.security.token.SecretManager.InvalidToken;
import org.apache.hadoop.test.GenericTestUtils; import org.apache.hadoop.test.GenericTestUtils;
import org.apache.hadoop.test.Whitebox; import org.apache.hadoop.test.Whitebox;
import org.apache.hadoop.security.token.Token; import org.apache.hadoop.security.token.Token;
import org.junit.AfterClass;
import org.junit.Assert; import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -65,9 +83,30 @@ public class TestWebHdfsTokens {
private static Configuration conf; private static Configuration conf;
URI uri = null; URI uri = null;
//secure cluster
private static MiniKdc kdc = null;
private static File baseDir;
private static File keytabFile;
private static String username = "webhdfs-tokens-test";
private static String principal;
private static String keystoresDir;
private static String sslConfDir;
@BeforeClass @BeforeClass
public static void setUp() { public static void setUp() {
conf = new Configuration(); conf = new Configuration();
}
@AfterClass
public static void destroy() throws Exception {
if (kdc != null) {
kdc.stop();
FileUtil.fullyDelete(baseDir);
KeyStoreTestUtil.cleanupSSLConfig(keystoresDir, sslConfDir);
}
}
private static void initEnv(){
SecurityUtil.setAuthenticationMethod(KERBEROS, conf); SecurityUtil.setAuthenticationMethod(KERBEROS, conf);
UserGroupInformation.setConfiguration(conf); UserGroupInformation.setConfiguration(conf);
UserGroupInformation.setLoginUser( UserGroupInformation.setLoginUser(
@ -75,6 +114,64 @@ public class TestWebHdfsTokens {
"LoginUser", new String[]{"supergroup"})); "LoginUser", new String[]{"supergroup"}));
} }
private static void initSecureConf(Configuration secureConf)
throws Exception {
baseDir = GenericTestUtils.getTestDir(
TestWebHdfsTokens.class.getSimpleName());
FileUtil.fullyDelete(baseDir);
assertTrue(baseDir.mkdirs());
Properties kdcConf = MiniKdc.createConf();
kdc = new MiniKdc(kdcConf, baseDir);
kdc.start();
SecurityUtil.setAuthenticationMethod(
UserGroupInformation.AuthenticationMethod.KERBEROS, secureConf);
UserGroupInformation.setConfiguration(secureConf);
KerberosName.resetDefaultRealm();
assertTrue("Expected secureConfiguration to enable security",
UserGroupInformation.isSecurityEnabled());
keytabFile = new File(baseDir, username + ".keytab");
String keytab = keytabFile.getAbsolutePath();
// Windows will not reverse name lookup "127.0.0.1" to "localhost".
String krbInstance = Path.WINDOWS ? "127.0.0.1" : "localhost";
principal = username + "/" + krbInstance + "@" + kdc.getRealm();
String spnegoPrincipal = "HTTP/" + krbInstance + "@" + kdc.getRealm();
kdc.createPrincipal(keytabFile, username, username + "/" + krbInstance,
"HTTP/" + krbInstance);
secureConf.set(DFS_NAMENODE_KERBEROS_PRINCIPAL_KEY, principal);
secureConf.set(DFS_NAMENODE_KEYTAB_FILE_KEY, keytab);
secureConf.set(DFS_DATANODE_KERBEROS_PRINCIPAL_KEY, principal);
secureConf.set(DFS_DATANODE_KEYTAB_FILE_KEY, keytab);
secureConf.set(DFS_WEB_AUTHENTICATION_KERBEROS_PRINCIPAL_KEY,
spnegoPrincipal);
secureConf.setBoolean(DFS_BLOCK_ACCESS_TOKEN_ENABLE_KEY, true);
secureConf.set(DFS_DATA_TRANSFER_PROTECTION_KEY, "authentication");
secureConf.set(DFS_HTTP_POLICY_KEY,
HttpConfig.Policy.HTTP_AND_HTTPS.name());
secureConf.set(DFS_NAMENODE_HTTPS_ADDRESS_KEY, "localhost:0");
secureConf.set(DFS_NAMENODE_HTTP_ADDRESS_KEY, "localhost:0");
secureConf.set(DFS_DATANODE_HTTPS_ADDRESS_KEY, "localhost:0");
secureConf.set(DFS_DATANODE_HTTP_ADDRESS_KEY, "localhost:0");
secureConf.setBoolean(DFS_NAMENODE_DELEGATION_TOKEN_ALWAYS_USE_KEY, true);
secureConf.setBoolean(IGNORE_SECURE_PORTS_FOR_TESTING_KEY, true);
keystoresDir = baseDir.getAbsolutePath();
sslConfDir = KeyStoreTestUtil.getClasspathDir(TestWebHdfsTokens.class);
KeyStoreTestUtil.setupSSLConfig(keystoresDir, sslConfDir,
secureConf, false);
secureConf.set(DFS_CLIENT_HTTPS_KEYSTORE_RESOURCE_KEY,
KeyStoreTestUtil.getClientSSLConfigFileName());
secureConf.set(DFS_SERVER_HTTPS_KEYSTORE_RESOURCE_KEY,
KeyStoreTestUtil.getServerSSLConfigFileName());
}
private WebHdfsFileSystem spyWebhdfsInSecureSetup() throws IOException { private WebHdfsFileSystem spyWebhdfsInSecureSetup() throws IOException {
WebHdfsFileSystem fsOrig = new WebHdfsFileSystem(); WebHdfsFileSystem fsOrig = new WebHdfsFileSystem();
fsOrig.initialize(URI.create("webhdfs://127.0.0.1:0"), conf); fsOrig.initialize(URI.create("webhdfs://127.0.0.1:0"), conf);
@ -84,6 +181,7 @@ public class TestWebHdfsTokens {
@Test(timeout = 5000) @Test(timeout = 5000)
public void testTokenForNonTokenOp() throws IOException { public void testTokenForNonTokenOp() throws IOException {
initEnv();
WebHdfsFileSystem fs = spyWebhdfsInSecureSetup(); WebHdfsFileSystem fs = spyWebhdfsInSecureSetup();
Token<?> token = mock(Token.class); Token<?> token = mock(Token.class);
doReturn(token).when(fs).getDelegationToken(null); doReturn(token).when(fs).getDelegationToken(null);
@ -104,16 +202,19 @@ public class TestWebHdfsTokens {
@Test(timeout = 5000) @Test(timeout = 5000)
public void testNoTokenForGetToken() throws IOException { public void testNoTokenForGetToken() throws IOException {
initEnv();
checkNoTokenForOperation(GetOpParam.Op.GETDELEGATIONTOKEN); checkNoTokenForOperation(GetOpParam.Op.GETDELEGATIONTOKEN);
} }
@Test(timeout = 5000) @Test(timeout = 5000)
public void testNoTokenForRenewToken() throws IOException { public void testNoTokenForRenewToken() throws IOException {
initEnv();
checkNoTokenForOperation(PutOpParam.Op.RENEWDELEGATIONTOKEN); checkNoTokenForOperation(PutOpParam.Op.RENEWDELEGATIONTOKEN);
} }
@Test(timeout = 5000) @Test(timeout = 5000)
public void testNoTokenForCancelToken() throws IOException { public void testNoTokenForCancelToken() throws IOException {
initEnv();
checkNoTokenForOperation(PutOpParam.Op.CANCELDELEGATIONTOKEN); checkNoTokenForOperation(PutOpParam.Op.CANCELDELEGATIONTOKEN);
} }
@ -162,86 +263,42 @@ public class TestWebHdfsTokens {
@Test @Test
public void testLazyTokenFetchForWebhdfs() throws Exception { public void testLazyTokenFetchForWebhdfs() throws Exception {
MiniDFSCluster cluster = null; MiniDFSCluster cluster = null;
WebHdfsFileSystem fs = null; UserGroupInformation ugi = null;
try { try {
final Configuration clusterConf = new HdfsConfiguration(conf); final Configuration clusterConf = new HdfsConfiguration(conf);
SecurityUtil.setAuthenticationMethod(SIMPLE, clusterConf); initSecureConf(clusterConf);
clusterConf.setBoolean(DFSConfigKeys
.DFS_NAMENODE_DELEGATION_TOKEN_ALWAYS_USE_KEY, true);
// trick the NN into thinking security is enabled w/o it trying
// to login from a keytab
UserGroupInformation.setConfiguration(clusterConf);
cluster = new MiniDFSCluster.Builder(clusterConf).numDataNodes(1).build(); cluster = new MiniDFSCluster.Builder(clusterConf).numDataNodes(1).build();
cluster.waitActive(); cluster.waitActive();
SecurityUtil.setAuthenticationMethod(KERBEROS, clusterConf);
UserGroupInformation.setConfiguration(clusterConf);
ugi = UserGroupInformation.loginUserFromKeytabAndReturnUGI(
principal, keytabFile.getAbsolutePath());
//test with swebhdfs
uri = DFSUtil.createUri(
"swebhdfs", cluster.getNameNode().getHttpsAddress());
validateLazyTokenFetch(ugi, clusterConf);
//test with webhdfs
uri = DFSUtil.createUri( uri = DFSUtil.createUri(
"webhdfs", cluster.getNameNode().getHttpAddress()); "webhdfs", cluster.getNameNode().getHttpAddress());
validateLazyTokenFetch(clusterConf); validateLazyTokenFetch(ugi, clusterConf);
} finally { } finally {
IOUtils.cleanup(null, fs);
if (cluster != null) { if (cluster != null) {
cluster.shutdown(); cluster.shutdown();
} }
// Reset UGI so that other tests are not affected.
UserGroupInformation.reset();
UserGroupInformation.setConfiguration(new Configuration());
} }
} }
@Test
public void testLazyTokenFetchForSWebhdfs() throws Exception {
MiniDFSCluster cluster = null;
SWebHdfsFileSystem fs = null;
String keystoresDir;
String sslConfDir;
try {
final Configuration clusterConf = new HdfsConfiguration(conf);
SecurityUtil.setAuthenticationMethod(SIMPLE, clusterConf);
clusterConf.setBoolean(DFSConfigKeys
.DFS_NAMENODE_DELEGATION_TOKEN_ALWAYS_USE_KEY, true);
String baseDir =
GenericTestUtils.getTempPath(TestWebHdfsTokens.class.getSimpleName());
clusterConf.set(DFSConfigKeys.DFS_HTTP_POLICY_KEY, HttpConfig.Policy.HTTPS_ONLY.name());
clusterConf.set(DFSConfigKeys.DFS_NAMENODE_HTTPS_ADDRESS_KEY, "localhost:0");
clusterConf.set(DFSConfigKeys.DFS_DATANODE_HTTPS_ADDRESS_KEY, "localhost:0");
File base = new File(baseDir);
FileUtil.fullyDelete(base);
base.mkdirs();
keystoresDir = new File(baseDir).getAbsolutePath();
sslConfDir = KeyStoreTestUtil.getClasspathDir(TestWebHdfsTokens.class);
KeyStoreTestUtil.setupSSLConfig(keystoresDir, sslConfDir, clusterConf, false);
clusterConf.set(DFSConfigKeys.DFS_CLIENT_HTTPS_KEYSTORE_RESOURCE_KEY,
KeyStoreTestUtil.getClientSSLConfigFileName());
clusterConf.set(DFSConfigKeys.DFS_SERVER_HTTPS_KEYSTORE_RESOURCE_KEY,
KeyStoreTestUtil.getServerSSLConfigFileName());
// trick the NN into thinking security is enabled w/o it trying
// to login from a keytab
UserGroupInformation.setConfiguration(clusterConf);
cluster = new MiniDFSCluster.Builder(clusterConf).numDataNodes(1).build();
cluster.waitActive();
InetSocketAddress addr = cluster.getNameNode().getHttpsAddress();
String nnAddr = NetUtils.getHostPortString(addr);
clusterConf.set(DFSConfigKeys.DFS_NAMENODE_HTTPS_ADDRESS_KEY, nnAddr);
SecurityUtil.setAuthenticationMethod(KERBEROS, clusterConf);
UserGroupInformation.setConfiguration(clusterConf);
uri = DFSUtil.createUri(
"swebhdfs", cluster.getNameNode().getHttpsAddress());
validateLazyTokenFetch(clusterConf);
} finally {
IOUtils.cleanup(null, fs);
if (cluster != null) {
cluster.shutdown();
}
}
KeyStoreTestUtil.cleanupSSLConfig(keystoresDir, sslConfDir);
}
@Test @Test
public void testSetTokenServiceAndKind() throws Exception { public void testSetTokenServiceAndKind() throws Exception {
initEnv();
MiniDFSCluster cluster = null; MiniDFSCluster cluster = null;
try { try {
@ -296,16 +353,20 @@ public class TestWebHdfsTokens {
} }
} }
private void validateLazyTokenFetch(final Configuration clusterConf) throws Exception{ private void validateLazyTokenFetch(UserGroupInformation ugi,
final String testUser = "DummyUser"; final Configuration clusterConf) throws Exception {
UserGroupInformation ugi = UserGroupInformation.createUserForTesting(
testUser, new String[]{"supergroup"}); String testUser = ugi.getShortUserName();
WebHdfsFileSystem fs = ugi.doAs(new PrivilegedExceptionAction<WebHdfsFileSystem>() {
@Override WebHdfsFileSystem fs = ugi.doAs(
public WebHdfsFileSystem run() throws IOException { new PrivilegedExceptionAction<WebHdfsFileSystem>() {
return spy((WebHdfsFileSystem) FileSystem.newInstance(uri, clusterConf)); @Override
} public WebHdfsFileSystem run() throws IOException {
}); return spy((WebHdfsFileSystem) FileSystem.newInstance(uri,
clusterConf));
}
});
// verify token ops don't get a token // verify token ops don't get a token
Assert.assertNull(fs.getRenewToken()); Assert.assertNull(fs.getRenewToken());
Token<?> token = fs.getDelegationToken(null); Token<?> token = fs.getDelegationToken(null);

View File

@ -21,6 +21,7 @@ package org.apache.hadoop.hdfs.web;
import static org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod.KERBEROS; import static org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod.KERBEROS;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import java.io.IOException; import java.io.IOException;
@ -144,37 +145,47 @@ public class TestWebHdfsUrl {
Path fsPath = new Path("/"); Path fsPath = new Path("/");
String tokenString = webhdfs.getDelegationToken().encodeToUrlString(); String tokenString = webhdfs.getDelegationToken().encodeToUrlString();
String userParam = new UserParam(ugi.getShortUserName()).toString();
// send user // send user
URL getTokenUrl = webhdfs.toUrl(GetOpParam.Op.GETDELEGATIONTOKEN, fsPath); URL getTokenUrl = webhdfs.toUrl(GetOpParam.Op.GETDELEGATIONTOKEN, fsPath);
assertTrue("secure webhdfs SHOULD NOT use user.name parameter",
getTokenUrl.toString().indexOf(userParam) == -1);
checkQueryParams( checkQueryParams(
new String[]{ new String[]{
GetOpParam.Op.GETDELEGATIONTOKEN.toQueryString(), GetOpParam.Op.GETDELEGATIONTOKEN.toQueryString(),
new UserParam(ugi.getShortUserName()).toString()
}, },
getTokenUrl); getTokenUrl);
// send user // send user
URL renewTokenUrl = webhdfs.toUrl(PutOpParam.Op.RENEWDELEGATIONTOKEN, URL renewTokenUrl = webhdfs.toUrl(PutOpParam.Op.RENEWDELEGATIONTOKEN,
fsPath, new TokenArgumentParam(tokenString)); fsPath, new TokenArgumentParam(tokenString));
assertTrue("secure webhdfs SHOULD NOT use user.name parameter",
renewTokenUrl.toString().indexOf(userParam) == -1);
checkQueryParams( checkQueryParams(
new String[]{ new String[]{
PutOpParam.Op.RENEWDELEGATIONTOKEN.toQueryString(), PutOpParam.Op.RENEWDELEGATIONTOKEN.toQueryString(),
new UserParam(ugi.getShortUserName()).toString(),
new TokenArgumentParam(tokenString).toString(), new TokenArgumentParam(tokenString).toString(),
}, },
renewTokenUrl); renewTokenUrl);
// send token // send token
URL cancelTokenUrl = webhdfs.toUrl(PutOpParam.Op.CANCELDELEGATIONTOKEN, URL cancelTokenUrl = webhdfs.toUrl(PutOpParam.Op.CANCELDELEGATIONTOKEN,
fsPath, new TokenArgumentParam(tokenString)); fsPath, new TokenArgumentParam(tokenString));
assertTrue("secure webhdfs SHOULD NOT use user.name parameter",
cancelTokenUrl.toString().indexOf(userParam) == -1);
checkQueryParams( checkQueryParams(
new String[]{ new String[]{
PutOpParam.Op.CANCELDELEGATIONTOKEN.toQueryString(), PutOpParam.Op.CANCELDELEGATIONTOKEN.toQueryString(),
new UserParam(ugi.getShortUserName()).toString(),
new TokenArgumentParam(tokenString).toString(), new TokenArgumentParam(tokenString).toString(),
}, },
cancelTokenUrl); cancelTokenUrl);
// send token // send token
URL fileStatusUrl = webhdfs.toUrl(GetOpParam.Op.GETFILESTATUS, fsPath); URL fileStatusUrl = webhdfs.toUrl(GetOpParam.Op.GETFILESTATUS, fsPath);
checkQueryParams( checkQueryParams(
@ -190,14 +201,16 @@ public class TestWebHdfsUrl {
// send user // send user
cancelTokenUrl = webhdfs.toUrl(PutOpParam.Op.CANCELDELEGATIONTOKEN, cancelTokenUrl = webhdfs.toUrl(PutOpParam.Op.CANCELDELEGATIONTOKEN,
fsPath, new TokenArgumentParam(tokenString)); fsPath, new TokenArgumentParam(tokenString));
assertTrue("secure webhdfs SHOULD NOT use user.name parameter",
cancelTokenUrl.toString().indexOf(userParam) == -1);
checkQueryParams( checkQueryParams(
new String[]{ new String[]{
PutOpParam.Op.CANCELDELEGATIONTOKEN.toQueryString(), PutOpParam.Op.CANCELDELEGATIONTOKEN.toQueryString(),
new UserParam(ugi.getShortUserName()).toString(),
new TokenArgumentParam(tokenString).toString(), new TokenArgumentParam(tokenString).toString(),
}, },
cancelTokenUrl); cancelTokenUrl);
// send user // send user
fileStatusUrl = webhdfs.toUrl(GetOpParam.Op.GETFILESTATUS, fsPath); fileStatusUrl = webhdfs.toUrl(GetOpParam.Op.GETFILESTATUS, fsPath);
checkQueryParams( checkQueryParams(
@ -225,40 +238,50 @@ public class TestWebHdfsUrl {
Path fsPath = new Path("/"); Path fsPath = new Path("/");
String tokenString = webhdfs.getDelegationToken().encodeToUrlString(); String tokenString = webhdfs.getDelegationToken().encodeToUrlString();
String userParam = new UserParam(ugi.getRealUser().
getShortUserName()).toString();
// send real+effective // send real+effective
URL getTokenUrl = webhdfs.toUrl(GetOpParam.Op.GETDELEGATIONTOKEN, fsPath); URL getTokenUrl = webhdfs.toUrl(GetOpParam.Op.GETDELEGATIONTOKEN, fsPath);
assertTrue("secure webhdfs SHOULD NOT use user.name parameter",
getTokenUrl.toString().indexOf(userParam) == -1);
checkQueryParams( checkQueryParams(
new String[]{ new String[]{
GetOpParam.Op.GETDELEGATIONTOKEN.toQueryString(), GetOpParam.Op.GETDELEGATIONTOKEN.toQueryString(),
new UserParam(ugi.getRealUser().getShortUserName()).toString(),
new DoAsParam(ugi.getShortUserName()).toString() new DoAsParam(ugi.getShortUserName()).toString()
}, },
getTokenUrl); getTokenUrl);
// send real+effective // send real+effective
URL renewTokenUrl = webhdfs.toUrl(PutOpParam.Op.RENEWDELEGATIONTOKEN, URL renewTokenUrl = webhdfs.toUrl(PutOpParam.Op.RENEWDELEGATIONTOKEN,
fsPath, new TokenArgumentParam(tokenString)); fsPath, new TokenArgumentParam(tokenString));
assertTrue("secure webhdfs SHOULD NOT use user.name parameter",
renewTokenUrl.toString().indexOf(userParam) == -1);
checkQueryParams( checkQueryParams(
new String[]{ new String[]{
PutOpParam.Op.RENEWDELEGATIONTOKEN.toQueryString(), PutOpParam.Op.RENEWDELEGATIONTOKEN.toQueryString(),
new UserParam(ugi.getRealUser().getShortUserName()).toString(),
new DoAsParam(ugi.getShortUserName()).toString(), new DoAsParam(ugi.getShortUserName()).toString(),
new TokenArgumentParam(tokenString).toString(), new TokenArgumentParam(tokenString).toString(),
}, },
renewTokenUrl); renewTokenUrl);
// send token // send token
URL cancelTokenUrl = webhdfs.toUrl(PutOpParam.Op.CANCELDELEGATIONTOKEN, URL cancelTokenUrl = webhdfs.toUrl(PutOpParam.Op.CANCELDELEGATIONTOKEN,
fsPath, new TokenArgumentParam(tokenString)); fsPath, new TokenArgumentParam(tokenString));
assertTrue("secure webhdfs SHOULD NOT use user.name parameter",
cancelTokenUrl.toString().indexOf(userParam) == -1);
checkQueryParams( checkQueryParams(
new String[]{ new String[]{
PutOpParam.Op.CANCELDELEGATIONTOKEN.toQueryString(), PutOpParam.Op.CANCELDELEGATIONTOKEN.toQueryString(),
new UserParam(ugi.getRealUser().getShortUserName()).toString(),
new DoAsParam(ugi.getShortUserName()).toString(), new DoAsParam(ugi.getShortUserName()).toString(),
new TokenArgumentParam(tokenString).toString(), new TokenArgumentParam(tokenString).toString(),
}, },
cancelTokenUrl); cancelTokenUrl);
// send token // send token
URL fileStatusUrl = webhdfs.toUrl(GetOpParam.Op.GETFILESTATUS, fsPath); URL fileStatusUrl = webhdfs.toUrl(GetOpParam.Op.GETFILESTATUS, fsPath);
checkQueryParams( checkQueryParams(
@ -274,15 +297,17 @@ public class TestWebHdfsUrl {
// send real+effective // send real+effective
cancelTokenUrl = webhdfs.toUrl(PutOpParam.Op.CANCELDELEGATIONTOKEN, cancelTokenUrl = webhdfs.toUrl(PutOpParam.Op.CANCELDELEGATIONTOKEN,
fsPath, new TokenArgumentParam(tokenString)); fsPath, new TokenArgumentParam(tokenString));
assertTrue("secure webhdfs SHOULD NOT use user.name parameter",
cancelTokenUrl.toString().indexOf(userParam) == -1);
checkQueryParams( checkQueryParams(
new String[]{ new String[]{
PutOpParam.Op.CANCELDELEGATIONTOKEN.toQueryString(), PutOpParam.Op.CANCELDELEGATIONTOKEN.toQueryString(),
new UserParam(ugi.getRealUser().getShortUserName()).toString(),
new DoAsParam(ugi.getShortUserName()).toString(), new DoAsParam(ugi.getShortUserName()).toString(),
new TokenArgumentParam(tokenString).toString() new TokenArgumentParam(tokenString).toString()
}, },
cancelTokenUrl); cancelTokenUrl);
// send real+effective // send real+effective
fileStatusUrl = webhdfs.toUrl(GetOpParam.Op.GETFILESTATUS, fsPath); fileStatusUrl = webhdfs.toUrl(GetOpParam.Op.GETFILESTATUS, fsPath);
checkQueryParams( checkQueryParams(