hbase-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From zg...@apache.org
Subject [hbase] 81/133: HBASE-17860 Implement secure native client connection
Date Tue, 12 Mar 2019 12:46:09 GMT
This is an automated email from the ASF dual-hosted git repository.

zghao pushed a commit to branch HBASE-14850
in repository https://gitbox.apache.org/repos/asf/hbase.git

commit f54794c498ede4ec76f3ff1f20b849b1e287953a
Author: tedyu <yuzhihong@gmail.com>
AuthorDate: Tue May 30 14:31:23 2017 -0700

    HBASE-17860 Implement secure native client connection
---
 hbase-native-client/bin/start-docker.sh            |   6 +-
 hbase-native-client/connection/BUCK                |  18 +-
 hbase-native-client/connection/client-handler.cc   |   6 +-
 .../connection/connection-factory.cc               |  10 +-
 .../connection/connection-factory.h                |   5 +-
 .../connection/connection-pool-test.cc             |   2 +-
 hbase-native-client/connection/connection-pool.cc  |   8 +-
 hbase-native-client/connection/connection-pool.h   |   4 +-
 hbase-native-client/connection/pipeline.cc         |  10 +-
 hbase-native-client/connection/pipeline.h          |   4 +-
 hbase-native-client/connection/rpc-client.cc       |   7 +-
 hbase-native-client/connection/rpc-client.h        |   3 +-
 hbase-native-client/connection/sasl-handler.cc     | 224 +++++++++++++++++++++
 hbase-native-client/connection/sasl-handler.h      |  78 +++++++
 hbase-native-client/connection/sasl-util.cc        |  92 +++++++++
 .../{security/user.h => connection/sasl-util.h}    |  35 ++--
 hbase-native-client/core/BUCK                      | 104 +++++++---
 hbase-native-client/core/async-connection.cc       |   4 +-
 .../core/async-rpc-retrying-test.cc                |   3 +-
 hbase-native-client/core/location-cache-test.cc    |   6 +-
 hbase-native-client/docker-files/Dockerfile        |  88 ++++++++
 hbase-native-client/docker-files/krb5.conf         |  20 ++
 hbase-native-client/security/BUCK                  |   8 +-
 hbase-native-client/security/user.h                |   1 +
 hbase-native-client/serde/BUCK                     |  56 ++++--
 .../serde/client-serializer-test.cc                |   4 +-
 hbase-native-client/serde/rpc.cc                   |  13 +-
 hbase-native-client/serde/rpc.h                    |   3 +-
 hbase-native-client/third-party/BUCK               |   7 +-
 hbase-native-client/utils/BUCK                     |  26 ++-
 hbase-native-client/utils/user-util-test.cc        |   2 +-
 hbase-native-client/utils/user-util.cc             |  37 +++-
 hbase-native-client/utils/user-util.h              |   4 +-
 33 files changed, 784 insertions(+), 114 deletions(-)

diff --git a/hbase-native-client/bin/start-docker.sh b/hbase-native-client/bin/start-docker.sh
index 8b017a0..53325c1 100755
--- a/hbase-native-client/bin/start-docker.sh
+++ b/hbase-native-client/bin/start-docker.sh
@@ -52,11 +52,11 @@ fi;
 # Build the image
 # 
 # This shouldn't be needed after the development environment is a little more stable.
-docker build -t hbase_native .
+docker build -t hbase_native -f docker-files/Dockerfile .
 
 # After the image is built run the thing
-docker run -p 16050:16050/tcp \
+docker run -h="securecluster" -p 16050:16050/tcp \
          -v ${BASE_DIR}/..:/usr/src/hbase \
            -v ~/.m2:/root/.m2 \
-           -it hbase_native  /bin/bash
+         -it hbase_native /bin/bash
 popd
diff --git a/hbase-native-client/connection/BUCK b/hbase-native-client/connection/BUCK
index 36111f8..c3119eb 100644
--- a/hbase-native-client/connection/BUCK
+++ b/hbase-native-client/connection/BUCK
@@ -22,6 +22,7 @@ cxx_library(
     exported_headers=[
         "client-dispatcher.h",
         "client-handler.h",
+        "sasl-handler.h",
         "connection-factory.h",
         "connection-pool.h",
         "connection-id.h",
@@ -31,6 +32,7 @@ cxx_library(
         "response.h",
         "service.h",
         "rpc-client.h",
+        "sasl-util.h",
     ],
     srcs=[
         "client-dispatcher.cc",
@@ -40,6 +42,8 @@ cxx_library(
         "pipeline.cc",
         "request.cc",
         "rpc-client.cc",
+        "sasl-handler.cc",
+        "sasl-util.cc",
     ],
     deps=[
         "//if:if",
@@ -51,8 +55,16 @@ cxx_library(
         "//exceptions:exceptions",
     ],
     compiler_flags=['-Weffc++'],
-    visibility=['//core/...',],)
+    linker_flags=['-L/usr/local/lib', '-lsasl2', '-lkrb5'],
+    exported_linker_flags=['-L/usr/local/lib', '-lsasl2', '-lkrb5'],
+    visibility=[
+        '//core/...',
+    ],)
 cxx_test(
     name="connection-pool-test",
-    srcs=["connection-pool-test.cc",],
-    deps=[":connection",],)
+    srcs=[
+        "connection-pool-test.cc",
+    ],
+    deps=[
+        ":connection",
+    ],)
diff --git a/hbase-native-client/connection/client-handler.cc b/hbase-native-client/connection/client-handler.cc
index 113ebd0..e60382d 100644
--- a/hbase-native-client/connection/client-handler.cc
+++ b/hbase-native-client/connection/client-handler.cc
@@ -128,11 +128,9 @@ Future<Unit> ClientHandler::write(Context *ctx, std::unique_ptr<Request> r) {
   // We need to send the header once.
   // So use call_once to make sure that only one thread wins this.
   std::call_once((*once_flag_), [ctx, this]() {
-    VLOG(3) << "Writing RPC connection Preamble and Header to server: " << server_;
-    auto pre = serde_.Preamble();
+    VLOG(3) << "Writing RPC Header to server: " << server_;
     auto header = serde_.Header(user_name_);
-    pre->appendChain(std::move(header));
-    ctx->fireWrite(std::move(pre));
+    ctx->fireWrite(std::move(header));
   });
 
   VLOG(3) << "Writing RPC Request with call_id:"
diff --git a/hbase-native-client/connection/connection-factory.cc b/hbase-native-client/connection/connection-factory.cc
index afa227d..9b06c84 100644
--- a/hbase-native-client/connection/connection-factory.cc
+++ b/hbase-native-client/connection/connection-factory.cc
@@ -18,9 +18,12 @@
  */
 
 #include "connection/connection-factory.h"
+#include "connection/sasl-handler.h"
 
 #include <chrono>
 
+#include <glog/logging.h>
+#include <wangle/channel/Handler.h>
 #include "connection/client-dispatcher.h"
 #include "connection/pipeline.h"
 #include "connection/service.h"
@@ -31,10 +34,13 @@ using std::chrono::milliseconds;
 using std::chrono::nanoseconds;
 
 ConnectionFactory::ConnectionFactory(std::shared_ptr<wangle::IOThreadPoolExecutor> io_pool,
-                                     std::shared_ptr<Codec> codec, nanoseconds connect_timeout)
+                                     std::shared_ptr<Codec> codec,
+                                     std::shared_ptr<Configuration> conf,
+                                     nanoseconds connect_timeout)
     : connect_timeout_(connect_timeout),
       io_pool_(io_pool),
-      pipeline_factory_(std::make_shared<RpcPipelineFactory>(codec)) {}
+      conf_(conf),
+      pipeline_factory_(std::make_shared<RpcPipelineFactory>(codec, conf)) {}
 
 std::shared_ptr<wangle::ClientBootstrap<SerializePipeline>> ConnectionFactory::MakeBootstrap() {
   auto client = std::make_shared<wangle::ClientBootstrap<SerializePipeline>>();
diff --git a/hbase-native-client/connection/connection-factory.h b/hbase-native-client/connection/connection-factory.h
index 1e75571..e1d7f6c 100644
--- a/hbase-native-client/connection/connection-factory.h
+++ b/hbase-native-client/connection/connection-factory.h
@@ -28,6 +28,7 @@
 #include "connection/request.h"
 #include "connection/response.h"
 #include "connection/service.h"
+#include "security/user.h"
 
 using std::chrono::nanoseconds;
 
@@ -44,7 +45,8 @@ class ConnectionFactory {
    * There should only be one ConnectionFactory per client.
    */
   ConnectionFactory(std::shared_ptr<wangle::IOThreadPoolExecutor> io_pool,
-                    std::shared_ptr<Codec> codec, nanoseconds connect_timeout = nanoseconds(0));
+                    std::shared_ptr<Codec> codec, std::shared_ptr<Configuration> conf,
+                    nanoseconds connect_timeout = nanoseconds(0));
 
   /** Default Destructor */
   virtual ~ConnectionFactory() = default;
@@ -65,6 +67,7 @@ class ConnectionFactory {
 
  private:
   nanoseconds connect_timeout_;
+  std::shared_ptr<Configuration> conf_;
   std::shared_ptr<wangle::IOThreadPoolExecutor> io_pool_;
   std::shared_ptr<RpcPipelineFactory> pipeline_factory_;
 };
diff --git a/hbase-native-client/connection/connection-pool-test.cc b/hbase-native-client/connection/connection-pool-test.cc
index 8ecdf29..04ec7f1 100644
--- a/hbase-native-client/connection/connection-pool-test.cc
+++ b/hbase-native-client/connection/connection-pool-test.cc
@@ -36,7 +36,7 @@ using hbase::ConnectionId;
 
 class MockConnectionFactory : public ConnectionFactory {
  public:
-  MockConnectionFactory() : ConnectionFactory(nullptr, nullptr) {}
+  MockConnectionFactory() : ConnectionFactory(nullptr, nullptr, nullptr) {}
   MOCK_METHOD0(MakeBootstrap, std::shared_ptr<wangle::ClientBootstrap<SerializePipeline>>());
   MOCK_METHOD3(Connect, std::shared_ptr<HBaseService>(
                             std::shared_ptr<wangle::ClientBootstrap<SerializePipeline>>,
diff --git a/hbase-native-client/connection/connection-pool.cc b/hbase-native-client/connection/connection-pool.cc
index 3121294..d3ac3c2 100644
--- a/hbase-native-client/connection/connection-pool.cc
+++ b/hbase-native-client/connection/connection-pool.cc
@@ -36,11 +36,13 @@ using folly::SharedMutexWritePriority;
 using folly::SocketAddress;
 
 ConnectionPool::ConnectionPool(std::shared_ptr<wangle::IOThreadPoolExecutor> io_executor,
-                               std::shared_ptr<Codec> codec, nanoseconds connect_timeout)
-    : cf_(std::make_shared<ConnectionFactory>(io_executor, codec, connect_timeout)),
+                               std::shared_ptr<Codec> codec, std::shared_ptr<Configuration> conf,
+                               nanoseconds connect_timeout)
+    : cf_(std::make_shared<ConnectionFactory>(io_executor, codec, conf, connect_timeout)),
       clients_(),
       connections_(),
-      map_mutex_() {}
+      map_mutex_(),
+      conf_(conf) {}
 ConnectionPool::ConnectionPool(std::shared_ptr<ConnectionFactory> cf)
     : cf_(cf), clients_(), connections_(), map_mutex_() {}
 
diff --git a/hbase-native-client/connection/connection-pool.h b/hbase-native-client/connection/connection-pool.h
index 2a8f195..0582d9b 100644
--- a/hbase-native-client/connection/connection-pool.h
+++ b/hbase-native-client/connection/connection-pool.h
@@ -50,7 +50,8 @@ class ConnectionPool {
  public:
   /** Create connection pool wit default connection factory */
   ConnectionPool(std::shared_ptr<wangle::IOThreadPoolExecutor> io_executor,
-                 std::shared_ptr<Codec> codec, nanoseconds connect_timeout = nanoseconds(0));
+                 std::shared_ptr<Codec> codec, std::shared_ptr<Configuration> conf,
+                 nanoseconds connect_timeout = nanoseconds(0));
 
   /**
    * Constructor that allows specifiying the connetion factory.
@@ -93,6 +94,7 @@ class ConnectionPool {
       clients_;
   folly::SharedMutexWritePriority map_mutex_;
   std::shared_ptr<ConnectionFactory> cf_;
+  std::shared_ptr<Configuration> conf_;
 };
 
 }  // namespace hbase
diff --git a/hbase-native-client/connection/pipeline.cc b/hbase-native-client/connection/pipeline.cc
index edada52..d27c849 100644
--- a/hbase-native-client/connection/pipeline.cc
+++ b/hbase-native-client/connection/pipeline.cc
@@ -25,13 +25,15 @@
 #include <wangle/codec/LengthFieldBasedFrameDecoder.h>
 
 #include "connection/client-handler.h"
+#include "connection/sasl-handler.h"
 
 using namespace folly;
 using namespace hbase;
 using namespace wangle;
 
-RpcPipelineFactory::RpcPipelineFactory(std::shared_ptr<Codec> codec)
-    : user_util_(), codec_(codec) {}
+RpcPipelineFactory::RpcPipelineFactory(std::shared_ptr<Codec> codec,
+                                       std::shared_ptr<Configuration> conf)
+    : user_util_(), codec_(codec), conf_(conf) {}
 
 SerializePipeline::Ptr RpcPipelineFactory::newPipeline(
     std::shared_ptr<AsyncTransportWrapper> sock) {
@@ -41,8 +43,10 @@ SerializePipeline::Ptr RpcPipelineFactory::newPipeline(
   auto pipeline = SerializePipeline::create();
   pipeline->addBack(AsyncSocketHandler{sock});
   pipeline->addBack(EventBaseHandler{});
+  auto secure = security::User::IsSecurityEnabled(*conf_);
+  pipeline->addBack(SaslHandler{user_util_.user_name(secure), conf_});
   pipeline->addBack(LengthFieldBasedFrameDecoder{});
-  pipeline->addBack(ClientHandler{user_util_.user_name(), codec_, addr.describe()});
+  pipeline->addBack(ClientHandler{user_util_.user_name(secure), codec_, addr.describe()});
   pipeline->finalize();
   return pipeline;
 }
diff --git a/hbase-native-client/connection/pipeline.h b/hbase-native-client/connection/pipeline.h
index ea40cfd..add7fe5 100644
--- a/hbase-native-client/connection/pipeline.h
+++ b/hbase-native-client/connection/pipeline.h
@@ -25,6 +25,7 @@
 
 #include "connection/request.h"
 #include "connection/response.h"
+#include "core/configuration.h"
 #include "serde/codec.h"
 #include "utils/user-util.h"
 
@@ -41,7 +42,7 @@ class RpcPipelineFactory : public wangle::PipelineFactory<SerializePipeline> {
   /**
    * Constructor. This will create user util.
    */
-  explicit RpcPipelineFactory(std::shared_ptr<Codec> codec);
+  explicit RpcPipelineFactory(std::shared_ptr<Codec> codec, std::shared_ptr<Configuration> conf);
 
   /**
    * Create a new pipeline.
@@ -57,5 +58,6 @@ class RpcPipelineFactory : public wangle::PipelineFactory<SerializePipeline> {
  private:
   UserUtil user_util_;
   std::shared_ptr<Codec> codec_;
+  std::shared_ptr<Configuration> conf_;
 };
 }  // namespace hbase
diff --git a/hbase-native-client/connection/rpc-client.cc b/hbase-native-client/connection/rpc-client.cc
index 5fa1138..57df66d 100644
--- a/hbase-native-client/connection/rpc-client.cc
+++ b/hbase-native-client/connection/rpc-client.cc
@@ -30,9 +30,10 @@ using hbase::RpcClient;
 namespace hbase {
 
 RpcClient::RpcClient(std::shared_ptr<wangle::IOThreadPoolExecutor> io_executor,
-                     std::shared_ptr<Codec> codec, nanoseconds connect_timeout)
-    : io_executor_(io_executor) {
-  cp_ = std::make_shared<ConnectionPool>(io_executor_, codec, connect_timeout);
+                     std::shared_ptr<Codec> codec, std::shared_ptr<Configuration> conf,
+                     nanoseconds connect_timeout)
+    : io_executor_(io_executor), conf_(conf) {
+  cp_ = std::make_shared<ConnectionPool>(io_executor_, codec, conf, connect_timeout);
 }
 
 void RpcClient::Close() { io_executor_->stop(); }
diff --git a/hbase-native-client/connection/rpc-client.h b/hbase-native-client/connection/rpc-client.h
index d416ceb..fbb773a 100644
--- a/hbase-native-client/connection/rpc-client.h
+++ b/hbase-native-client/connection/rpc-client.h
@@ -46,7 +46,7 @@ namespace hbase {
 class RpcClient {
  public:
   RpcClient(std::shared_ptr<wangle::IOThreadPoolExecutor> io_executor, std::shared_ptr<Codec> codec,
-            nanoseconds connect_timeout = nanoseconds(0));
+            std::shared_ptr<Configuration> conf, nanoseconds connect_timeout = nanoseconds(0));
 
   virtual ~RpcClient() { Close(); }
 
@@ -78,5 +78,6 @@ class RpcClient {
  private:
   std::shared_ptr<ConnectionPool> cp_;
   std::shared_ptr<wangle::IOThreadPoolExecutor> io_executor_;
+  std::shared_ptr<Configuration> conf_;
 };
 }  // namespace hbase
diff --git a/hbase-native-client/connection/sasl-handler.cc b/hbase-native-client/connection/sasl-handler.cc
new file mode 100644
index 0000000..02cddce
--- /dev/null
+++ b/hbase-native-client/connection/sasl-handler.cc
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include "connection/sasl-handler.h"
+
+#include <glog/logging.h>
+#include <sasl/sasl.h>
+#include <sasl/saslplug.h>
+#include <sasl/saslutil.h>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <wangle/channel/Handler.h>
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <utility>
+
+#include "connection/service.h"
+#include "security/user.h"
+using hbase::security::User;
+
+using std::chrono::nanoseconds;
+using namespace folly;
+using namespace wangle;
+using namespace hbase;
+
+SaslHandler::SaslHandler(std::string user_name, std::shared_ptr<Configuration> conf)
+    : user_name_(user_name) {
+  host_name_.clear();
+  secure_ = User::IsSecurityEnabled(*conf);
+  service_name_ = SaslUtil::ParseServiceName(conf, secure_);
+  sasl_connection_setup_started_.clear();
+  sasl_connection_setup_in_progress_.store(true);
+}
+
+SaslHandler::SaslHandler(const SaslHandler &hdlr) {
+  user_name_ = hdlr.user_name_;
+  service_name_ = hdlr.service_name_;
+  secure_ = hdlr.secure_;
+  host_name_ = hdlr.host_name_;
+  // copy-constructor sets the flags below to their initial state as opposed to getting them
+  // from the object this class is constructed from. That way, this instance is ready to do
+  // sasl stuff without issues, right from the SaslInit. Sharing a sasl session is not useful
+  // between two handler instances.
+  sasl_connection_setup_started_.clear();
+  sasl_connection_setup_in_progress_.store(true);
+  sconn_ = nullptr;
+}
+
+SaslHandler::~SaslHandler() {
+  if (nullptr != sconn_) {
+    sasl_dispose(&sconn_);
+  }
+  sconn_ = nullptr;
+}
+
+void SaslHandler::transportActive(Context *ctx) {
+  // assign hostname; needed for the sasl handshake if secure
+  folly::SocketAddress address;
+  ctx->getTransport()->getPeerAddress(&address);
+  host_name_ = address.getHostStr();
+
+  // now init the sasl library; this is once per process
+  if (secure_) {
+    sasl_util_.InitializeSaslLib();
+  }
+  // write the preamble to kick off the RPC handshake
+  VLOG(3) << "Writing RPC connection Preamble to server: " << host_name_;
+  auto preamble = RpcSerde::Preamble(secure_);
+  ctx->fireWrite(std::move(preamble));
+}
+
+void SaslHandler::read(Context *ctx, folly::IOBufQueue &buf) {
+  // if security is not on, or in case of security-on, if secure connection setup not in progress,
+  // pass it up without touching
+  if (!secure_ || !sasl_connection_setup_in_progress_.load()) {
+    ctx->fireRead(buf);
+  } else {
+    // message is for this handler; process it appropriately
+    ContinueSaslNegotiation(ctx, &buf);
+  }
+}
+
+folly::Future<folly::Unit> SaslHandler::write(Context *ctx, std::unique_ptr<folly::IOBuf> buf) {
+  // if security is on, and if secure connection setup in progress,
+  // this message is for this handler to process and respond
+  if (secure_ && sasl_connection_setup_in_progress_.load()) {
+    // store IOBuf which is to be sent to server after SASL handshake
+    iobuf_.push_back(std::move(buf));
+    if (!sasl_connection_setup_started_.test_and_set()) {
+      // for the first incoming RPC from the higher layer, trigger sasl initialization
+      return SaslInit(ctx);
+    } else {
+      // for the subsequent incoming RPCs from the higher layer, just return empty future
+      folly::Promise<folly::Unit> p_;
+      return p_.getFuture();
+    }
+  }
+  // pass the bytes recieved down without touching it
+  return ctx->fireWrite(std::move(buf));
+}
+
+folly::Future<folly::Unit> SaslHandler::WriteSaslOutput(Context *ctx, const char *out,
+                                                        unsigned int outlen) {
+  int buffer_size = outlen + 4;
+  auto iob = IOBuf::create(buffer_size);
+  iob->append(buffer_size);
+  // Create the array output stream.
+  google::protobuf::io::ArrayOutputStream aos{iob->writableData(), buffer_size};
+  std::unique_ptr<google::protobuf::io::CodedOutputStream> coded_output =
+      std::make_unique<google::protobuf::io::CodedOutputStream>(&aos);
+  uint32_t total_size = outlen;
+  total_size = ntohl(total_size);
+  coded_output->WriteRaw(&total_size, 4);
+  coded_output->WriteRaw(out, outlen);
+  return ctx->fireWrite(std::move(iob));
+}
+
+void SaslHandler::FinishAuth(Context *ctx, folly::IOBufQueue* bufQueue) {
+  std::unique_ptr<folly::IOBuf> iob;
+  if (!bufQueue->empty()) {
+    iob = bufQueue->pop_front();
+    throw std::runtime_error("Error in the final step of handshake " +
+                             std::string(reinterpret_cast<const char *>(iob->data())));
+  } else {
+    sasl_connection_setup_in_progress_.store(false);
+    // write what we buffered
+    for (size_t i = 0; i < iobuf_.size(); i++) {
+      iob = std::move(iobuf_.at(i));
+      ctx->fireWrite(std::move(iob));
+    }
+  }
+}
+
+folly::Future<folly::Unit> SaslHandler::SaslInit(Context *ctx) {
+  int rc;
+  const char *mechusing, *mechlist = "GSSAPI";
+  const char *out;
+  unsigned int outlen;
+
+  rc = sasl_client_new(service_name_.c_str(), /* The service we are using*/
+                       host_name_.c_str(), NULL,
+                       NULL, /* Local and remote IP address strings
+                                   (NULL disables mechanisms which require this info)*/
+                       NULL, /*connection-specific callbacks*/
+                       0 /*security flags*/, &sconn_);
+  if (rc != SASL_OK) {
+    LOG(FATAL) << "Cannot create client (" << rc << ") ";
+    throw std::runtime_error("Cannot create client");
+  }
+  int curr_rc;
+  do {
+    curr_rc = sasl_client_start(sconn_,   /* the same context from above */
+                                mechlist, /* the list of mechanisms from the server */
+                                NULL,     /* filled in if an interaction is needed */
+                                &out,     /* filled in on success */
+                                &outlen,  /* filled in on success */
+                                &mechusing);
+  } while (curr_rc == SASL_INTERACT); /* the mechanism may ask us to fill
+     in things many times. result is SASL_CONTINUE on success */
+  if (curr_rc != SASL_CONTINUE) {
+    throw std::runtime_error("Cannot start client (" + std::to_string(curr_rc) + ")");
+  }
+  folly::Future<folly::Unit> fut = WriteSaslOutput(ctx, out, outlen);
+  return fut;
+}
+
+void SaslHandler::ContinueSaslNegotiation(Context *ctx, folly::IOBufQueue* bufQueue) {
+  const char *out;
+  unsigned int outlen;
+
+  int bytes_sent = 0;
+  int bytes_received = 0;
+
+  std::unique_ptr<folly::IOBuf> iob = bufQueue->pop_front();
+  bytes_received = iob->length();
+  if (bytes_received == 0) {
+    throw std::runtime_error("Error in sasl handshake");
+  }
+  folly::io::RWPrivateCursor c(iob.get());
+  std::uint32_t status = c.readBE<std::uint32_t>();
+  std::uint32_t sz = c.readBE<std::uint32_t>();
+
+  if (status != 0 /*Status 0 is success*/) {
+    // Assumption here is that the response from server is not more than 8 * 1024
+    throw std::runtime_error("Error in sasl handshake " +
+                             std::string(reinterpret_cast<char *>(c.writableData())));
+  }
+  out = nullptr;
+  outlen = 0;
+
+  int curr_rc =
+      sasl_client_step(sconn_,                                     /* our context */
+                       reinterpret_cast<char *>(c.writableData()), /* the data from the server */
+                       sz,                                         /* its length */
+                       NULL,     /* this should be unallocated and NULL */
+                       &out,     /* filled in on success */
+                       &outlen); /* filled in on success */
+
+  if (curr_rc == SASL_OK || curr_rc == SASL_CONTINUE) {
+    WriteSaslOutput(ctx, out, outlen);
+  }
+  if (curr_rc == SASL_OK) {
+    FinishAuth(ctx, bufQueue);
+  }
+}
diff --git a/hbase-native-client/connection/sasl-handler.h b/hbase-native-client/connection/sasl-handler.h
new file mode 100644
index 0000000..f606a23
--- /dev/null
+++ b/hbase-native-client/connection/sasl-handler.h
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#pragma once
+
+#include <glog/logging.h>
+#include <sasl/sasl.h>
+#include <sasl/saslplug.h>
+#include <sasl/saslutil.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "connection/sasl-util.h"
+#include "connection/service.h"
+#include "security/user.h"
+#include "serde/rpc.h"
+
+namespace hbase {
+
+/**
+ * Class to perform SASL handshake with server (currently works with regionserver principals only)
+ * It is inserted between EventBaseHandler and LengthFieldBasedFrameDecoder in the pipeline
+ * SaslHandler would intercept writes to server by buffering the IOBuf's and start the handshake
+ * process
+ *   (via sasl_client_XX calls provided by Cyrus)
+ * After handshake is complete, SaslHandler would send the buffered IOBuf's to server and
+ *   act as pass-thru from then on
+ */
+class SaslHandler
+    : public wangle::HandlerAdapter<folly::IOBufQueue&, std::unique_ptr<folly::IOBuf>> {
+ public:
+  explicit SaslHandler(std::string user_name, std::shared_ptr<Configuration> conf);
+  SaslHandler(const SaslHandler& hdlr);
+  ~SaslHandler();
+
+  // from HandlerAdapter
+  void read(Context* ctx, folly::IOBufQueue& buf) override;
+  folly::Future<folly::Unit> write(Context* ctx, std::unique_ptr<folly::IOBuf> buf) override;
+  void transportActive(Context* ctx) override;
+
+ private:
+  // used by Cyrus
+  sasl_conn_t* sconn_ = nullptr;
+  std::string user_name_;
+  std::string service_name_;
+  std::string host_name_;
+  bool secure_;
+  std::atomic_flag sasl_connection_setup_started_;
+  std::atomic<bool> sasl_connection_setup_in_progress_{true};
+  // vector of folly::IOBuf which buffers client writes before handshake is complete
+  std::vector<std::unique_ptr<folly::IOBuf>> iobuf_;
+  SaslUtil sasl_util_;
+
+  // writes the output returned by sasl_client_XX to server
+  folly::Future<folly::Unit> WriteSaslOutput(Context* ctx, const char* out, unsigned int outlen);
+  folly::Future<folly::Unit> SaslInit(Context* ctx);
+  void FinishAuth(Context* ctx, folly::IOBufQueue* bufQueue);
+  void ContinueSaslNegotiation(Context* ctx, folly::IOBufQueue* buf);
+  std::string ParseServiceName(std::shared_ptr<Configuration> conf, bool secure);
+};
+}  // namespace hbase
diff --git a/hbase-native-client/connection/sasl-util.cc b/hbase-native-client/connection/sasl-util.cc
new file mode 100644
index 0000000..ecaf015
--- /dev/null
+++ b/hbase-native-client/connection/sasl-util.cc
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include "connection/sasl-util.h"
+
+#include <glog/logging.h>
+#include <sasl/sasl.h>
+#include <sasl/saslplug.h>
+#include <sasl/saslutil.h>
+
+#include <string>
+
+int SaslUtil::GetPluginPath(void *context __attribute__((unused)), const char **path) {
+  *path = getenv("SASL_PATH");
+
+  if (*path == NULL) {
+    *path = kDefaultPluginDir;
+  }
+  return SASL_OK;
+}
+
+void *SaslUtil::MutexNew(void) {
+  auto m = new std::mutex();
+  return m;
+}
+
+int SaslUtil::MutexLock(void *m) {
+  (reinterpret_cast<std::mutex *>(m))->lock();
+  return SASL_OK;
+}
+
+int SaslUtil::MutexUnlock(void *m) {
+  (reinterpret_cast<std::mutex *>(m))->unlock();
+  return SASL_OK;
+}
+
+void SaslUtil::MutexDispose(void *m) {
+  std::mutex *mutex = reinterpret_cast<std::mutex *>(m);
+  delete mutex;
+}
+
+std::once_flag SaslUtil::library_inited_;
+
+void SaslUtil::InitializeSaslLib() {
+  std::call_once(library_inited_, []() {
+    sasl_set_mutex(reinterpret_cast<sasl_mutex_alloc_t *>(&SaslUtil::MutexNew),
+                   reinterpret_cast<sasl_mutex_lock_t *>(&SaslUtil::MutexLock),
+                   reinterpret_cast<sasl_mutex_unlock_t *>(&SaslUtil::MutexUnlock),
+                   reinterpret_cast<sasl_mutex_free_t *>(&SaslUtil::MutexDispose));
+    static sasl_callback_t callbacks[] = {
+        {SASL_CB_GETPATH, (sasl_callback_ft)&SaslUtil::GetPluginPath, NULL},
+        {SASL_CB_LIST_END, NULL, NULL}};
+    int rc = sasl_client_init(callbacks);
+    if (rc != SASL_OK) {
+      throw std::runtime_error("Cannot initialize client " + std::to_string(rc));
+    }
+  });
+}
+
+std::string SaslUtil::ParseServiceName(std::shared_ptr<hbase::Configuration> conf, bool secure) {
+  if (!secure) {
+    return std::string();
+  }
+  std::string svrPrincipal = conf->Get(kServerPrincipalConfKey, "");
+  // principal is of this form: hbase/23a03935850c@EXAMPLE.COM
+  // where 23a03935850c is the host (optional)
+  std::size_t pos = svrPrincipal.find("/");
+  if (pos == std::string::npos && svrPrincipal.find("@") != std::string::npos) {
+    pos = svrPrincipal.find("@");
+  }
+  if (pos == std::string::npos) {
+    throw std::runtime_error("Couldn't retrieve service principal from conf");
+  }
+  VLOG(1) << "pos " << pos << " " << svrPrincipal;
+  std::string service_name = svrPrincipal.substr(0, pos);
+  return service_name;
+}
diff --git a/hbase-native-client/security/user.h b/hbase-native-client/connection/sasl-util.h
similarity index 53%
copy from hbase-native-client/security/user.h
copy to hbase-native-client/connection/sasl-util.h
index 035af31..4d58d9ee 100644
--- a/hbase-native-client/security/user.h
+++ b/hbase-native-client/connection/sasl-util.h
@@ -18,28 +18,29 @@
  */
 #pragma once
 
+#include <memory>
 #include <mutex>
 #include <string>
+
 #include "core/configuration.h"
 
-namespace hbase {
-namespace security {
-static constexpr const char* kKerberos = "kerberos";
-class User {
+class SaslUtil {
  public:
-  explicit User(const std::string& user_name) : user_name_(user_name) {}
-  virtual ~User() = default;
-
-  std::string user_name() { return user_name_; }
-
-  static std::shared_ptr<User> defaultUser() { return std::make_shared<User>("__drwho"); }
-
-  static bool IsSecurityEnabled(const Configuration& conf) {
-    return conf.Get("hbase.security.authentication", "").compare(kKerberos) == 0;
-  }
+  void InitializeSaslLib(void);
+  static std::string ParseServiceName(std::shared_ptr<hbase::Configuration> conf, bool secure);
 
  private:
-  std::string user_name_;
+  static constexpr const char *kDefaultPluginDir = "/usr/lib/sasl2";
+  // for now the sasl handler is hardcoded to work against the regionservers only. In the future, if
+  // we
+  // need the master rpc to work, we could have a map of service names to principals to use (similar
+  // to the Java implementation)
+  static constexpr const char *kServerPrincipalConfKey = "hbase.regionserver.kerberos.principal";
+
+  static int GetPluginPath(void *context, const char **path);
+  static void *MutexNew(void);
+  static int MutexLock(void *m);
+  static int MutexUnlock(void *m);
+  static void MutexDispose(void *m);
+  static std::once_flag library_inited_;
 };
-}
-}
diff --git a/hbase-native-client/core/BUCK b/hbase-native-client/core/BUCK
index e9fc716..9cea1f6 100644
--- a/hbase-native-client/core/BUCK
+++ b/hbase-native-client/core/BUCK
@@ -92,7 +92,9 @@ cxx_library(
         "//third-party:zookeeper_mt",
     ],
     compiler_flags=['-Weffc++', '-ggdb'],
-    visibility=['PUBLIC',],)
+    visibility=[
+        'PUBLIC',
+    ],)
 cxx_library(
     name="conf",
     exported_headers=[
@@ -105,10 +107,14 @@ cxx_library(
     ],
     deps=["//third-party:folly"],
     compiler_flags=['-Weffc++', '-ggdb'],
-    visibility=['PUBLIC',],)
+    visibility=[
+        'PUBLIC',
+    ],)
 cxx_test(
     name="location-cache-test",
-    srcs=["location-cache-test.cc",],
+    srcs=[
+        "location-cache-test.cc",
+    ],
     deps=[
         ":core",
         "//test-util:test-util",
@@ -116,12 +122,18 @@ cxx_test(
     run_test_separately=True,)
 cxx_test(
     name="cell-test",
-    srcs=["cell-test.cc",],
-    deps=[":core",],
+    srcs=[
+        "cell-test.cc",
+    ],
+    deps=[
+        ":core",
+    ],
     run_test_separately=True,)
 cxx_test(
     name="filter-test",
-    srcs=["filter-test.cc",],
+    srcs=[
+        "filter-test.cc",
+    ],
     deps=[
         ":core",
         "//if:if",
@@ -131,17 +143,27 @@ cxx_test(
     run_test_separately=True,)
 cxx_test(
     name="get-test",
-    srcs=["get-test.cc",],
-    deps=[":core",],
+    srcs=[
+        "get-test.cc",
+    ],
+    deps=[
+        ":core",
+    ],
     run_test_separately=True,)
 cxx_test(
     name="put-test",
-    srcs=["put-test.cc",],
-    deps=[":core",],
+    srcs=[
+        "put-test.cc",
+    ],
+    deps=[
+        ":core",
+    ],
     run_test_separately=True,)
 cxx_test(
     name="retry-test",
-    srcs=["async-rpc-retrying-test.cc",],
+    srcs=[
+        "async-rpc-retrying-test.cc",
+    ],
     deps=[
         ":core",
         "//test-util:test-util",
@@ -150,32 +172,54 @@ cxx_test(
     run_test_separately=True,)
 cxx_test(
     name="time-range-test",
-    srcs=["time-range-test.cc",],
-    deps=[":core",],
+    srcs=[
+        "time-range-test.cc",
+    ],
+    deps=[
+        ":core",
+    ],
     run_test_separately=True,)
 cxx_test(
     name="configuration-test",
-    srcs=["configuration-test.cc",],
-    deps=[":core",],
+    srcs=[
+        "configuration-test.cc",
+    ],
+    deps=[
+        ":core",
+    ],
     run_test_separately=True,)
 cxx_test(
     name="hbase-configuration-test",
-    srcs=["hbase-configuration-test.cc",],
-    deps=[":core",],
+    srcs=[
+        "hbase-configuration-test.cc",
+    ],
+    deps=[
+        ":core",
+    ],
     run_test_separately=True,)
 cxx_test(
     name="scan-test",
-    srcs=["scan-test.cc",],
-    deps=[":core",],
+    srcs=[
+        "scan-test.cc",
+    ],
+    deps=[
+        ":core",
+    ],
     run_test_separately=True,)
 cxx_test(
     name="result-test",
-    srcs=["result-test.cc",],
-    deps=[":core",],
+    srcs=[
+        "result-test.cc",
+    ],
+    deps=[
+        ":core",
+    ],
     run_test_separately=True,)
 cxx_test(
     name="request-converter-test",
-    srcs=["request-converter-test.cc",],
+    srcs=[
+        "request-converter-test.cc",
+    ],
     deps=[
         ":core",
         "//connection:connection",
@@ -184,7 +228,9 @@ cxx_test(
     run_test_separately=True,)
 cxx_test(
     name="client-test",
-    srcs=["client-test.cc",],
+    srcs=[
+        "client-test.cc",
+    ],
     deps=[
         ":core",
         "//if:if",
@@ -194,10 +240,16 @@ cxx_test(
     run_test_separately=True,)
 cxx_test(
     name="zk-util-test",
-    srcs=["zk-util-test.cc",],
-    deps=[":core",],
+    srcs=[
+        "zk-util-test.cc",
+    ],
+    deps=[
+        ":core",
+    ],
     run_test_separately=True,)
 cxx_binary(
     name="simple-client",
-    srcs=["simple-client.cc",],
+    srcs=[
+        "simple-client.cc",
+    ],
     deps=[":core", "//connection:connection"],)
diff --git a/hbase-native-client/core/async-connection.cc b/hbase-native-client/core/async-connection.cc
index 4642c61..ef945fb 100644
--- a/hbase-native-client/core/async-connection.cc
+++ b/hbase-native-client/core/async-connection.cc
@@ -44,8 +44,8 @@ void AsyncConnectionImpl::Init() {
   } else {
     LOG(WARNING) << "Not using RPC Cell Codec";
   }
-  rpc_client_ =
-      std::make_shared<hbase::RpcClient>(io_executor_, codec, connection_conf_->connect_timeout());
+  rpc_client_ = std::make_shared<hbase::RpcClient>(io_executor_, codec, conf_,
+                                                   connection_conf_->connect_timeout());
   location_cache_ =
       std::make_shared<hbase::LocationCache>(conf_, cpu_executor_, rpc_client_->connection_pool());
   caller_factory_ =
diff --git a/hbase-native-client/core/async-rpc-retrying-test.cc b/hbase-native-client/core/async-rpc-retrying-test.cc
index 487c34c..11750eb 100644
--- a/hbase-native-client/core/async-rpc-retrying-test.cc
+++ b/hbase-native-client/core/async-rpc-retrying-test.cc
@@ -317,7 +317,8 @@ void runTest(std::shared_ptr<AsyncRegionLocatorBase> region_locator, std::string
   auto io_executor_ = client.async_connection()->io_executor();
   auto retry_executor_ = std::make_shared<wangle::IOThreadPoolExecutor>(1);
   auto codec = std::make_shared<hbase::KeyValueCodec>();
-  auto rpc_client = std::make_shared<RpcClient>(io_executor_, codec);
+  auto rpc_client =
+      std::make_shared<RpcClient>(io_executor_, codec, AsyncRpcRetryTest::test_util->conf());
   // auto retry_event_base_ = std::make_shared<folly::ScopedEventBaseThread>(true);
   std::shared_ptr<folly::HHWheelTimer> retry_timer =
       folly::HHWheelTimer::newTimer(retry_executor_->getEventBase());
diff --git a/hbase-native-client/core/location-cache-test.cc b/hbase-native-client/core/location-cache-test.cc
index 8d1ac5f..3253c56 100644
--- a/hbase-native-client/core/location-cache-test.cc
+++ b/hbase-native-client/core/location-cache-test.cc
@@ -52,7 +52,7 @@ TEST_F(LocationCacheTest, TestGetMetaNodeContents) {
   auto cpu = std::make_shared<wangle::CPUThreadPoolExecutor>(4);
   auto io = std::make_shared<wangle::IOThreadPoolExecutor>(4);
   auto codec = std::make_shared<KeyValueCodec>();
-  auto cp = std::make_shared<ConnectionPool>(io, codec);
+  auto cp = std::make_shared<ConnectionPool>(io, codec, LocationCacheTest::test_util_->conf());
   LocationCache cache{LocationCacheTest::test_util_->conf(), cpu, cp};
   auto f = cache.LocateMeta();
   auto result = f.get();
@@ -68,7 +68,7 @@ TEST_F(LocationCacheTest, TestGetRegionLocation) {
   auto cpu = std::make_shared<wangle::CPUThreadPoolExecutor>(4);
   auto io = std::make_shared<wangle::IOThreadPoolExecutor>(4);
   auto codec = std::make_shared<KeyValueCodec>();
-  auto cp = std::make_shared<ConnectionPool>(io, codec);
+  auto cp = std::make_shared<ConnectionPool>(io, codec, LocationCacheTest::test_util_->conf());
   LocationCache cache{LocationCacheTest::test_util_->conf(), cpu, cp};
 
   // If there is no table this should throw an exception
@@ -87,7 +87,7 @@ TEST_F(LocationCacheTest, TestCaching) {
   auto cpu = std::make_shared<wangle::CPUThreadPoolExecutor>(4);
   auto io = std::make_shared<wangle::IOThreadPoolExecutor>(4);
   auto codec = std::make_shared<KeyValueCodec>();
-  auto cp = std::make_shared<ConnectionPool>(io, codec);
+  auto cp = std::make_shared<ConnectionPool>(io, codec, LocationCacheTest::test_util_->conf());
   LocationCache cache{LocationCacheTest::test_util_->conf(), cpu, cp};
 
   auto tn_1 = folly::to<hbase::pb::TableName>("t1");
diff --git a/hbase-native-client/docker-files/Dockerfile b/hbase-native-client/docker-files/Dockerfile
new file mode 100644
index 0000000..c5f47ad
--- /dev/null
+++ b/hbase-native-client/docker-files/Dockerfile
@@ -0,0 +1,88 @@
+##
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+FROM pjameson/buck-folly-watchman:20160511
+
+ARG CC=/usr/bin/gcc-5
+ARG CXX=/usr/bin/g++-5
+ARG CFLAGS="-D_GLIBCXX_USE_CXX11_ABI=0 -fPIC -g -fno-omit-frame-pointer -O2 -pthread"
+ARG CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=0 -fPIC -g -fno-omit-frame-pointer -O2 -pthread"
+
+ENV JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64/"
+
+RUN wget ftp://ftp.cyrusimap.org/cyrus-sasl/cyrus-sasl-2.1.26.tar.gz ; \
+    tar zxf cyrus-sasl-2.1.26.tar.gz ; \
+    cd cyrus-sasl-2.1.26 ; \
+    ./configure ; \
+    make ; \
+    make install ;\
+    cp /usr/local/lib/sasl2/* /usr/lib/sasl2/
+
+RUN apt-get install -y vim maven inetutils-ping python-pip doxygen graphviz clang-format && \
+      pip install yapf && \
+      apt-get -qq clean && \
+      apt-get -y -qq autoremove && \
+      rm -rf /var/lib/{apt,dpkg,cache,log}/ && \
+      rm -rf /tmp/*
+
+RUN apt-get update && \
+    apt-get install -y debconf-utils debconf-set-selections && \
+    echo "krb5-config krb5-config/kerberos_servers string localhost" | debconf-set-selections ; \
+    echo "krb5-config krb5-config/admin_server string localhost" | debconf-set-selections ; \
+    echo "krb5-config krb5-config/add_servers_realm string EXAMPLE.COM" | debconf-set-selections ; \
+    echo "krb5-config krb5-config/default_realm string EXAMPLE.COM" | debconf-set-selections ; \
+    apt-get install -y krb5-kdc krb5-admin-server ; \
+    echo "admin" > /tmp/krb-realm.pass ; \
+    echo "admin" >> /tmp/krb-realm.pass ; \
+    krb5_newrealm < /tmp/krb-realm.pass ; \
+    echo "addprinc hbase" > /tmp/krb-princ.pass ; \
+    echo "admin" >> /tmp/krb-princ.pass ; \
+    echo "admin" >> /tmp/krb-princ.pass ; \
+    kadmin.local < /tmp/krb-princ.pass ; \
+    echo 'addprinc hbase/securecluster' > /tmp/krb-princ.pass; echo 'admin' >> /tmp/krb-princ.pass ; \
+    rm hbase-host.keytab ; echo 'admin' >> /tmp/krb-princ.pass ; \
+    echo 'xst -k hbase-host.keytab hbase/securecluster@EXAMPLE.COM' >> /tmp/krb-princ.pass ; \
+    kadmin.local < /tmp/krb-princ.pass ;
+COPY docker-files/krb5.conf /etc
+
+RUN git clone https://github.com/google/protobuf.git /usr/src/protobuf && \
+  cd /usr/src/protobuf/ && \
+  git checkout 2.7.0 && \
+  mkdir gmock && \
+  ldconfig && \
+  ./autogen.sh && \
+  ./configure && \
+  make && \
+  make install && \ 
+  make clean && \
+  rm -rf .git && \
+  cd /usr/src && \
+  wget http://www-us.apache.org/dist/zookeeper/zookeeper-3.4.8/zookeeper-3.4.8.tar.gz && \ 
+  tar zxf zookeeper-3.4.8.tar.gz && \ 
+  rm -rf zookeeper-3.4.8.tar.gz && \
+  cd zookeeper-3.4.8 && \
+  cd src/c && \
+  ldconfig && \
+  ./configure && \
+  make && \
+  make install && \
+  make clean && \
+  ldconfig
+
+ENTRYPOINT /usr/sbin/krb5kdc -P /var/run/krb5kdc.pid && /bin/bash
+
+WORKDIR /usr/src/hbase/hbase-native-client
diff --git a/hbase-native-client/docker-files/krb5.conf b/hbase-native-client/docker-files/krb5.conf
new file mode 100644
index 0000000..d449928
--- /dev/null
+++ b/hbase-native-client/docker-files/krb5.conf
@@ -0,0 +1,20 @@
+[logging]
+ default = FILE:/var/log/krb5libs.log
+ kdc = FILE:/var/log/krb5kdc.log
+ admin_server = FILE:/var/log/kadmind.log
+
+[libdefaults]
+ default_realm = EXAMPLE.COM
+ dns_lookup_realm = false
+ dns_lookup_kdc = false
+ ticket_lifetime = 24h
+ renew_lifetime = 7d
+ forwardable = true
+ rdns = false
+
+[realms]
+EXAMPLE.COM = {
+kdc = localhost
+admin_server = localhost
+}
+[domain_realm]
diff --git a/hbase-native-client/security/BUCK b/hbase-native-client/security/BUCK
index 7383028..f8a5695 100644
--- a/hbase-native-client/security/BUCK
+++ b/hbase-native-client/security/BUCK
@@ -19,8 +19,12 @@
 # to a single server.
 cxx_library(
     name="security",
-    exported_headers=["user.h",],
+    exported_headers=[
+        "user.h",
+    ],
     srcs=[],
     deps=["//core:conf"],
     compiler_flags=['-Weffc++'],
-    visibility=['//core/...', '//connection/...'],)
+    visibility=[
+        'PUBLIC',
+    ],)
diff --git a/hbase-native-client/security/user.h b/hbase-native-client/security/user.h
index 035af31..307fc61 100644
--- a/hbase-native-client/security/user.h
+++ b/hbase-native-client/security/user.h
@@ -18,6 +18,7 @@
  */
 #pragma once
 
+#include <glog/logging.h>
 #include <mutex>
 #include <string>
 #include "core/configuration.h"
diff --git a/hbase-native-client/serde/BUCK b/hbase-native-client/serde/BUCK
index 38e7b4d..18e949c 100644
--- a/hbase-native-client/serde/BUCK
+++ b/hbase-native-client/serde/BUCK
@@ -31,7 +31,9 @@ cxx_library(
         "rpc.cc",
         "zk.cc",
     ],
-    deps=["//if:if", "//third-party:folly", "//utils:utils"],
+    deps=[
+        "//if:if", "//third-party:folly", "//utils:utils", "//security:security"
+    ],
     tests=[
         ":client-deserializer-test",
         ":client-serializer-test",
@@ -41,28 +43,54 @@ cxx_library(
         ":region-info-deserializer-test",
     ],
     compiler_flags=['-Weffc++'],
-    visibility=['PUBLIC',],)
+    visibility=[
+        'PUBLIC',
+    ],)
 cxx_test(
     name="table-name-test",
-    srcs=["table-name-test.cc",],
-    deps=[":serde",],)
+    srcs=[
+        "table-name-test.cc",
+    ],
+    deps=[
+        ":serde",
+    ],)
 cxx_test(
     name="server-name-test",
-    srcs=["server-name-test.cc",],
-    deps=[":serde",],)
+    srcs=[
+        "server-name-test.cc",
+    ],
+    deps=[
+        ":serde",
+    ],)
 cxx_test(
     name="client-serializer-test",
-    srcs=["client-serializer-test.cc",],
-    deps=[":serde",],)
+    srcs=[
+        "client-serializer-test.cc",
+    ],
+    deps=[
+        ":serde",
+    ],)
 cxx_test(
     name="client-deserializer-test",
-    srcs=["client-deserializer-test.cc",],
-    deps=[":serde",],)
+    srcs=[
+        "client-deserializer-test.cc",
+    ],
+    deps=[
+        ":serde",
+    ],)
 cxx_test(
     name="zk-deserializer-test",
-    srcs=["zk-deserializer-test.cc",],
-    deps=[":serde",],)
+    srcs=[
+        "zk-deserializer-test.cc",
+    ],
+    deps=[
+        ":serde",
+    ],)
 cxx_test(
     name="region-info-deserializer-test",
-    srcs=["region-info-deserializer-test.cc",],
-    deps=[":serde",],)
+    srcs=[
+        "region-info-deserializer-test.cc",
+    ],
+    deps=[
+        ":serde",
+    ],)
diff --git a/hbase-native-client/serde/client-serializer-test.cc b/hbase-native-client/serde/client-serializer-test.cc
index 33c48f3..7d8b29c 100644
--- a/hbase-native-client/serde/client-serializer-test.cc
+++ b/hbase-native-client/serde/client-serializer-test.cc
@@ -33,7 +33,7 @@ using namespace folly::io;
 
 TEST(RpcSerdeTest, PreambleIncludesHBas) {
   RpcSerde ser{nullptr};
-  auto buf = ser.Preamble();
+  auto buf = ser.Preamble(false);
   const char *p = reinterpret_cast<const char *>(buf->data());
   // Take the first for chars and make sure they are the
   // magic string
@@ -44,7 +44,7 @@ TEST(RpcSerdeTest, PreambleIncludesHBas) {
 
 TEST(RpcSerdeTest, PreambleIncludesVersion) {
   RpcSerde ser{nullptr};
-  auto buf = ser.Preamble();
+  auto buf = ser.Preamble(false);
   EXPECT_EQ(0, static_cast<const uint8_t *>(buf->data())[4]);
   EXPECT_EQ(80, static_cast<const uint8_t *>(buf->data())[5]);
 }
diff --git a/hbase-native-client/serde/rpc.cc b/hbase-native-client/serde/rpc.cc
index e657a64..968cd5b 100644
--- a/hbase-native-client/serde/rpc.cc
+++ b/hbase-native-client/serde/rpc.cc
@@ -50,6 +50,7 @@ static const std::string PREAMBLE = "HBas";
 static const std::string INTERFACE = "ClientService";
 static const uint8_t RPC_VERSION = 0;
 static const uint8_t DEFAULT_AUTH_TYPE = 80;
+static const uint8_t KERBEROS_AUTH_TYPE = 81;
 
 int RpcSerde::ParseDelimited(const IOBuf *buf, Message *msg) {
   if (buf == nullptr || msg == nullptr) {
@@ -85,17 +86,21 @@ int RpcSerde::ParseDelimited(const IOBuf *buf, Message *msg) {
   return coded_stream.CurrentPosition();
 }
 
-RpcSerde::RpcSerde(std::shared_ptr<Codec> codec) : auth_type_(DEFAULT_AUTH_TYPE), codec_(codec) {}
+RpcSerde::RpcSerde(std::shared_ptr<Codec> codec) : codec_(codec) {}
 
-unique_ptr<IOBuf> RpcSerde::Preamble() {
+std::unique_ptr<IOBuf> RpcSerde::Preamble(bool secure) {
   auto magic = IOBuf::copyBuffer(PREAMBLE, 0, 2);
   magic->append(2);
   RWPrivateCursor c(magic.get());
   c.skip(4);
   // Version
   c.write(RPC_VERSION);
-  // Standard security aka Please don't lie to me.
-  c.write(auth_type_);
+  if (secure) {
+    // for now support only KERBEROS (DIGEST is not supported)
+    c.write(KERBEROS_AUTH_TYPE);
+  } else {
+    c.write(DEFAULT_AUTH_TYPE);
+  }
   return magic;
 }
 
diff --git a/hbase-native-client/serde/rpc.h b/hbase-native-client/serde/rpc.h
index abebe94..15aa1ee 100644
--- a/hbase-native-client/serde/rpc.h
+++ b/hbase-native-client/serde/rpc.h
@@ -68,7 +68,7 @@ class RpcSerde {
   /**
    * Create a new connection preamble in a new IOBuf.
    */
-  std::unique_ptr<folly::IOBuf> Preamble();
+  static std::unique_ptr<folly::IOBuf> Preamble(bool secure);
 
   /**
    * Create the header protobuf object and serialize it to a new IOBuf.
@@ -119,7 +119,6 @@ class RpcSerde {
 
  private:
   /* data */
-  uint8_t auth_type_;
   std::shared_ptr<Codec> codec_;
   std::unique_ptr<pb::VersionInfo> CreateVersionInfo();
 };
diff --git a/hbase-native-client/third-party/BUCK b/hbase-native-client/third-party/BUCK
index f37eb4e..418323b 100644
--- a/hbase-native-client/third-party/BUCK
+++ b/hbase-native-client/third-party/BUCK
@@ -91,7 +91,8 @@ wangle = add_system_libs(
 genrule(
     name="gen_zk",
     out="gen_zk",
-    bash="mkdir -p $OUT  && wget http://www-us.apache.org/dist/zookeeper/zookeeper-3.4.8/zookeeper-3.4.8.tar.gz &&   tar zxf zookeeper-3.4.8.tar.gz &&   rm -rf zookeeper-3.4.8.tar.gz &&   cd zookeeper-3.4.8 &&   cd src/c && ./configure --prefix=$OUT &&   make &&   make install && cd $OUT && rm -rf zookeeper-3.4.8*"
+    bash=
+    "mkdir -p $OUT  && wget http://www-us.apache.org/dist/zookeeper/zookeeper-3.4.8/zookeeper-3.4.8.tar.gz &&   tar zxf zookeeper-3.4.8.tar.gz &&   rm -rf zookeeper-3.4.8.tar.gz &&   cd zookeeper-3.4.8 &&   cd src/c && ./configure --prefix=$OUT &&   make &&   make install && cd $OUT && rm -rf zookeeper-3.4.8*"
 )
 cxx_library(
     name='google-test',
@@ -112,4 +113,6 @@ cxx_library(
         ('googletest/googlemock', 'src/*.cc'),
     ]),
     exported_deps=dynamic_rules,
-    visibility=['PUBLIC',],)
+    visibility=[
+        'PUBLIC',
+    ],)
diff --git a/hbase-native-client/utils/BUCK b/hbase-native-client/utils/BUCK
index 04e2b67..ed8e114 100644
--- a/hbase-native-client/utils/BUCK
+++ b/hbase-native-client/utils/BUCK
@@ -26,15 +26,29 @@ cxx_library(
         "version.h",
     ],
     srcs=["bytes-util.cc", "connection-util.cc", "user-util.cc"],
-    deps=['//third-party:folly',],
+    deps=[
+        '//third-party:folly',
+    ],
     tests=[":user-util-test"],
-    visibility=['PUBLIC',],
+    linker_flags=['-L/usr/local/lib', '-lkrb5'],
+    exported_linker_flags=['-L/usr/local/lib', '-lkrb5'],
+    visibility=[
+        'PUBLIC',
+    ],
     compiler_flags=['-Weffc++'],)
 cxx_test(
     name="user-util-test",
-    srcs=["user-util-test.cc",],
-    deps=[":utils",],)
+    srcs=[
+        "user-util-test.cc",
+    ],
+    deps=[
+        ":utils",
+    ],)
 cxx_test(
     name="bytes-util-test",
-    srcs=["bytes-util-test.cc",],
-    deps=[":utils",],)
+    srcs=[
+        "bytes-util-test.cc",
+    ],
+    deps=[
+        ":utils",
+    ],)
diff --git a/hbase-native-client/utils/user-util-test.cc b/hbase-native-client/utils/user-util-test.cc
index 7c11d8c..aa3fa45 100644
--- a/hbase-native-client/utils/user-util-test.cc
+++ b/hbase-native-client/utils/user-util-test.cc
@@ -28,7 +28,7 @@ using namespace hbase;
 
 TEST(TestUserUtil, TestGetSomething) {
   UserUtil u_util;
-  string name = u_util.user_name();
+  string name = u_util.user_name(false);
 
   // TODO shell out to whoami to check this.
   ASSERT_GT(name.length(), 0);
diff --git a/hbase-native-client/utils/user-util.cc b/hbase-native-client/utils/user-util.cc
index 9e170e0..71f0012 100644
--- a/hbase-native-client/utils/user-util.cc
+++ b/hbase-native-client/utils/user-util.cc
@@ -20,6 +20,7 @@
 #include "utils/user-util.h"
 
 #include <folly/Logging.h>
+#include <krb5/krb5.h>
 #include <pwd.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -27,14 +28,14 @@
 using namespace hbase;
 using namespace std;
 
-UserUtil::UserUtil() : once_flag_{}, user_name_{"drwho"} {}
+UserUtil::UserUtil() : once_flag_{} {}
 
-string UserUtil::user_name() {
-  std::call_once(once_flag_, [this]() { compute_user_name(); });
+string UserUtil::user_name(bool secure) {
+  std::call_once(once_flag_, [this, secure]() { compute_user_name(secure); });
   return user_name_;
 }
 
-void UserUtil::compute_user_name() {
+void UserUtil::compute_user_name(bool secure) {
   // According to the man page of getpwuid
   // this should never be free'd
   //
@@ -45,4 +46,32 @@ void UserUtil::compute_user_name() {
   if (passwd && passwd->pw_name) {
     user_name_ = string{passwd->pw_name};
   }
+  if (!secure) return;
+  krb5_context ctx;
+  krb5_error_code ret = krb5_init_context(&ctx);
+  if (ret != 0) {
+    throw std::runtime_error("cannot init krb ctx " + std::to_string(ret));
+  }
+  krb5_ccache ccache;
+  ret = krb5_cc_default(ctx, &ccache);
+  if (ret != 0) {
+    throw std::runtime_error("cannot get default cache " + std::to_string(ret));
+  }
+  // Here is sample principal: hbase/23a03935850c@EXAMPLE.COM
+  // There may be one (user) or two (user/host) components before the @ sign
+  krb5_principal princ;
+  ret = krb5_cc_get_principal(ctx, ccache, &princ);
+  if (ret != 0) {
+    throw std::runtime_error("cannot get default principal " + std::to_string(ret));
+  }
+  user_name_ = princ->data->data;
+  if (krb5_princ_size(ctx, princ) >= 2) {
+    user_name_ += "/";
+    user_name_ += static_cast<char *>(princ->data[1].data);
+  }
+  user_name_ += "@";
+  user_name_ += princ->realm.data;
+  VLOG(1) << "user " << user_name_;
+  krb5_free_principal(ctx, princ);
+  krb5_free_context(ctx);
 }
diff --git a/hbase-native-client/utils/user-util.h b/hbase-native-client/utils/user-util.h
index 6f8fce1..6258c85 100644
--- a/hbase-native-client/utils/user-util.h
+++ b/hbase-native-client/utils/user-util.h
@@ -41,13 +41,13 @@ class UserUtil {
    * Get the username of the user owning this process. This is thread safe and
    * lockless for every invocation other than the first one.
    */
-  std::string user_name();
+  std::string user_name(bool secure = false);
 
  private:
   /**
    * Compute the username. This will block.
    */
-  void compute_user_name();
+  void compute_user_name(bool secure);
   std::once_flag once_flag_;
   std::string user_name_;
 };


Mime
View raw message