From fe2c4f630d3b5f3346c9ee9f95c256186c9e6907 Mon Sep 17 00:00:00 2001 From: "Apekshit(Appy) Sharma" Date: Thu, 10 Sep 2015 12:32:24 -0700 Subject: [PATCH] HBASE-14400 Fix HBase RPC protection documentation Signed-off-by: Andrew Purtell --- .../hbase/security/SaslClientHandler.java | 3 +- .../hadoop/hbase/security/SaslUtil.java | 44 +++++++++++++++---- .../security/TestHBaseSaslRpcClient.java | 10 +++++ .../hadoop/hbase/thrift2/ThriftServer.java | 23 +++++----- src/main/asciidoc/_chapters/security.adoc | 10 ++--- 5 files changed, 64 insertions(+), 26 deletions(-) diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/SaslClientHandler.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/SaslClientHandler.java index e2c9e025b3f..f52987b53dd 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/SaslClientHandler.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/SaslClientHandler.java @@ -74,7 +74,8 @@ public class SaslClientHandler extends ChannelDuplexHandler { * @param token for Sasl * @param serverPrincipal Server's Kerberos principal name * @param fallbackAllowed True if server may also fall back to less secure connection - * @param rpcProtection Quality of protection. Integrity or privacy + * @param rpcProtection Quality of protection. Can be 'authentication', 'integrity' or + * 'privacy'. * @param exceptionHandler handler for exceptions * @param successfulConnectHandler handler for succesful connects * @throws java.io.IOException if handler could not be created diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/SaslUtil.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/SaslUtil.java index 726a75364c0..f2f33938e1b 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/SaslUtil.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/SaslUtil.java @@ -24,10 +24,13 @@ import java.util.TreeMap; import javax.security.sasl.Sasl; import org.apache.commons.codec.binary.Base64; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.classification.InterfaceAudience; @InterfaceAudience.Private public class SaslUtil { + private static final Log log = LogFactory.getLog(SaslUtil.class); public static final String SASL_DEFAULT_REALM = "default"; public static final Map SASL_PROPS = new TreeMap(); @@ -66,16 +69,41 @@ public class SaslUtil { return new String(Base64.encodeBase64(password)).toCharArray(); } - static void initSaslProperties(String rpcProtection) { - QualityOfProtection saslQOP = QualityOfProtection.AUTHENTICATION; - if (QualityOfProtection.INTEGRITY.name().toLowerCase() - .equals(rpcProtection)) { - saslQOP = QualityOfProtection.INTEGRITY; - } else if (QualityOfProtection.PRIVACY.name().toLowerCase().equals( - rpcProtection)) { - saslQOP = QualityOfProtection.PRIVACY; + /** + * Returns {@link org.apache.hadoop.hbase.security.SaslUtil.QualityOfProtection} + * corresponding to the given {@code stringQop} value. Returns null if value is + * invalid. + */ + public static QualityOfProtection getQop(String stringQop) { + QualityOfProtection qop = null; + if (QualityOfProtection.AUTHENTICATION.name().toLowerCase().equals(stringQop) + || QualityOfProtection.AUTHENTICATION.saslQop.equals(stringQop)) { + qop = QualityOfProtection.AUTHENTICATION; + } else if (QualityOfProtection.INTEGRITY.name().toLowerCase().equals(stringQop) + || QualityOfProtection.INTEGRITY.saslQop.equals(stringQop)) { + qop = QualityOfProtection.INTEGRITY; + } else if (QualityOfProtection.PRIVACY.name().toLowerCase().equals(stringQop) + || QualityOfProtection.PRIVACY.saslQop.equals(stringQop)) { + qop = QualityOfProtection.PRIVACY; } + if (qop == null) { + throw new IllegalArgumentException("Invalid qop: " + stringQop + + ". It must be one of 'authentication', 'integrity', 'privacy'."); + } + if (QualityOfProtection.AUTHENTICATION.saslQop.equals(stringQop) + || QualityOfProtection.INTEGRITY.saslQop.equals(stringQop) + || QualityOfProtection.PRIVACY.saslQop.equals(stringQop)) { + log.warn("Use authentication/integrity/privacy as value for rpc protection " + + "configurations instead of auth/auth-int/auth-conf."); + } + return qop; + } + static void initSaslProperties(String rpcProtection) { + QualityOfProtection saslQOP = getQop(rpcProtection); + if (saslQOP == null) { + saslQOP = QualityOfProtection.AUTHENTICATION; + } SaslUtil.SASL_PROPS.put(Sasl.QOP, saslQOP.getSaslQop()); SaslUtil.SASL_PROPS.put(Sasl.SERVER_AUTH, "true"); } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/TestHBaseSaslRpcClient.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/TestHBaseSaslRpcClient.java index 21450a2f599..96ed9864133 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/TestHBaseSaslRpcClient.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/TestHBaseSaslRpcClient.java @@ -53,8 +53,10 @@ import org.apache.hadoop.security.token.TokenIdentifier; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; +import org.junit.rules.ExpectedException; import org.mockito.Mockito; import com.google.common.base.Strings; @@ -72,6 +74,10 @@ public class TestHBaseSaslRpcClient { private static final Logger LOG = Logger.getLogger(TestHBaseSaslRpcClient.class); + + @Rule + public ExpectedException exception = ExpectedException.none(); + @BeforeClass public static void before() { Logger.getRootLogger().setLevel(Level.DEBUG); @@ -101,6 +107,10 @@ public class TestHBaseSaslRpcClient { "integrity"); assertTrue(SaslUtil.SASL_PROPS.get(Sasl.QOP).equals(SaslUtil.QualityOfProtection. INTEGRITY.getSaslQop())); + + exception.expect(IllegalArgumentException.class); + new HBaseSaslRpcClient(AuthMethod.DIGEST, token, "principal/host@DOMAIN.COM", false, + "wrongvalue"); } @Test diff --git a/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/ThriftServer.java b/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/ThriftServer.java index 632513bd7ea..3c9716695e9 100644 --- a/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/ThriftServer.java +++ b/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/ThriftServer.java @@ -54,6 +54,7 @@ import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.HBaseInterfaceAudience; import org.apache.hadoop.hbase.filter.ParseFilter; import org.apache.hadoop.hbase.http.InfoServer; +import org.apache.hadoop.hbase.security.SaslUtil; import org.apache.hadoop.hbase.security.SecurityUtil; import org.apache.hadoop.hbase.security.UserProvider; import org.apache.hadoop.hbase.thrift.CallQueue; @@ -98,9 +99,9 @@ public class ThriftServer extends Configured implements Tool { /** * Thrift quality of protection configuration key. Valid values can be: - * auth-conf: authentication, integrity and confidentiality checking - * auth-int: authentication and integrity checking - * auth: authentication only + * privacy: authentication, integrity and confidentiality checking + * integrity: authentication and integrity checking + * authentication: authentication only * * This is used to authenticate the callers and support impersonation. * The thrift server and the HBase cluster must run in secure mode. @@ -161,7 +162,8 @@ public class ThriftServer extends Configured implements Tool { } private static TTransportFactory getTTransportFactory( - String qop, String name, String host, boolean framed, int frameSize) { + SaslUtil.QualityOfProtection qop, String name, String host, + boolean framed, int frameSize) { if (framed) { if (qop != null) { throw new RuntimeException("Thrift server authentication" @@ -173,7 +175,7 @@ public class ThriftServer extends Configured implements Tool { return new TTransportFactory(); } else { Map saslProperties = new HashMap(); - saslProperties.put(Sasl.QOP, qop); + saslProperties.put(Sasl.QOP, qop.getSaslQop()); TSaslServerTransport.Factory saslFactory = new TSaslServerTransport.Factory(); saslFactory.addServerDefinition("GSSAPI", name, host, saslProperties, new SaslGssCallbackHandler() { @@ -377,13 +379,10 @@ public class ThriftServer extends Configured implements Tool { } UserGroupInformation realUser = userProvider.getCurrent().getUGI(); - String qop = conf.get(THRIFT_QOP_KEY); - if (qop != null) { - if (!qop.equals("auth") && !qop.equals("auth-int") - && !qop.equals("auth-conf")) { - throw new IOException("Invalid " + THRIFT_QOP_KEY + ": " + qop - + ", it must be 'auth', 'auth-int', or 'auth-conf'"); - } + String stringQop = conf.get(THRIFT_QOP_KEY); + SaslUtil.QualityOfProtection qop = null; + if (stringQop != null) { + qop = SaslUtil.getQop(stringQop); if (!securityEnabled) { throw new IOException("Thrift server must" + " run in secure mode to support authentication"); diff --git a/src/main/asciidoc/_chapters/security.adoc b/src/main/asciidoc/_chapters/security.adoc index 40e8e168e69..3d9082c73b4 100644 --- a/src/main/asciidoc/_chapters/security.adoc +++ b/src/main/asciidoc/_chapters/security.adoc @@ -222,9 +222,9 @@ To enable it, do the following. . Be sure that HBase is configured to allow proxy users, as described in <>. . In _hbase-site.xml_ for each cluster node running a Thrift gateway, set the property `hbase.thrift.security.qop` to one of the following three values: + -* `auth-conf` - authentication, integrity, and confidentiality checking -* `auth-int` - authentication and integrity checking -* `auth` - authentication checking only +* `privacy` - authentication, integrity, and confidentiality checking. +* `integrity` - authentication and integrity checking +* `authentication` - authentication checking only . Restart the Thrift gateway processes for the changes to take effect. If a node is running Thrift, the output of the `jps` command will list a `ThriftServer` process. @@ -765,7 +765,7 @@ For an example of using both together, see <>. ---- + -Optionally, you can enable transport security, by setting `hbase.rpc.protection` to `auth-conf`. +Optionally, you can enable transport security, by setting `hbase.rpc.protection` to `privacy`. This requires HBase 0.98.4 or newer. . Set up the Hadoop group mapper in the Hadoop namenode's _core-site.xml_. @@ -1668,7 +1668,7 @@ All options have been discussed separately in the sections above. hbase.rpc.protection - auth-conf + privacy