Repository: hbase Updated Branches: refs/heads/0.98 6267b919a -> f3e8140f8
HBASE-11960 Provide a sample to show how to use Thrift client authentication Project: http://git-wip-us.apache.org/repos/asf/hbase/repo Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/f3e8140f Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/f3e8140f Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/f3e8140f Branch: refs/heads/0.98 Commit: f3e8140f84d36f7a11be73636793f1270d19b4e4 Parents: 6267b91 Author: Jimmy Xiang <jxi...@cloudera.com> Authored: Mon Sep 15 10:27:41 2014 -0700 Committer: Jimmy Xiang <jxi...@cloudera.com> Committed: Fri Sep 19 09:39:29 2014 -0700 ---------------------------------------------------------------------- hbase-examples/README.txt | 4 +- .../apache/hadoop/hbase/thrift/DemoClient.java | 105 +++++++++++++++---- .../apache/hadoop/hbase/thrift2/DemoClient.java | 92 ++++++++++++++-- 3 files changed, 171 insertions(+), 30 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/hbase/blob/f3e8140f/hbase-examples/README.txt ---------------------------------------------------------------------- diff --git a/hbase-examples/README.txt b/hbase-examples/README.txt index a2e4cf0..700e41f 100644 --- a/hbase-examples/README.txt +++ b/hbase-examples/README.txt @@ -25,8 +25,10 @@ Example code. for f in `find . -name "libthrift-*.jar" -or -name "slf4j-*.jar" -or -name "log4j-*.jar"`; do HBASE_EXAMPLE_CLASSPATH=${HBASE_EXAMPLE_CLASSPATH}:$f; done - 2. Execute: + 2. If HBase server is not secure, or authentication is not enabled for the Thrift server, execute: {java -cp hbase-examples-[VERSION].jar:${HBASE_EXAMPLE_CLASSPATH} org.apache.hadoop.hbase.thrift.DemoClient <host> <port>} + 3. If HBase server is secure, and authentication is enabled for the Thrift server, run kinit at first, then execute: + {java -cp hbase-examples-[VERSION].jar:${HBASE_EXAMPLE_CLASSPATH} org.apache.hadoop.hbase.thrift.DemoClient <host> <port> true} * Ruby: hbase-examples/src/main/ruby/DemoClient.rb 1. Modify the import path in the file to point to {$THRIFT_HOME}/lib/rb/lib. http://git-wip-us.apache.org/repos/asf/hbase/blob/f3e8140f/hbase-examples/src/main/java/org/apache/hadoop/hbase/thrift/DemoClient.java ---------------------------------------------------------------------- diff --git a/hbase-examples/src/main/java/org/apache/hadoop/hbase/thrift/DemoClient.java b/hbase-examples/src/main/java/org/apache/hadoop/hbase/thrift/DemoClient.java index e318cc8..64adc93 100644 --- a/hbase-examples/src/main/java/org/apache/hadoop/hbase/thrift/DemoClient.java +++ b/hbase-examples/src/main/java/org/apache/hadoop/hbase/thrift/DemoClient.java @@ -23,29 +23,34 @@ import java.nio.ByteBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; +import java.security.PrivilegedExceptionAction; import java.text.NumberFormat; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.TreeMap; import java.util.SortedMap; +import java.util.TreeMap; + +import javax.security.auth.Subject; +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; +import javax.security.auth.login.LoginContext; +import javax.security.sasl.Sasl; import org.apache.hadoop.hbase.thrift.generated.AlreadyExists; import org.apache.hadoop.hbase.thrift.generated.ColumnDescriptor; import org.apache.hadoop.hbase.thrift.generated.Hbase; -import org.apache.hadoop.hbase.thrift.generated.IOError; -import org.apache.hadoop.hbase.thrift.generated.IllegalArgument; import org.apache.hadoop.hbase.thrift.generated.Mutation; import org.apache.hadoop.hbase.thrift.generated.TCell; import org.apache.hadoop.hbase.thrift.generated.TRowResult; - -import org.apache.thrift.TException; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.transport.TSaslClientTransport; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; -/* +/** * See the instructions under hbase-examples/README.txt */ public class DemoClient { @@ -54,23 +59,33 @@ public class DemoClient { static protected String host; CharsetDecoder decoder = null; - public static void main(String[] args) - throws IOError, TException, UnsupportedEncodingException, IllegalArgument, AlreadyExists { + private static boolean secure = false; - if (args.length != 2) { + public static void main(String[] args) throws Exception { + + if (args.length < 2 || args.length > 3) { System.out.println("Invalid arguments!"); - System.out.println("Usage: DemoClient host port"); + System.out.println("Usage: DemoClient host port [secure=false]"); System.exit(-1); } port = Integer.parseInt(args[1]); host = args[0]; + if (args.length > 2) { + secure = Boolean.parseBoolean(args[2]); + } - - DemoClient client = new DemoClient(); - client.run(); + final DemoClient client = new DemoClient(); + Subject.doAs(getSubject(), + new PrivilegedExceptionAction<Void>() { + @Override + public Void run() throws Exception { + client.run(); + return null; + } + }); } DemoClient() { @@ -96,15 +111,28 @@ public class DemoClient { } } - private void run() throws IOError, TException, IllegalArgument, - AlreadyExists { - + private void run() throws Exception { TTransport transport = new TSocket(host, port); - TProtocol protocol = new TBinaryProtocol(transport, true, true); - Hbase.Client client = new Hbase.Client(protocol); + if (secure) { + Map<String, String> saslProperties = new HashMap<String, String>(); + saslProperties.put(Sasl.QOP, "auth-conf,auth-int,auth"); + /** + * The Thrift server the DemoClient is trying to connect to + * must have a matching principal, and support authentication. + * + * The HBase cluster must be secure, allow proxy user. + */ + transport = new TSaslClientTransport("GSSAPI", null, + "hbase", // Thrift server user name, should be an authorized proxy user. + host, // Thrift server domain + saslProperties, null, transport); + } transport.open(); + TProtocol protocol = new TBinaryProtocol(transport, true, true); + Hbase.Client client = new Hbase.Client(protocol); + byte[] t = bytes("demo_table"); // @@ -130,10 +158,12 @@ public class DemoClient { ColumnDescriptor col; col = new ColumnDescriptor(); col.name = ByteBuffer.wrap(bytes("entry:")); + col.timeToLive = Integer.MAX_VALUE; col.maxVersions = 10; columns.add(col); col = new ColumnDescriptor(); col.name = ByteBuffer.wrap(bytes("unused:")); + col.timeToLive = Integer.MAX_VALUE; columns.add(col); System.out.println("creating table: " + utf8(t)); @@ -165,9 +195,9 @@ public class DemoClient { ArrayList<Mutation> mutations; // non-utf8 is fine for data mutations = new ArrayList<Mutation>(); - mutations.add(new Mutation(false, ByteBuffer.wrap(bytes("entry:foo")), + mutations.add(new Mutation(false, ByteBuffer.wrap(bytes("entry:foo")), ByteBuffer.wrap(invalid), writeToWal)); - client.mutateRow(ByteBuffer.wrap(t), ByteBuffer.wrap(bytes("foo")), + client.mutateRow(ByteBuffer.wrap(t), ByteBuffer.wrap(bytes("foo")), mutations, dummyAttributes); @@ -337,4 +367,39 @@ public class DemoClient { printRow(rowResult); } } + + static Subject getSubject() throws Exception { + if (!secure) return new Subject(); + + /* + * To authenticate the DemoClient, kinit should be invoked ahead. + * Here we try to get the Kerberos credential from the ticket cache. + */ + LoginContext context = new LoginContext("", new Subject(), null, + new Configuration() { + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String name) { + Map<String, String> options = new HashMap<String, String>(); + options.put("useKeyTab", "false"); + options.put("storeKey", "false"); + options.put("doNotPrompt", "true"); + options.put("useTicketCache", "true"); + options.put("renewTGT", "true"); + options.put("refreshKrb5Config", "true"); + options.put("isInitiator", "true"); + String ticketCache = System.getenv("KRB5CCNAME"); + if (ticketCache != null) { + options.put("ticketCache", ticketCache); + } + options.put("debug", "true"); + + return new AppConfigurationEntry[]{ + new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule", + AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, + options)}; + } + }); + context.login(); + return context.getSubject(); + } } http://git-wip-us.apache.org/repos/asf/hbase/blob/f3e8140f/hbase-examples/src/main/java/org/apache/hadoop/hbase/thrift2/DemoClient.java ---------------------------------------------------------------------- diff --git a/hbase-examples/src/main/java/org/apache/hadoop/hbase/thrift2/DemoClient.java b/hbase-examples/src/main/java/org/apache/hadoop/hbase/thrift2/DemoClient.java index a1a9632..72c6929 100644 --- a/hbase-examples/src/main/java/org/apache/hadoop/hbase/thrift2/DemoClient.java +++ b/hbase-examples/src/main/java/org/apache/hadoop/hbase/thrift2/DemoClient.java @@ -19,30 +19,40 @@ package org.apache.hadoop.hbase.thrift2; import java.nio.ByteBuffer; +import java.security.PrivilegedExceptionAction; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; + +import javax.security.auth.Subject; +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; +import javax.security.auth.login.LoginContext; +import javax.security.sasl.Sasl; import org.apache.hadoop.hbase.thrift2.generated.TColumnValue; import org.apache.hadoop.hbase.thrift2.generated.TGet; import org.apache.hadoop.hbase.thrift2.generated.THBaseService; -import org.apache.hadoop.hbase.thrift2.generated.TIOError; import org.apache.hadoop.hbase.thrift2.generated.TPut; import org.apache.hadoop.hbase.thrift2.generated.TResult; -import org.apache.thrift.TException; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.transport.TFramedTransport; +import org.apache.thrift.transport.TSaslClientTransport; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; public class DemoClient { - public static void main(String[] args) throws TIOError, TException { + + private static String host = "localhost"; + private static int port = 9090; + private static boolean secure = false; + + public static void main(String[] args) throws Exception { System.out.println("Thrift2 Demo"); - System.out.println("Usage: DemoClient [host=localhost] [port=9090]"); + System.out.println("Usage: DemoClient [host=localhost] [port=9090] [secure=false]"); System.out.println("This demo assumes you have a table called \"example\" with a column family called \"family1\""); - - String host = "localhost"; - int port = 9090; // use passed in arguments instead of defaults if (args.length >= 1) { @@ -51,21 +61,50 @@ public class DemoClient { if (args.length >= 2) { port = Integer.parseInt(args[1]); } + if (args.length >= 3) { + secure = Boolean.parseBoolean(args[2]); + } + final DemoClient client = new DemoClient(); + Subject.doAs(getSubject(), + new PrivilegedExceptionAction<Void>() { + @Override + public Void run() throws Exception { + client.run(); + return null; + } + }); + } + + public void run() throws Exception { int timeout = 10000; boolean framed = false; TTransport transport = new TSocket(host, port, timeout); if (framed) { transport = new TFramedTransport(transport); + } else if (secure) { + /** + * The Thrift server the DemoClient is trying to connect to + * must have a matching principal, and support authentication. + * + * The HBase cluster must be secure, allow proxy user. + */ + Map<String, String> saslProperties = new HashMap<String, String>(); + saslProperties.put(Sasl.QOP, "auth-conf,auth-int,auth"); + transport = new TSaslClientTransport("GSSAPI", null, + "hbase", // Thrift server user name, should be an authorized proxy user. + host, // Thrift server domain + saslProperties, null, transport); } + TProtocol protocol = new TBinaryProtocol(transport); // This is our thrift client. THBaseService.Iface client = new THBaseService.Client(protocol); // open the transport transport.open(); - + ByteBuffer table = ByteBuffer.wrap("example".getBytes()); TPut put = new TPut(); @@ -93,7 +132,42 @@ public class DemoClient { System.out.print("value = " + new String(resultColumnValue.getValue())); System.out.print("timestamp = " + resultColumnValue.getTimestamp()); } - + transport.close(); } + + static Subject getSubject() throws Exception { + if (!secure) return new Subject(); + + /* + * To authenticate the DemoClient, kinit should be invoked ahead. + * Here we try to get the Kerberos credential from the ticket cache. + */ + LoginContext context = new LoginContext("", new Subject(), null, + new Configuration() { + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String name) { + Map<String, String> options = new HashMap<String, String>(); + options.put("useKeyTab", "false"); + options.put("storeKey", "false"); + options.put("doNotPrompt", "true"); + options.put("useTicketCache", "true"); + options.put("renewTGT", "true"); + options.put("refreshKrb5Config", "true"); + options.put("isInitiator", "true"); + String ticketCache = System.getenv("KRB5CCNAME"); + if (ticketCache != null) { + options.put("ticketCache", ticketCache); + } + options.put("debug", "true"); + + return new AppConfigurationEntry[]{ + new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule", + AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, + options)}; + } + }); + context.login(); + return context.getSubject(); + } }