From f3132eee1011b750158169c099b26ce8f6e2d1f4 Mon Sep 17 00:00:00 2001 From: "Aaron T. Myers" Date: Mon, 13 Oct 2014 18:09:39 -0700 Subject: [PATCH] HADOOP-11176. KMSClientProvider authentication fails when both currentUgi and loginUgi are a proxied user. Contributed by Arun Suresh. (cherry picked from commit 0e57aa3bf689374736939300d8f3525ec38bead7) --- .../hadoop-common/CHANGES.txt | 3 + .../crypto/key/kms/KMSClientProvider.java | 15 +- .../hadoop/crypto/key/kms/server/TestKMS.java | 154 +++++++++++++++++- 3 files changed, 155 insertions(+), 17 deletions(-) diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt index 4ad8f669af4..5e0fb552f79 100644 --- a/hadoop-common-project/hadoop-common/CHANGES.txt +++ b/hadoop-common-project/hadoop-common/CHANGES.txt @@ -486,6 +486,9 @@ Release 2.6.0 - UNRELEASED HADOOP-11193. Fix uninitialized variables in NativeIO.c (Xiaoyu Yao via wheat9) + HADOOP-11176. KMSClientProvider authentication fails when both currentUgi + and loginUgi are a proxied user. (Arun Suresh via atm) + BREAKDOWN OF HDFS-6134 AND HADOOP-10150 SUBTASKS AND RELATED JIRAS HADOOP-10734. Implement high-performance secure random number sources. diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/kms/KMSClientProvider.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/kms/KMSClientProvider.java index 537fd973529..5c332a8541b 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/kms/KMSClientProvider.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/kms/KMSClientProvider.java @@ -251,8 +251,8 @@ public class KMSClientProvider extends KeyProvider implements CryptoExtension, private SSLFactory sslFactory; private ConnectionConfigurator configurator; private DelegationTokenAuthenticatedURL.Token authToken; - private UserGroupInformation loginUgi; private final int authRetry; + private final UserGroupInformation actualUgi; @Override public String toString() { @@ -336,7 +336,11 @@ public class KMSClientProvider extends KeyProvider implements CryptoExtension, KMS_CLIENT_ENC_KEY_CACHE_NUM_REFILL_THREADS_DEFAULT), new EncryptedQueueRefiller()); authToken = new DelegationTokenAuthenticatedURL.Token(); - loginUgi = UserGroupInformation.getCurrentUser(); + actualUgi = + (UserGroupInformation.getCurrentUser().getAuthenticationMethod() == + UserGroupInformation.AuthenticationMethod.PROXY) ? UserGroupInformation + .getCurrentUser().getRealUser() : UserGroupInformation + .getCurrentUser(); } private String createServiceURL(URL url) throws IOException { @@ -407,7 +411,7 @@ public class KMSClientProvider extends KeyProvider implements CryptoExtension, ? currentUgi.getShortUserName() : null; // creating the HTTP connection using the current UGI at constructor time - conn = loginUgi.doAs(new PrivilegedExceptionAction() { + conn = actualUgi.doAs(new PrivilegedExceptionAction() { @Override public HttpURLConnection run() throws Exception { DelegationTokenAuthenticatedURL authUrl = @@ -457,8 +461,6 @@ public class KMSClientProvider extends KeyProvider implements CryptoExtension, // WWW-Authenticate header as well).. KMSClientProvider.this.authToken = new DelegationTokenAuthenticatedURL.Token(); - KMSClientProvider.this.loginUgi = - UserGroupInformation.getCurrentUser(); if (authRetryCount > 0) { String contentType = conn.getRequestProperty(CONTENT_TYPE); String requestMethod = conn.getRequestMethod(); @@ -475,9 +477,6 @@ public class KMSClientProvider extends KeyProvider implements CryptoExtension, // Ignore the AuthExceptions.. since we are just using the method to // extract and set the authToken.. (Workaround till we actually fix // AuthenticatedURL properly to set authToken post initialization) - } finally { - KMSClientProvider.this.loginUgi = - UserGroupInformation.getCurrentUser(); } HttpExceptionUtils.validateResponse(conn, expectedResponse); if (APPLICATION_JSON_MIME.equalsIgnoreCase(conn.getContentType()) diff --git a/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMS.java b/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMS.java index ad2f500f746..4628e3696a5 100644 --- a/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMS.java +++ b/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMS.java @@ -1585,22 +1585,34 @@ public class TestKMS { } @Test - public void testProxyUser() throws Exception { + public void testProxyUserKerb() throws Exception { + doProxyUserTest(true); + } + + @Test + public void testProxyUserSimple() throws Exception { + doProxyUserTest(false); + } + + public void doProxyUserTest(final boolean kerberos) throws Exception { Configuration conf = new Configuration(); conf.set("hadoop.security.authentication", "kerberos"); UserGroupInformation.setConfiguration(conf); final File testDir = getTestDir(); conf = createBaseKMSConf(testDir); - conf.set("hadoop.kms.authentication.type", "kerberos"); + if (kerberos) { + conf.set("hadoop.kms.authentication.type", "kerberos"); + } conf.set("hadoop.kms.authentication.kerberos.keytab", keytab.getAbsolutePath()); conf.set("hadoop.kms.authentication.kerberos.principal", "HTTP/localhost"); conf.set("hadoop.kms.authentication.kerberos.name.rules", "DEFAULT"); - conf.set("hadoop.kms.proxyuser.client.users", "foo"); + conf.set("hadoop.kms.proxyuser.client.users", "foo,bar"); conf.set("hadoop.kms.proxyuser.client.hosts", "*"); - conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "kAA.ALL", "*"); - conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "kBB.ALL", "*"); - conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "kCC.ALL", "*"); + conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "kAA.ALL", "client"); + conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "kBB.ALL", "foo"); + conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "kCC.ALL", "foo1"); + conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "kDD.ALL", "bar"); writeConf(testDir, conf); @@ -1611,9 +1623,16 @@ public class TestKMS { conf.setInt(KeyProvider.DEFAULT_BITLENGTH_NAME, 64); final URI uri = createKMSUri(getKMSUrl()); - // proxyuser client using kerberos credentials - final UserGroupInformation clientUgi = UserGroupInformation. - loginUserFromKeytabAndReturnUGI("client", keytab.getAbsolutePath()); + UserGroupInformation proxyUgi = null; + if (kerberos) { + // proxyuser client using kerberos credentials + proxyUgi = UserGroupInformation. + loginUserFromKeytabAndReturnUGI("client", keytab.getAbsolutePath()); + } else { + proxyUgi = UserGroupInformation.createRemoteUser("client"); + } + + final UserGroupInformation clientUgi = proxyUgi; clientUgi.doAs(new PrivilegedExceptionAction() { @Override public Void run() throws Exception { @@ -1649,6 +1668,123 @@ public class TestKMS { return null; } }); + + // authorized proxyuser + UserGroupInformation barUgi = + UserGroupInformation.createProxyUser("bar", clientUgi); + barUgi.doAs(new PrivilegedExceptionAction() { + @Override + public Void run() throws Exception { + Assert.assertNotNull(kp.createKey("kDD", + new KeyProvider.Options(conf))); + return null; + } + }); + return null; + } + }); + + return null; + } + }); + } + + @Test + public void testWebHDFSProxyUserKerb() throws Exception { + doWebHDFSProxyUserTest(true); + } + + @Test + public void testWebHDFSProxyUserSimple() throws Exception { + doWebHDFSProxyUserTest(false); + } + + public void doWebHDFSProxyUserTest(final boolean kerberos) throws Exception { + Configuration conf = new Configuration(); + conf.set("hadoop.security.authentication", "kerberos"); + UserGroupInformation.setConfiguration(conf); + final File testDir = getTestDir(); + conf = createBaseKMSConf(testDir); + if (kerberos) { + conf.set("hadoop.kms.authentication.type", "kerberos"); + } + conf.set("hadoop.kms.authentication.kerberos.keytab", + keytab.getAbsolutePath()); + conf.set("hadoop.kms.authentication.kerberos.principal", "HTTP/localhost"); + conf.set("hadoop.kms.authentication.kerberos.name.rules", "DEFAULT"); + conf.set("hadoop.security.kms.client.timeout", "300"); + conf.set("hadoop.kms.proxyuser.client.users", "foo,bar"); + conf.set("hadoop.kms.proxyuser.client.hosts", "*"); + conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "kAA.ALL", "foo"); + conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "kBB.ALL", "foo1"); + conf.set(KeyAuthorizationKeyProvider.KEY_ACL + "kCC.ALL", "bar"); + + writeConf(testDir, conf); + + runServer(null, null, testDir, new KMSCallable() { + @Override + public Void call() throws Exception { + final Configuration conf = new Configuration(); + conf.setInt(KeyProvider.DEFAULT_BITLENGTH_NAME, 64); + final URI uri = createKMSUri(getKMSUrl()); + + UserGroupInformation proxyUgi = null; + if (kerberos) { + // proxyuser client using kerberos credentials + proxyUgi = UserGroupInformation. + loginUserFromKeytabAndReturnUGI("client", keytab.getAbsolutePath()); + } else { + proxyUgi = UserGroupInformation.createRemoteUser("client"); + } + + final UserGroupInformation clientUgi = proxyUgi; + clientUgi.doAs(new PrivilegedExceptionAction() { + @Override + public Void run() throws Exception { + + // authorized proxyuser + UserGroupInformation fooUgi = + UserGroupInformation.createProxyUser("foo", clientUgi); + fooUgi.doAs(new PrivilegedExceptionAction() { + @Override + public Void run() throws Exception { + KeyProvider kp = new KMSClientProvider(uri, conf); + Assert.assertNotNull(kp.createKey("kAA", + new KeyProvider.Options(conf))); + return null; + } + }); + + // unauthorized proxyuser + UserGroupInformation foo1Ugi = + UserGroupInformation.createProxyUser("foo1", clientUgi); + foo1Ugi.doAs(new PrivilegedExceptionAction() { + @Override + public Void run() throws Exception { + try { + KeyProvider kp = new KMSClientProvider(uri, conf); + kp.createKey("kBB", new KeyProvider.Options(conf)); + Assert.fail(); + } catch (Exception ex) { + Assert.assertTrue(ex.getMessage(), ex.getMessage().contains("Forbidden")); + } + return null; + } + }); + + // authorized proxyuser + UserGroupInformation barUgi = + UserGroupInformation.createProxyUser("bar", clientUgi); + barUgi.doAs(new PrivilegedExceptionAction() { + @Override + public Void run() throws Exception { + KeyProvider kp = new KMSClientProvider(uri, conf); + Assert.assertNotNull(kp.createKey("kCC", + new KeyProvider.Options(conf))); + return null; + } + }); + return null; } });