This is an automated email from the ASF dual-hosted git repository.

alexey pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git


The following commit(s) were added to refs/heads/master by this push:
     new f9517b181 [ranger-kms] KUDU-3385 - part2: mini_ranger init
f9517b181 is described below

commit f9517b181101bd122b73d9826d80d3b31641542d
Author: Zoltan Chovan <[email protected]>
AuthorDate: Tue May 3 20:47:20 2022 +0200

    [ranger-kms] KUDU-3385 - part2: mini_ranger init
    
    For Kudu to be able to provide data at rest encryption, a separate key
    management service can be used for encrypting/decrypting keys.
    
    This patch lays the groundwork for integrating RangerKMS as such a
    service, enabling us to create mini test clusters with it, thus making
    it possible to test the required interactions properly.
    
    Two existing services mini_ranger and mini_pg were modified, so that the
    mini_pg instance could be shared between mini_ranger and mini_ranger_kms
    and other future services as well.
    
    Change-Id: I11617468245068dd732fb3f2578bb086b2f6024f
    Reviewed-on: http://gerrit.cloudera.org:8080/18645
    Reviewed-by: Alexey Serbin <[email protected]>
    Reviewed-by: Attila Bukor <[email protected]>
    Tested-by: Kudu Jenkins
---
 CMakeLists.txt                                     |   1 +
 src/kudu/mini-cluster/CMakeLists.txt               |   1 +
 src/kudu/mini-cluster/external_mini_cluster.cc     |  25 +-
 src/kudu/mini-cluster/external_mini_cluster.h      |  25 +-
 src/kudu/postgres/mini_postgres.cc                 |   3 +-
 src/kudu/postgres/mini_postgres.h                  |   8 +
 .../{mini-cluster => ranger-kms}/CMakeLists.txt    |  61 +-
 src/kudu/ranger-kms/mini_ranger_kms.cc             | 306 +++++++++
 src/kudu/ranger-kms/mini_ranger_kms.h              | 164 +++++
 src/kudu/ranger-kms/mini_ranger_kms_configs.h      | 723 +++++++++++++++++++++
 src/kudu/ranger/mini_ranger-test.cc                |  10 +-
 src/kudu/ranger/mini_ranger.cc                     |  20 +-
 src/kudu/ranger/mini_ranger.h                      |  31 +-
 src/kudu/ranger/ranger_client-test.cc              |   6 +-
 src/kudu/util/env_util.cc                          |  53 +-
 src/kudu/util/env_util.h                           |   8 +
 16 files changed, 1364 insertions(+), 81 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6ef5877db..4ce900a59 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1493,6 +1493,7 @@ add_subdirectory(src/kudu/master)
 add_subdirectory(src/kudu/mini-cluster)
 add_subdirectory(src/kudu/postgres)
 add_subdirectory(src/kudu/ranger)
+add_subdirectory(src/kudu/ranger-kms)
 add_subdirectory(src/kudu/rebalance)
 add_subdirectory(src/kudu/rpc)
 add_subdirectory(src/kudu/security)
diff --git a/src/kudu/mini-cluster/CMakeLists.txt 
b/src/kudu/mini-cluster/CMakeLists.txt
index 047c08c05..479e83f44 100644
--- a/src/kudu/mini-cluster/CMakeLists.txt
+++ b/src/kudu/mini-cluster/CMakeLists.txt
@@ -38,6 +38,7 @@ set(MINI_CLUSTER_LIBS
   mini_hms
   mini_kdc
   mini_ranger
+  mini_ranger_kms
   server_base_proto
   tablet_proto
   tserver
diff --git a/src/kudu/mini-cluster/external_mini_cluster.cc 
b/src/kudu/mini-cluster/external_mini_cluster.cc
index 91e79dce8..7a765b83b 100644
--- a/src/kudu/mini-cluster/external_mini_cluster.cc
+++ b/src/kudu/mini-cluster/external_mini_cluster.cc
@@ -35,6 +35,8 @@
 #include "kudu/client/client.h"
 #include "kudu/client/master_rpc.h"
 #include "kudu/fs/fs.pb.h"
+#include "kudu/postgres/mini_postgres.h"
+#include "kudu/ranger-kms/mini_ranger_kms.h"
 #include "kudu/rpc/rpc_header.pb.h"
 #include "kudu/server/key_provider.h"
 #if !defined(NO_CHRONY)
@@ -131,6 +133,7 @@ ExternalMiniClusterOptions::ExternalMiniClusterOptions()
       principal("kudu"),
       hms_mode(HmsMode::NONE),
       enable_ranger(false),
+      enable_ranger_kms(false),
       enable_encryption(FLAGS_encrypt_data_at_rest),
       logtostderr(true),
       start_process_timeout(MonoDelta::FromSeconds(70)),
@@ -323,9 +326,12 @@ Status ExternalMiniCluster::Start() {
   }
 #endif // #if !defined(NO_CHRONY) ...
 
-  if (opts_.enable_ranger) {
+  if (opts_.enable_ranger || opts_.enable_ranger_kms) {
+    if (!postgres_ || !postgres_->IsStarted()) {
+      postgres_.reset(new postgres::MiniPostgres(cluster_root(), 
GetBindIpForExternalServer(0)));
+    }
     string host = GetBindIpForExternalServer(0);
-    ranger_.reset(new ranger::MiniRanger(cluster_root(), host));
+    ranger_.reset(new ranger::MiniRanger(cluster_root(), host, postgres_));
     if (opts_.enable_kerberos) {
 
       // The SPNs match the ones defined in mini_ranger_configs.h.
@@ -357,6 +363,21 @@ Status ExternalMiniCluster::Start() {
                           "Failed to write Ranger client config");
   }
 
+  if (opts_.enable_ranger_kms) {
+    string host = GetBindIpForExternalServer(0);
+    ranger_kms_.reset(new rangerkms::MiniRangerKMS(cluster_root(), host, 
postgres_, ranger_));
+    if (opts_.enable_kerberos) {
+      string keytab;
+      RETURN_NOT_OK_PREPEND(kdc_->CreateServiceKeytab(
+      Substitute("rangerkms/[email protected]", host),
+                  &keytab),
+                  "could not create rangeradmin keytab");
+
+      ranger_kms_->EnableKerberos(kdc_->GetEnvVars()["KRB5_CONFIG"], keytab);
+    }
+    RETURN_NOT_OK_PREPEND(ranger_kms_->Start(), "Failed to start the Ranger 
KMS service");
+  }
+
   // Start the HMS.
   if (opts_.hms_mode == HmsMode::DISABLE_HIVE_METASTORE ||
       opts_.hms_mode == HmsMode::ENABLE_HIVE_METASTORE ||
diff --git a/src/kudu/mini-cluster/external_mini_cluster.h 
b/src/kudu/mini-cluster/external_mini_cluster.h
index 26422c454..b42494c44 100644
--- a/src/kudu/mini-cluster/external_mini_cluster.h
+++ b/src/kudu/mini-cluster/external_mini_cluster.h
@@ -48,6 +48,10 @@ class Env;
 class NodeInstancePB;
 class Sockaddr;
 class Subprocess;
+namespace rangerkms {
+class MiniRangerKMS;
+}  // namespace rangerkms
+
 namespace security {
 class KeyProvider;
 }  // namespace security
@@ -75,6 +79,10 @@ namespace rpc {
 class Messenger;
 } // namespace rpc
 
+namespace postgres {
+class MiniPostgres;
+} // namespace postgres
+
 namespace ranger {
 class MiniRanger;
 } // namespace ranger
@@ -214,6 +222,11 @@ struct ExternalMiniClusterOptions {
   // Default: false.
   bool enable_ranger;
 
+  // If true, set up a Ranger KMS service as part of this ExternalMiniCluster.
+  //
+  // Default: false.
+  bool enable_ranger_kms;
+
   // If true, enable data at rest encryption.
   //
   // Default: false.
@@ -376,10 +389,18 @@ class ExternalMiniCluster : public MiniCluster {
     return hms_.get();
   }
 
+  postgres::MiniPostgres* postgres() const {
+    return postgres_.get();
+  }
+
   ranger::MiniRanger* ranger() const {
     return ranger_.get();
   }
 
+  rangerkms::MiniRangerKMS* ranger_kms() const {
+    return ranger_kms_.get();
+  }
+
   const std::string& cluster_root() const {
     return opts_.cluster_root;
   }
@@ -550,8 +571,10 @@ class ExternalMiniCluster : public MiniCluster {
 #endif
   std::unique_ptr<MiniKdc> kdc_;
   std::unique_ptr<hms::MiniHms> hms_;
-  std::unique_ptr<ranger::MiniRanger> ranger_;
+  std::shared_ptr<postgres::MiniPostgres> postgres_;
+  std::shared_ptr<ranger::MiniRanger> ranger_;
   std::unique_ptr<security::KeyProvider> key_provider_;
+  std::unique_ptr<rangerkms::MiniRangerKMS> ranger_kms_;
 
   std::shared_ptr<rpc::Messenger> messenger_;
 
diff --git a/src/kudu/postgres/mini_postgres.cc 
b/src/kudu/postgres/mini_postgres.cc
index aafc8f0bf..08ceafa0c 100644
--- a/src/kudu/postgres/mini_postgres.cc
+++ b/src/kudu/postgres/mini_postgres.cc
@@ -53,11 +53,10 @@ Status MiniPostgres::Start() {
   if (process_) {
     return Status::IllegalState("Postgres already running");
   }
-  Env* env = Env::Default();
 
   VLOG(1) << "Starting Postgres";
   string pgr = pg_root();
-  if (!env->FileExists(pgr)) {
+  if (IsFirstRun()) {
     // This is our first time running. Set up our directories, config files,
     // and port.
     LOG(INFO) << "Running initdb...";
diff --git a/src/kudu/postgres/mini_postgres.h 
b/src/kudu/postgres/mini_postgres.h
index 67ac085ad..201fc711a 100644
--- a/src/kudu/postgres/mini_postgres.h
+++ b/src/kudu/postgres/mini_postgres.h
@@ -69,6 +69,10 @@ class MiniPostgres {
     return port_;
   }
 
+  const std::string& host() const {
+    return host_;
+  }
+
   std::string pg_root() const {
     return JoinPathSegments(data_root_, "postgres");
   }
@@ -77,6 +81,10 @@ class MiniPostgres {
     return JoinPathSegments(bin_dir_, "postgres");
   }
 
+  bool IsFirstRun() const { return !Env::Default()->FileExists(pg_root()); }
+
+  bool IsStarted() const { return process_ && process_->IsStarted(); }
+
  private:
   static std::string GetBinDir() {
     Env* env = Env::Default();
diff --git a/src/kudu/mini-cluster/CMakeLists.txt 
b/src/kudu/ranger-kms/CMakeLists.txt
similarity index 50%
copy from src/kudu/mini-cluster/CMakeLists.txt
copy to src/kudu/ranger-kms/CMakeLists.txt
index 047c08c05..a119668b8 100644
--- a/src/kudu/mini-cluster/CMakeLists.txt
+++ b/src/kudu/ranger-kms/CMakeLists.txt
@@ -15,46 +15,29 @@
 # specific language governing permissions and limitations
 # under the License.
 
-set(MINI_CLUSTER_SRCS
-  external_mini_cluster.cc
-  internal_mini_cluster.cc
-  mini_cluster.cc
-  webui_checker.cc
-)
+#######################################
+# mini_ranger_kms
+#######################################
+execute_process(COMMAND ln -nsf
+        "${THIRDPARTY_DIR}/installed/common/opt/ranger-kms"
+        "${EXECUTABLE_OUTPUT_PATH}/ranger_kms-home")
 
-set(MINI_CLUSTER_LIBS
-  gflags
-  glog
-  gmock
-  gtest
-  gutil
-  krpc
-  kudu_client
-  kudu_common
-  kudu_test_util
-  kudu_util
-  master
-  master_proto
-  mini_hms
-  mini_kdc
-  mini_ranger
-  server_base_proto
-  tablet_proto
-  tserver
-  tserver_proto
-  tserver_service_proto
-  wire_protocol_proto)
+set(MINI_RANGER_KMS_SRCS
+        mini_ranger_kms.cc)
+set(MINI_RANGER_KMS_DEPS
+        kudu_test_util
+        kudu_util
+        mini_postgres
+        mini_ranger)
 
-if (NOT NO_CHRONY)
-  set(MINI_CLUSTER_LIBS ${MINI_CLUSTER_LIBS} mini_chronyd)
-endif()
+add_library(mini_ranger_kms ${MINI_RANGER_KMS_SRCS})
+target_link_libraries(mini_ranger_kms ${MINI_RANGER_KMS_DEPS})
 
-add_library(mini_cluster ${MINI_CLUSTER_SRCS})
-target_link_libraries(mini_cluster ${MINI_CLUSTER_LIBS})
-add_dependencies(mini_cluster
-  kudu-tserver
-  kudu-master)
+#######################################
+# Unit tests
+#######################################
 
-# Tests
-SET_KUDU_TEST_LINK_LIBS(mini_cluster kudu_hms)
-ADD_KUDU_TEST(external_mini_cluster-test PROCESSORS 3)
+SET_KUDU_TEST_LINK_LIBS(
+        itest_util
+        mini_postgres
+        mini_ranger_kms)
\ No newline at end of file
diff --git a/src/kudu/ranger-kms/mini_ranger_kms.cc 
b/src/kudu/ranger-kms/mini_ranger_kms.cc
new file mode 100644
index 000000000..6ff39f1f9
--- /dev/null
+++ b/src/kudu/ranger-kms/mini_ranger_kms.cc
@@ -0,0 +1,306 @@
+// 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 "kudu/ranger-kms/mini_ranger_kms.h"
+
+#include <csignal>
+
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include <glog/logging.h>
+
+#include "kudu/gutil/strings/substitute.h"
+#include "kudu/postgres/mini_postgres.h"
+#include "kudu/ranger-kms/mini_ranger_kms_configs.h"
+#include "kudu/ranger/mini_ranger.h"
+#include "kudu/util/easy_json.h"
+#include "kudu/util/env_util.h"
+#include "kudu/util/faststring.h"
+#include "kudu/util/monotime.h"
+#include "kudu/util/net/net_util.h"
+#include "kudu/util/slice.h"
+#include "kudu/util/stopwatch.h"
+#include "kudu/util/subprocess.h"
+
+using std::string;
+using std::vector;
+using strings::Substitute;
+
+namespace kudu {
+namespace rangerkms {
+
+Status MiniRangerKMS::Start() {
+  return StartRangerKMS();
+}
+
+MiniRangerKMS::~MiniRangerKMS() {
+  WARN_NOT_OK(Stop(), "Failed to stop Ranger KMS");
+}
+
+Status MiniRangerKMS::Stop() {
+  if (process_) {
+    LOG(INFO) << "Stopping Ranger KMS...";
+    RETURN_NOT_OK(process_->KillAndWait(SIGTERM));
+    LOG(INFO) << "Stopped Ranger KMS";
+    process_.reset();
+  }
+  return mini_ranger_->Stop();
+}
+
+Status MiniRangerKMS::InitRangerKMS(const std::string& kms_home, bool 
*fresh_install) {
+  if (env_->FileExists(kms_home)) {
+    *fresh_install = false;
+    return Status::OK();
+  }
+  *fresh_install = true;
+
+  RETURN_NOT_OK(env_->CreateDir(kms_home));
+
+  RETURN_NOT_OK(mini_pg_->AddUser("rangerkms", /*super=*/false));
+  LOG(INFO) << "Created minirangerkms Postgres user";
+
+  RETURN_NOT_OK(mini_pg_->CreateDb("rangerkms", "rangerkms"));
+  LOG(INFO) << "Created rangerkms Postgres database";
+
+  return Status::OK();
+}
+
+Status MiniRangerKMS::CreateConfigs(const std::string& conf_dir) {
+
+  if (port_ == 0) {
+      RETURN_NOT_OK(GetRandomPort(host_, &port_));
+  }
+
+  string kms_home = ranger_kms_home();
+  ranger_kms_url_ = Substitute("http://$0:$1";, host_, port_);
+
+  // Write config files
+  RETURN_NOT_OK(WriteStringToFile(env_,
+                                  GetRangerKMSInstallProperties(bin_dir(),
+                                                                host_,
+                                                                
mini_pg_->bound_port(),
+                                                                
mini_ranger_->admin_url()),
+                                  JoinPathSegments(kms_home, 
"install.properties")));
+
+  RETURN_NOT_OK(WriteStringToFile(env_,
+                                  GetRangerKMSSiteXml(host_,
+                                                      port_,
+                                                      
JoinPathSegments(kms_home, "ews/webapp"),
+                                                      conf_dir),
+                                  JoinPathSegments(kms_home, 
"ranger-kms-site.xml")));
+
+  RETURN_NOT_OK(WriteStringToFile(env_,
+                                  GetRangerKMSDbksSiteXml(host_,
+                                                          
mini_pg_->bound_port(),
+                                                          "postgresql.jar"),
+                                  JoinPathSegments(kms_home, 
"dbks-site.xml")));
+
+  RETURN_NOT_OK(WriteStringToFile(env_,
+                                  GetRangerKMSLog4jProperties("info"),
+                                  JoinPathSegments(kms_home, 
"log4j.properties")));
+
+  RETURN_NOT_OK(WriteStringToFile(env_,
+                                  
GetRangerKMSSecurityXml(mini_ranger_->admin_url(), kms_home),
+                                  JoinPathSegments(kms_home, 
"ranger-kms-security.xml")));
+
+  RETURN_NOT_OK(WriteStringToFile(env_,
+                                  GetKMSSiteXml(kerberos_, ktpath_),
+                                  JoinPathSegments(kms_home, "kms-site.xml")));
+
+  RETURN_NOT_OK(WriteStringToFile(env_,
+                                  GetRangerKMSPolicymgrSSLXml(),
+                                  JoinPathSegments(kms_home, 
"ranger-kms-policymgr-ssl.xml")));
+
+  return Status::OK();
+}
+
+Status MiniRangerKMS::DbSetup(const std::string &kms_home, const std::string 
&ews_dir,
+                              const std::string &web_app_dir) {
+  RETURN_NOT_OK(env_->CreateDir(ews_dir));
+  RETURN_NOT_OK(env_util::CopyDirectory(env_, 
JoinPathSegments(ranger_kms_home_, "ews/webapp"),
+                                        web_app_dir, WritableFileOptions()));
+  RETURN_NOT_OK(env_->CreateSymLink(JoinPathSegments(ranger_kms_home_, 
"jisql"),
+                                    JoinPathSegments(kms_home, "jisql")));
+  RETURN_NOT_OK(env_->CreateSymLink(JoinPathSegments(ranger_kms_home_, "db"),
+                                    JoinPathSegments(kms_home, "db")));
+  RETURN_NOT_OK(env_->CreateSymLink(JoinPathSegments(ranger_kms_home_, "cred"),
+                                    JoinPathSegments(kms_home, "cred")));
+
+  RETURN_NOT_OK(env_->DeleteRecursively(JoinPathSegments(web_app_dir, 
"WEB-INF/classes/conf")));
+  RETURN_NOT_OK(env_->CreateDir(JoinPathSegments(web_app_dir, 
"WEB-INF/classes/conf")));
+
+  static const vector<string> files_to_copy = {
+    "dbks-site.xml",
+    "install.properties"
+    "kms-site.xml"
+    "log4j.properties",
+    "ranger-kms-policymgr-ssl.xml",
+    "ranger-kms-security.xml"
+  };
+
+  for (const auto& file : files_to_copy) {
+    RETURN_NOT_OK(
+    env_util::CopyFile(env_,
+                       JoinPathSegments(kms_home, file),
+                       JoinPathSegments(web_app_dir,
+                                        Substitute("WEB-INF/classes/conf/$0", 
file)),
+                       WritableFileOptions()));
+  }
+
+  // replace conf files in proc dir from kms home dir
+  Subprocess db_setup({ "python", JoinPathSegments(ranger_kms_home_, 
"db_setup.py")});
+
+  db_setup.SetEnvVars({
+    {"RANGER_KMS_HOME", kms_home},
+    {"RANGER_KMS_CONF", ranger_kms_home_},
+    { "JAVA_HOME", java_home_ },
+  });
+
+  db_setup.SetCurrentDir(kms_home);
+  RETURN_NOT_OK(db_setup.Start());
+  return db_setup.WaitAndCheckExitCode();
+}
+
+Status MiniRangerKMS::StartRangerKMS() {
+  bool fresh_install;
+
+  if (!mini_ranger_->IsRunning()) {
+    return Status::IllegalState("Ranger is not running");
+  }
+
+  LOG_TIMING(INFO, "starting Ranger KMS") {
+    LOG(INFO) << "Starting Ranger KMS...";
+    string exe;
+    RETURN_NOT_OK(env_->GetExecutablePath(&exe));
+    const string bin_dir = DirName(exe);
+    RETURN_NOT_OK(FindHomeDir("hadoop", bin_dir, &hadoop_home_));
+    RETURN_NOT_OK(FindHomeDir("ranger_kms", bin_dir, &ranger_kms_home_));
+    RETURN_NOT_OK(FindHomeDir("java", bin_dir, &java_home_));
+
+    const string kKMSHome = ranger_kms_home();
+    // kms_home/ews
+    const string kEwsDir = JoinPathSegments(kKMSHome, "ews");
+    // kms_home/ews/webapp
+    const string kWebAppDir = JoinPathSegments(kEwsDir, "webapp");
+    // kms_home/ews/webapp/WEB-INF
+    const string kWebInfDir = JoinPathSegments(kWebAppDir, "WEB-INF");
+    // kms_home/ews/webapp/WEB-INF/classes
+    const string kClassesDir = JoinPathSegments(kWebInfDir, "classes");
+    // kms_home/ews/webapp/WEB-INF/classes/conf
+    const string kConfDir = JoinPathSegments(kClassesDir, "conf");
+    // kms_home/ews/webapp/WEB-INF/classes/lib
+    const string kLibDir = JoinPathSegments(kClassesDir, "lib");
+
+    RETURN_NOT_OK(InitRangerKMS(kKMSHome, &fresh_install));
+
+    LOG(INFO) << "Starting Ranger KMS out of " << kKMSHome;
+    LOG(INFO) << "Using postgres at " << mini_pg_->host() << ":" << 
mini_pg_->bound_port();
+
+    RETURN_NOT_OK(CreateConfigs(kConfDir));
+
+    if (fresh_install) {
+        RETURN_NOT_OK(DbSetup(kKMSHome, kEwsDir, kWebAppDir));
+        RETURN_NOT_OK_PREPEND(CreateKMSService(), "Unable to create KMS 
Service in Ranger");
+    }
+
+    string classpath = ranger_kms_classpath();
+
+    LOG(INFO) << "Using RangerKMS classpath: " << classpath;
+
+    LOG(INFO) << "Using host: " << host_;
+
+    // @todo(zchovan): add link to source
+    std::vector<string> args({
+      JoinPathSegments(java_home_, "bin/java"),
+      "-Dproc_rangerkms",
+      Substitute("-Dhostname=$0", host_),
+      Substitute("-Dlog4j.configuration=file:$0",
+                  JoinPathSegments(kKMSHome, "log4j.properties")),
+      "-Duser=minirangerkms",
+      "-Dservername=minirangerkms",
+      Substitute("-Dcatalina.base=$0", kEwsDir),
+      Substitute("-Dkms.config.dir=$0", kConfDir),
+      Substitute("-Dlogdir=$0", JoinPathSegments(kKMSHome, "logs")),
+    });
+
+    if (kerberos_) {
+      args.emplace_back(Substitute("-Djava.security.krb5.conf=$0", 
krb5_config_));
+    }
+
+    args.emplace_back("-cp");
+    args.emplace_back(classpath);
+    args.emplace_back("org.apache.ranger.server.tomcat.EmbeddedServer");
+    args.emplace_back("ranger-kms-site.xml");
+    process_.reset(new Subprocess(args));
+    process_->SetEnvVars({
+         { "JAVA_HOME", java_home_ },
+         { "RANGER_KMS_PID_NAME", "rangerkms.pid" },
+         { "KMS_CONF_DIR", kKMSHome },
+         { "RANGER_USER", "miniranger" },
+         { "RANGER_KMS_DIR", ranger_kms_home_},
+         { "RANGER_KMS_EWS_DIR", kEwsDir},
+         { "RANGER_KMS_EWS_CONF_DIR", kConfDir },
+         { "RANGER_KMS_EWS_LIB_DIR", kLibDir }
+    });
+    RETURN_NOT_OK(process_->Start());
+    LOG(INFO) << "Ranger KMS PID: " << process_->pid() << std::endl;
+    RETURN_NOT_OK(WaitForTcpBind(process_->pid(),
+                                 &port_,
+                                 { "0.0.0.0", "127.0.0.1", },
+                                 MonoDelta::FromMilliseconds(90000)));
+    LOG(INFO) << "Ranger KMS bound to " << port_;
+    LOG(INFO) << "Ranger KMS URL: " << ranger_kms_url_;
+  }
+
+  return Status::OK();
+}
+
+Status MiniRangerKMS::CreateKMSService() {
+  string service_name = "kms";
+  EasyJson service;
+  service.Set("name", service_name);
+  service.Set("type", service_name);
+
+  EasyJson configs = service.Set("configs", EasyJson::kObject);
+  configs.Set("policy.download.auth.users", service_name);
+  configs.Set("tag.download.auth.users", service_name);
+  configs.Set("provider", ranger_kms_url_);
+  configs.Set("username", "rangerkms");
+  configs.Set("password", "rangerkms");
+
+  RETURN_NOT_OK_PREPEND(mini_ranger_->PostToRanger("service/plugins/services", 
service),
+                        Substitute("Failed to create $0 service", 
service_name));
+  LOG(INFO) << Substitute("Created $0 service", service_name);
+  return Status::OK();
+}
+
+Status MiniRangerKMS::GetKeys() const {
+  EasyCurl curl;
+  faststring response;
+
+  LOG(INFO) << Substitute("fetching: $0:$1/kms/v1/keys/names", host_, port_) 
<< std::endl;
+  RETURN_NOT_OK(curl.FetchURL(Substitute("$0:$1/kms/v1/keys/names", host_, 
port_), &response));
+  LOG(INFO) << "response: ";
+  LOG(INFO) << response << std::endl;
+
+  return Status::OK();
+}
+
+} // namespace rangerkms
+} // namespace kudu
diff --git a/src/kudu/ranger-kms/mini_ranger_kms.h 
b/src/kudu/ranger-kms/mini_ranger_kms.h
new file mode 100644
index 000000000..262f8105e
--- /dev/null
+++ b/src/kudu/ranger-kms/mini_ranger_kms.h
@@ -0,0 +1,164 @@
+// 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 <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <glog/logging.h>
+
+#include "kudu/gutil/port.h"
+#include "kudu/gutil/strings/substitute.h"
+#include "kudu/util/curl_util.h"
+#include "kudu/util/env.h"
+#include "kudu/util/path_util.h"
+#include "kudu/util/status.h"
+#include "kudu/util/test_util.h"
+
+namespace kudu {
+class Subprocess;
+namespace postgres {
+class MiniPostgres;
+}  // namespace postgres
+namespace ranger {
+class MiniRanger;
+}  // namespace ranger
+
+namespace rangerkms {
+
+class MiniRangerKMS {
+ public:
+  MiniRangerKMS(std::string host,
+                std::shared_ptr<postgres::MiniPostgres> mini_pg,
+                std::shared_ptr<ranger::MiniRanger> mini_ranger)
+      : MiniRangerKMS(GetTestDataDirectory(),
+                      std::move(host),
+                      std::move(mini_pg),
+                      std::move(mini_ranger)) {}
+
+  ~MiniRangerKMS();
+
+  MiniRangerKMS(std::string data_root, std::string host,
+                std::shared_ptr<postgres::MiniPostgres> mini_pg,
+                std::shared_ptr<ranger::MiniRanger> mini_ranger)
+        : data_root_(std::move(data_root)),
+          host_(std::move(host)),
+          mini_pg_(std::move(mini_pg)),
+          mini_ranger_(std::move(mini_ranger)),
+          env_(Env::Default()) {
+            curl_.set_auth(CurlAuthType::BASIC, "admin", "admin");
+  }
+
+  // Starts Ranger and its dependencies.
+  Status Start() WARN_UNUSED_RESULT;
+
+  // Stops Ranger and its dependencies.
+  Status Stop() WARN_UNUSED_RESULT;
+
+  Status CreateKMSService();
+
+  Status GetKeys() const;
+
+  void EnableKerberos(std::string krb5_config,
+                      std::string ktpath) {
+    kerberos_ = true;
+    krb5_config_ = std::move(krb5_config);
+    ktpath_ = std::move(ktpath);
+  }
+
+ private:
+  // Starts RangerKMS Service
+  Status StartRangerKMS() WARN_UNUSED_RESULT;
+
+  // Initializes Ranger KMS within 'kms_home' (home directory of the Ranger KMS
+  // admin). Sets 'fresh_install' to true if 'kms_home' didn't exist before
+  // calling InitRangerKMS().
+  Status InitRangerKMS(const std::string& kms_home, bool* fresh_install) 
WARN_UNUSED_RESULT;
+
+  // Creates configuration files.
+  // ref:
+  // 
https://docs.cloudera.com/HDPDocuments/HDP2/HDP-2.6.5/bk_security/content/ranger_kms_properties.html
+  Status CreateConfigs(const std::string& conf_dir) WARN_UNUSED_RESULT;
+
+  // Initializes Ranger KMS' database.
+  Status DbSetup(const std::string& kms_home, const std::string& ews_dir,
+                 const std::string& web_app_dir) WARN_UNUSED_RESULT;
+
+  // Returns RangerKMS' home directory.
+  std::string ranger_kms_home() const {
+    return JoinPathSegments(data_root_, "rangerkms");
+  }
+
+  std::string bin_dir() const {
+    std::string exe;
+    CHECK_OK(env_->GetExecutablePath(&exe));
+    return DirName(exe);
+  }
+
+  // Returns classpath for Ranger KMS.
+  std::string ranger_kms_classpath() const {
+    std::string kms_home = ranger_kms_home();
+    // ${RANGER_KMS_CONF_DIR}:
+    // ${SQL_CONNECTOR_JAR}:
+    // ${RANGER_KMS_HOME}/ews/webapp/WEB-INF/classes/lib/:
+    // ${RANGER_KMS_HOME}/ews/webapp/lib/:
+    // ${JAVA_HOME}/lib/:
+    // ${RANGER_KMS_HADOOP_CONF_DIR}/"
+    return strings::Substitute("$0:$1:$2:$3:$4",
+                               kms_home,
+                               JoinPathSegments(bin_dir(), "postgresql.jar"),
+                               JoinPathSegments(ranger_kms_home_,
+                                                
"ews/webapp/WEB-INF/classes/lib/*"),
+                               JoinPathSegments(ranger_kms_home_, 
"ews/webapp/lib/*"),
+                               JoinPathSegments(java_home_, "lib/*"),
+                               JoinPathSegments(hadoop_home_, "conf"));
+  }
+  // Directory in which to put all our stuff.
+  const std::string data_root_;
+  // Host url for RangerKMS.
+  const std::string host_;
+
+  std::shared_ptr<postgres::MiniPostgres> mini_pg_;
+  std::shared_ptr<ranger::MiniRanger> mini_ranger_;
+  std::unique_ptr<Subprocess> process_;
+
+  // URL of the Ranger KMS REST API.
+  std::string ranger_kms_url_;
+
+  // Locations in which to find Hadoop, Ranger, and Java.
+  // These may be in the thirdparty build, or may be shared across tests. As
+  // such, their contents should be treated as read-only.
+  std::string hadoop_home_;
+  std::string ranger_kms_home_;
+  std::string java_home_;
+
+  bool kerberos_;
+  std::string krb5_config_;
+  std::string ktpath_;
+
+  Env* env_;
+  EasyCurl curl_;
+
+  uint16_t port_ = 0;
+};
+
+
+} // namespace rangerkms
+} // namespace kudu
diff --git a/src/kudu/ranger-kms/mini_ranger_kms_configs.h 
b/src/kudu/ranger-kms/mini_ranger_kms_configs.h
new file mode 100644
index 000000000..8b5011d8a
--- /dev/null
+++ b/src/kudu/ranger-kms/mini_ranger_kms_configs.h
@@ -0,0 +1,723 @@
+// 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 <string>
+
+#include "kudu/gutil/strings/substitute.h"
+
+namespace kudu {
+namespace rangerkms {
+
+// Gets the contents of the install.properties file used by the db_setup.py
+// script.
+inline std::string GetRangerKMSInstallProperties(const std::string& bin_dir,
+                                                 const std::string& pg_host,
+                                                 uint16_t pg_port,
+                                                 const std::string& 
ranger_url) {
+  // Taken and modified from:
+  // 
https://github.com/apache/ranger/blob/master/kms/scripts/install.properties
+  //
+  // $0: directory containing postgresql.jar
+  // $1: postgres host
+  // $2: postgres port
+  // $3: ranger url
+  constexpr const char* const kRangerKMSInstallPropertiesTemplate = R"(
+PYTHON_COMMAND_INVOKER=python
+DB_FLAVOR=POSTGRES
+SQL_CONNECTOR_JAR=$0/postgresql.jar
+db_root_user=postgres
+db_root_password=
+db_host=$1:$2
+db_ssl_enabled=false
+db_ssl_required=false
+db_ssl_verifyServerCertificate=false
+db_name=rangerkms
+db_user=rangerkms
+db_password=
+ranger_kms_http_enabled=true
+KMS_MASTER_KEY_PASSWD=Str0ngPassw0rd
+POLICY_MGR_URL=$3
+REPOSITORY_NAME=kms
+XAAUDIT.SUMMARY.ENABLE=false
+XAAUDIT.ELASTICSEARCH.ENABLE=false
+XAAUDIT.HDFS.ENABLE=false
+XAAUDIT.LOG4J.ENABLE=false
+mysql_core_file=db/mysql/kms_core_db.sql
+oracle_core_file=db/oracle/kms_core_db_oracle.sql
+postgres_core_file=db/postgres/kms_core_db_postgres.sql
+sqlserver_core_file=db/sqlserver/kms_core_db_sqlserver.sql
+sqlanywhere_core_file=db/sqlanywhere/kms_core_db_sqlanywhere.sql
+KMS_BLACKLIST_DECRYPT_EEK=hdfs)";
+
+  return strings::Substitute(kRangerKMSInstallPropertiesTemplate,
+                             bin_dir, pg_host, pg_port, ranger_url);
+}
+
+inline std::string GetRangerKMSSiteXml(const std::string& kms_host,
+                                       uint16_t kms_port,
+                                       const std::string& webapp_dir,
+                                       const std::string& conf_dir) {
+  constexpr const char* const kRangerKMSSiteXmlTemplate = R"(
+<configuration>
+  <property>
+    <name>ranger.service.host</name>
+    <value>$0</value>
+  </property>
+  <property>
+    <name>ranger.service.http.port</name>
+    <value>$1</value>
+  </property>
+  <property>
+    <name>ranger.service.shutdown.port</name>
+    <value>0</value>
+  </property>
+  <property>
+    <name>ranger.contextName</name>
+    <value>/kms</value>
+  </property>
+  <property>
+    <name>xa.webapp.dir</name>
+    <value>$2</value>
+  </property>
+  <property>
+    <name>ranger.service.https.attrib.ssl.enabled</name>
+    <value>false</value>
+  </property>
+  <property>
+    <name>ajp.enabled</name>
+    <value>false</value>
+  </property>
+  <property>
+    <name>ranger.service.https.attrib.client.auth</name>
+    <value>want</value>
+  </property>
+  <property>
+    <name>ranger.credential.provider.path</name>
+    <value>/etc/ranger/kms/rangerkms.jceks</value>
+  </property>
+  <property>
+    <name>ranger.service.https.attrib.keystore.file</name>
+    <value />
+  </property>
+  <property>
+    <name>ranger.service.https.attrib.keystore.keyalias</name>
+    <value>rangerkms</value>
+  </property>
+  <property>
+    <name>ranger.service.https.attrib.keystore.pass</name>
+    <value />
+  </property>
+  <property>
+    <name>ranger.service.https.attrib.keystore.credential.alias</name>
+    <value>keyStoreCredentialAlias</value>
+  </property>
+  <property>
+    <name>kms.config.dir</name>
+    <value>$3</value>
+  </property>
+</configuration>)";
+  return strings::Substitute(kRangerKMSSiteXmlTemplate, kms_host, kms_port, 
webapp_dir, conf_dir);
+}
+
+inline std::string GetRangerKMSDbksSiteXml(const std::string& pg_host,
+                                           const uint16_t pg_port,
+                                           const std::string& pg_driver) {
+  constexpr const char* const kRangerKMSDbksSiteXmlTemplate = R"(
+<configuration>
+  <property>
+    <name>hadoop.kms.blacklist.DECRYPT_EEK</name>
+    <value>hdfs</value>
+    <description>
+          Blacklist for decrypt EncryptedKey
+          CryptoExtension operations
+    </description>
+  </property>
+  <property>
+    <name>ranger.db.encrypt.key.password</name>
+    <value>Str0ngPassw0rd</value>
+    <description>
+            Password used for encrypting Master Key
+    </description>
+  </property>
+  <property>
+    <name>ranger.kms.service.masterkey.password.cipher</name>
+    <value>AES</value>
+    <description>
+            Cipher used for encrypting Master Key
+    </description>
+  </property>
+  <property>
+   <name>ranger.kms.service.masterkey.password.size</name>
+   <value>256</value>
+    <description>
+            Size of masterkey
+    </description>
+ </property>
+  <property>
+    <name>ranger.kms.service.masterkey.password.salt.size</name>
+    <value>8</value>
+    <description>
+            Salt size to encrypt Master Key
+    </description>
+  </property>
+  <property>
+    <name>ranger.kms.service.masterkey.password.salt</name>
+    <value>abcdefghijklmnopqrstuvwxyz01234567890</value>
+    <description>
+            Salt to encrypt Master Key
+    </description>
+  </property>
+  <property>
+    <name>ranger.kms.service.masterkey.password.iteration.count</name>
+    <value>1000</value>
+    <description>
+            Iteration count to encrypt Master Key
+    </description>
+  </property>
+  <property>
+    <name>ranger.kms.service.masterkey.password.encryption.algorithm</name>
+    <value>PBEWithMD5AndDES</value>
+    <description>
+            Algorithm to encrypt Master Key
+    </description>
+  </property>
+  <property>
+    <name>ranger.kms.service.masterkey.password.md.algorithm</name>
+    <value>SHA</value>
+    <description>
+            Message Digest algorithn to encrypt Master Key
+    </description>
+  </property>
+  <property>
+    <name>ranger.ks.jpa.jdbc.url</name>
+    <value>jdbc:postgresql://$0:$1/rangerkms</value>
+    <description>
+      URL for Database
+    </description>
+  </property>
+  <property>
+    <name>ranger.ks.jpa.jdbc.user</name>
+    <value>rangerkms</value>
+    <description>
+      Database username used for operation
+    </description>
+  </property>
+  <property>
+    <name>ranger.ks.jpa.jdbc.password</name>
+    <value></value>
+    <description>
+      Database user's password
+    </description>
+  </property>
+  <property>
+    <name>ranger.ks.jpa.jdbc.credential.provider.path</name>
+    
<value>/root/ranger-2.1.0-kms/ews/webapp/WEB-INF/classes/conf/.jceks/rangerkms.jceks</value>
+    <description>
+      Credential provider path
+    </description>
+  </property>
+  <property>
+    <name>ranger.ks.jpa.jdbc.credential.alias</name>
+    <value>ranger.ks.jpa.jdbc.credential.alias</value>
+    <description>
+      Credential alias used for password
+    </description>
+  </property>
+  <property>
+    <name>ranger.ks.masterkey.credential.alias</name>
+    <value>ranger.ks.masterkey.password</value>
+    <description>
+      Credential alias used for masterkey
+    </description>
+  </property>
+  <property>
+    <name>ranger.ks.jpa.jdbc.dialect</name>
+    <value>org.eclipse.persistence.platform.database.PostgreSQLPlatform</value>
+    <description>
+      Dialect used for database
+    </description>
+  </property>
+  <property>
+    <name>ranger.ks.jpa.jdbc.driver</name>
+    <value>org.postgresql.Driver</value>
+    <description>
+      Driver used for database
+    </description>
+  </property>
+  <property>
+    <name>ranger.ks.jdbc.sqlconnectorjar</name>
+    <value>$2</value>
+    <description>
+      Driver used for database
+    </description>
+  </property>
+  <property>
+    <name>ranger.ks.kerberos.principal</name>
+    <value>rangerkms/[email protected]</value>
+  </property>
+  <property>
+    <name>ranger.ks.kerberos.keytab</name>
+    <value />
+  </property>
+  <property>
+    <name>ranger.kms.keysecure.enabled</name>
+    <value>false</value>
+    <description />
+  </property>
+  <property>
+    <name>ranger.kms.keysecure.UserPassword.Authentication</name>
+    <value>true</value>
+    <description />
+  </property>
+  <property>
+    <name>ranger.kms.keysecure.masterkey.name</name>
+    <value>safenetmasterkey</value>
+    <description>Safenet key secure master key name</description>
+  </property>
+  <property>
+    <name>ranger.kms.keysecure.login.username</name>
+    <value>user1</value>
+    <description>Safenet key secure username</description>
+  </property>
+  <property>
+    <name>ranger.kms.keysecure.login.password</name>
+    <value>t1e2s3t4</value>
+    <description>Safenet key secure user password</description>
+  </property>
+  <property>
+    <name>ranger.kms.keysecure.login.password.alias</name>
+    <value>ranger.ks.login.password</value>
+    <description>Safenet key secure user password</description>
+  </property>
+  <property>
+    <name>ranger.kms.keysecure.hostname</name>
+    <value>SunPKCS11-keysecurehn</value>
+    <description>Safenet key secure hostname</description>
+  </property>
+  <property>
+    <name>ranger.kms.keysecure.masterkey.size</name>
+    <value>256</value>
+    <description>key size</description>
+  </property>
+  <property>
+    <name>ranger.kms.keysecure.sunpkcs11.cfg.filepath</name>
+    <value>/opt/safenetConf/64/8.3.1/sunpkcs11.cfg</value>
+    <description>Location of Safenet key secure library configuration 
file</description>
+  </property>
+  <property>
+    <name>ranger.kms.keysecure.provider.type</name>
+    <value>SunPKCS11</value>
+    <description>Security Provider for key secure</description>
+  </property>
+  <property>
+    <name>ranger.ks.db.ssl.enabled</name>
+    <value>false</value>
+  </property>
+  <property>
+    <name>ranger.ks.db.ssl.required</name>
+    <value>false</value>
+  </property>
+  <property>
+    <name>ranger.ks.db.ssl.verifyServerCertificate</name>
+    <value>false</value>
+  </property>
+  <property>
+    <name>ranger.ks.db.ssl.auth.type</name>
+    <value>2-way</value>
+  </property>
+</configuration>
+)";
+  return strings::Substitute(kRangerKMSDbksSiteXmlTemplate, pg_host, pg_port, 
pg_driver);
+}
+
+inline std::string GetRangerKMSLog4jProperties(const std::string& log_level) {
+  // log4j.properties file
+  //
+  // This is the default log4j.properties with the only difference that 
rootLogger
+  // is made configurable if it's needed for debugging.
+  //
+  // $0: log level
+  constexpr const char* const kLog4jPropertiesTemplate = R"(
+log4j.appender.kms=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.kms.DatePattern='.'yyyy-MM-dd
+log4j.appender.kms.File=$${logdir}/ranger-kms-$${hostname}-$${user}.log
+log4j.appender.kms.Append=true
+log4j.appender.kms.layout=org.apache.log4j.PatternLayout
+log4j.appender.kms.layout.ConversionPattern=%d{ISO8601} %-5p %c{1} - %m%n
+
+log4j.appender.kms-audit=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.kms-audit.DatePattern='.'yyyy-MM-dd
+log4j.appender.kms-audit.File=$${logdir}/kms-audit-$${hostname}-$${user}.log
+log4j.appender.kms-audit.Append=true
+log4j.appender.kms-audit.layout=org.apache.log4j.PatternLayout
+log4j.appender.kms-audit.layout.ConversionPattern=%d{ISO8601} %m%n
+
+log4j.logger.kms-audit=INFO, kms-audit
+log4j.additivity.kms-audit=false
+
+log4j.logger=$0, kms
+log4j.rootLogger=WARN, kms
+log4j.logger.org.apache.hadoop.conf=INFO
+log4j.logger.org.apache.hadoop=INFO
+log4j.logger.org.apache.ranger=INFO
+log4j.logger.com.sun.jersey.server.wadl.generators.WadlGeneratorJAXBGrammarGenerator=OFF
+)";
+  return strings::Substitute(kLog4jPropertiesTemplate, log_level);
+}
+
+inline std::string GetRangerKMSSecurityXml(const std::string& ranger_url,
+                                           const std::string& kms_home) {
+  constexpr const char* const kRangerKmsSecurityXmlTemplate = R"(
+<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
+<configuration xmlns:xi="http://www.w3.org/2001/XInclude";>
+  <property>
+    <name>ranger.plugin.kms.service.name</name>
+    <value>kms</value>
+    <description>
+      Name of the Ranger service containing policies for this kms instance
+    </description>
+  </property>
+  <property>
+    <name>ranger.plugin.kms.policy.source.impl</name>
+    <value>org.apache.ranger.admin.client.RangerAdminRESTClient</value>
+    <description>
+      Class to retrieve policies from the source
+    </description>
+  </property>
+  <property>
+    <name>ranger.plugin.kms.policy.rest.url</name>
+    <value>$0</value>
+    <description>
+      URL to Ranger Admin
+    </description>
+  </property>
+  <property>
+    <name>ranger.plugin.kms.policy.rest.ssl.config.file</name>
+    <value>$1/ranger-kms-policymgr-ssl.xml</value>
+    <description>
+      Path to the file containing SSL details to contact Ranger Admin
+    </description>
+  </property>
+  <property>
+    <name>ranger.plugin.kms.policy.pollIntervalMs</name>
+    <value>30000</value>
+    <description>
+      How often to poll for changes in policies?
+    </description>
+  </property>
+  <property>
+    <name>ranger.plugin.kms.policy.cache.dir</name>
+    <value>$2/policycache</value>
+    <description>
+      Directory where Ranger policies are cached after successful retrieval 
from the source
+    </description>
+  </property>
+  <property>
+    <name>ranger.plugin.kms.policy.rest.client.connection.timeoutMs</name>
+    <value>120000</value>
+    <description>
+      RangerRestClient Connection Timeout in Milli Seconds
+    </description>
+  </property>
+  <property>
+    <name>ranger.plugin.kms.policy.rest.client.read.timeoutMs</name>
+    <value>30000</value>
+    <description>
+      RangerRestClient read Timeout in Milli Seconds
+    </description>
+  </property>
+</configuration>
+)";
+  return strings::Substitute(kRangerKmsSecurityXmlTemplate, ranger_url, 
kms_home, kms_home);
+}
+
+inline std::string GetKMSSiteXml(bool secure, const std::string& keytab) {
+  constexpr const char* const kmsSiteXml = R"(
+  <configuration>
+
+  <!-- KMS Backend KeyProvider -->
+
+  <property>
+    <name>hadoop.kms.key.provider.uri</name>
+    <value>dbks://http@localhost:9292/kms</value>
+    <description>
+      URI of the backing KeyProvider for the KMS.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.security.keystore.JavaKeyStoreProvider.password</name>
+    <value>none</value>
+    <description>
+      If using the JavaKeyStoreProvider, the password for the keystore file.
+    </description>
+  </property>
+
+  <!-- KMS Cache -->
+
+  <property>
+    <name>hadoop.kms.cache.enable</name>
+    <value>true</value>
+    <description>
+      Whether the KMS will act as a cache for the backing KeyProvider.
+      When the cache is enabled, operations like getKeyVersion, getMetadata,
+      and getCurrentKey will sometimes return cached data without consulting
+      the backing KeyProvider. Cached values are flushed when keys are deleted
+      or modified.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.cache.timeout.ms</name>
+    <value>600000</value>
+    <description>
+      Expiry time for the KMS key version and key metadata cache, in
+      milliseconds. This affects getKeyVersion and getMetadata.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.current.key.cache.timeout.ms</name>
+    <value>30000</value>
+    <description>
+      Expiry time for the KMS current key cache, in milliseconds. This
+      affects getCurrentKey operations.
+    </description>
+  </property>
+
+  <!-- KMS Audit -->
+
+  <property>
+    <name>hadoop.kms.audit.aggregation.window.ms</name>
+    <value>10000</value>
+    <description>
+      Duplicate audit log events within the aggregation window (specified in
+      ms) are quashed to reduce log traffic. A single message for aggregated
+      events is printed at the end of the window, along with a count of the
+      number of aggregated events.
+    </description>
+  </property>
+
+  <!-- KMS Security -->
+
+  <property>
+    <name>hadoop.kms.authentication.type</name>
+    <value>$0</value>
+    <description>
+      Authentication type for the KMS. Can be either &quot;simple&quot;
+      or &quot;kerberos&quot;.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.authentication.kerberos.keytab</name>
+    <value>$1</value>
+    <description>
+      Path to the keytab with credentials for the configured Kerberos 
principal.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.authentication.kerberos.principal</name>
+    <value>HTTP/localhost</value>
+    <description>
+      The Kerberos principal to use for the HTTP endpoint.
+      The principal must start with 'HTTP/' as per the Kerberos HTTP SPNEGO 
specification.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.authentication.kerberos.name.rules</name>
+    <value>DEFAULT</value>
+    <description>
+      Rules used to resolve Kerberos principal names.
+    </description>
+  </property>
+
+  <!-- Authentication cookie signature source -->
+
+  <property>
+    <name>hadoop.kms.authentication.signer.secret.provider</name>
+    <value>random</value>
+    <description>
+      Indicates how the secret to sign the authentication cookies will be
+      stored. Options are 'random' (default), 'string' and 'zookeeper'.
+      If using a setup with multiple KMS instances, 'zookeeper' should be used.
+    </description>
+  </property>
+
+  <!-- Configuration for 'zookeeper' authentication cookie signature source -->
+
+  <property>
+    
<name>hadoop.kms.authentication.signer.secret.provider.zookeeper.path</name>
+    <value>/hadoop-kms/hadoop-auth-signature-secret</value>
+    <description>
+      The Zookeeper ZNode path where the KMS instances will store and retrieve
+      the secret from.
+    </description>
+  </property>
+
+  <property>
+    
<name>hadoop.kms.authentication.signer.secret.provider.zookeeper.connection.string</name>
+    <value>#HOSTNAME#:#PORT#,...</value>
+    <description>
+      The Zookeeper connection string, a list of hostnames and port comma
+      separated.
+    </description>
+  </property>
+
+  <property>
+    
<name>hadoop.kms.authentication.signer.secret.provider.zookeeper.auth.type</name>
+    <value>kerberos</value>
+    <description>
+      The Zookeeper authentication type, 'none' or 'sasl' (Kerberos).
+    </description>
+  </property>
+
+  <property>
+    
<name>hadoop.kms.authentication.signer.secret.provider.zookeeper.kerberos.keytab</name>
+    <value>/etc/hadoop/conf/kms.keytab</value>
+    <description>
+      The absolute path for the Kerberos keytab with the credentials to
+      connect to Zookeeper.
+    </description>
+  </property>
+
+  <property>
+    
<name>hadoop.kms.authentication.signer.secret.provider.zookeeper.kerberos.principal</name>
+    <value>kms/#HOSTNAME#</value>
+    <description>
+      The Kerberos service principal used to connect to Zookeeper.
+    </description>
+  </property>
+
+  <property>
+    <name>hadoop.kms.security.authorization.manager</name>
+    
<value>org.apache.ranger.authorization.kms.authorizer.RangerKmsAuthorizer</value>
+  </property>
+
+  <property>
+    <name>hadoop.kms.proxyuser.ranger.groups</name>
+    <value>*</value>
+  </property>
+
+  <property>
+    <name>hadoop.kms.proxyuser.ranger.hosts</name>
+    <value>*</value>
+  </property>
+
+  <property>
+    <name>hadoop.kms.proxyuser.ranger.users</name>
+    <value>*</value>
+  </property>
+</configuration>
+)";
+  if (secure) {
+    return strings::Substitute(kmsSiteXml, "kerberos", keytab);
+  }
+  return strings::Substitute(kmsSiteXml, "simple", keytab);
+}
+
+inline std::string GetRangerKMSAuditXml() {
+  constexpr const char* const kRangerKMSAuditXml = R"(
+<configuration>
+  <property>
+    <name>xasecure.audit.is.enabled</name>
+    <value>false</value>
+  </property>
+  <!-- DB audit provider configuration -->
+  <property>
+    <name>xasecure.audit.db.is.enabled</name>
+    <value>false</value>
+  </property>
+  <!-- HDFS audit provider configuration -->
+  <property>
+    <name>xasecure.audit.hdfs.is.enabled</name>
+    <value>false</value>
+  </property>
+  <property>
+    <name>xasecure.audit.destination.hdfs</name>
+    <value>disabled</value>
+  </property>
+  <property>
+    <name>xasecure.audit.log4j.is.enabled</name>
+    <value>false</value>
+  </property>
+  <property>
+    <name>xasecure.audit.kafka.is.enabled</name>
+    <value>false</value>
+  </property>
+  <property>
+    <name>xasecure.audit.provider.summary.enabled</name>
+    <value>false</value>
+  </property>
+  <property>
+    <name>xasecure.audit.destination.solr</name>
+    <value>false</value>
+  </property>
+  <property>
+    <name>xasecure.audit.destination.hdfs</name>
+    <value>false</value>
+  </property>
+</configuration>
+)";
+  return kRangerKMSAuditXml;
+}
+
+inline std::string GetRangerKMSPolicymgrSSLXml() {
+  constexpr const char* const kRangerKMSPolicymgrSSLXml = R"(
+<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
+<configuration xmlns:xi="http://www.w3.org/2001/XInclude";>
+  <!--  The following properties are used for 2-way SSL client server 
validation -->
+  <property>
+    <name>xasecure.policymgr.clientssl.keystore</name>
+    <value>/etc/ranger/kms/conf/ranger-plugin-keystore.jks</value>
+    <description>
+      Java Keystore files
+    </description>
+  </property>
+  <property>
+    <name>xasecure.policymgr.clientssl.truststore</name>
+    <value>/etc/ranger/kms/conf/ranger-plugin-truststore.jks</value>
+    <description>
+      java truststore file
+    </description>
+  </property>
+    <property>
+    <name>xasecure.policymgr.clientssl.keystore.credential.file</name>
+    <value>jceks://file/etc/ranger/kmsdev/cred.jceks</value>
+    <description>
+      java  keystore credential file
+    </description>
+  </property>
+  <property>
+    <name>xasecure.policymgr.clientssl.truststore.credential.file</name>
+    <value>jceks://file/etc/ranger/kmsdev/cred.jceks</value>
+    <description>
+      java  truststore credential file
+    </description>
+  </property>
+</configuration>
+)";
+  return kRangerKMSPolicymgrSSLXml;
+}
+
+
+} // namespace rangerkms
+} // namespace kudu
diff --git a/src/kudu/ranger/mini_ranger-test.cc 
b/src/kudu/ranger/mini_ranger-test.cc
index da5b86cd7..8af214eb4 100644
--- a/src/kudu/ranger/mini_ranger-test.cc
+++ b/src/kudu/ranger/mini_ranger-test.cc
@@ -17,19 +17,23 @@
 
 #include "kudu/ranger/mini_ranger.h"
 
+#include <memory>
 #include <string>
 #include <utility>
 #include <vector>
 
 #include <gtest/gtest.h>
 
+#include "kudu/postgres/mini_postgres.h"
 #include "kudu/ranger/ranger.pb.h"
 #include "kudu/util/curl_util.h"
-#include "kudu/util/path_util.h"
 #include "kudu/util/faststring.h"
+#include "kudu/util/path_util.h"
 #include "kudu/util/test_macros.h"
 #include "kudu/util/test_util.h"
 
+using kudu::postgres::MiniPostgres;
+
 using std::string;
 
 namespace kudu {
@@ -37,8 +41,8 @@ namespace ranger {
 
 class MiniRangerTest : public KuduTest {
  public:
-  MiniRangerTest()
-    : ranger_("127.0.0.1") {}
+  MiniRangerTest() :
+    ranger_("127.0.0.1", std::make_shared<MiniPostgres>("127.0.0.1")) {}
   void SetUp() override {
     ASSERT_OK(ranger_.Start());
   }
diff --git a/src/kudu/ranger/mini_ranger.cc b/src/kudu/ranger/mini_ranger.cc
index e6f6f11f3..a342e29a4 100644
--- a/src/kudu/ranger/mini_ranger.cc
+++ b/src/kudu/ranger/mini_ranger.cc
@@ -52,7 +52,9 @@ namespace kudu {
 namespace ranger {
 
 Status MiniRanger::Start() {
-  RETURN_NOT_OK_PREPEND(mini_pg_.Start(), "Failed to start Postgres");
+  if (!mini_pg_->IsStarted()) {
+    RETURN_NOT_OK_PREPEND(mini_pg_->Start(), "Failed to start Postgres");
+  }
   return StartRanger();
 }
 
@@ -67,10 +69,10 @@ Status MiniRanger::Stop() {
     LOG(INFO) << "Stopped Ranger";
     process_.reset();
   }
-  return mini_pg_.Stop();
+  return mini_pg_->Stop();
 }
 
-Status MiniRanger::InitRanger(string admin_home, bool* fresh_install) {
+Status MiniRanger::InitRanger(const string& admin_home, bool* fresh_install) {
   if (env_->FileExists(admin_home)) {
     *fresh_install = false;
     return Status::OK();
@@ -79,10 +81,10 @@ Status MiniRanger::InitRanger(string admin_home, bool* 
fresh_install) {
 
   RETURN_NOT_OK(env_->CreateDir(admin_home));
 
-  RETURN_NOT_OK(mini_pg_.AddUser("miniranger", /*super=*/ false));
+  RETURN_NOT_OK(mini_pg_->AddUser("miniranger", /*super=*/ false));
   LOG(INFO) << "Created miniranger Postgres user";
 
-  RETURN_NOT_OK(mini_pg_.CreateDb("ranger", "miniranger"));
+  RETURN_NOT_OK(mini_pg_->CreateDb("ranger", "miniranger"));
   LOG(INFO) << "Created ranger Postgres database";
 
   return Status::OK();
@@ -108,11 +110,11 @@ Status MiniRanger::CreateConfigs() {
 
   // Write config files
   RETURN_NOT_OK(WriteStringToFile(
-      env_, GetRangerInstallProperties(bin_dir(), host_, 
mini_pg_.bound_port()),
+      env_, GetRangerInstallProperties(bin_dir(), host_, 
mini_pg_->bound_port()),
       JoinPathSegments(admin_home, "install.properties")));
 
   RETURN_NOT_OK(WriteStringToFile(
-      env_, GetRangerAdminSiteXml(host_, port_, host_, mini_pg_.bound_port(),
+      env_, GetRangerAdminSiteXml(host_, port_, host_, mini_pg_->bound_port(),
                                   admin_ktpath_, lookup_ktpath_,
                                   spnego_ktpath_),
       JoinPathSegments(admin_home, "ranger-admin-site.xml")));
@@ -160,6 +162,10 @@ Status MiniRanger::DbSetup(const string& admin_home, const 
string& ews_dir,
 
 Status MiniRanger::StartRanger() {
   bool fresh_install;
+  if (!mini_pg_->IsStarted()) {
+    return Status::IllegalState("Postgres is not running");
+  }
+
   LOG_TIMING(INFO, "starting Ranger") {
     LOG(INFO) << "Starting Ranger...";
     string exe;
diff --git a/src/kudu/ranger/mini_ranger.h b/src/kudu/ranger/mini_ranger.h
index 185472edd..056335017 100644
--- a/src/kudu/ranger/mini_ranger.h
+++ b/src/kudu/ranger/mini_ranger.h
@@ -29,17 +29,19 @@
 
 #include "kudu/gutil/port.h"
 #include "kudu/gutil/strings/substitute.h"
-#include "kudu/postgres/mini_postgres.h"
 #include "kudu/ranger/ranger.pb.h"
 #include "kudu/util/curl_util.h"
 #include "kudu/util/env.h"
 #include "kudu/util/path_util.h"
 #include "kudu/util/status.h"
+#include "kudu/util/subprocess.h"
 #include "kudu/util/test_util.h"
 
 namespace kudu {
 class EasyJson;
-class Subprocess;
+namespace postgres {
+class MiniPostgres;
+}  // namespace postgres
 
 namespace ranger {
 
@@ -70,15 +72,17 @@ struct AuthorizationPolicy {
 // Wrapper around Apache Ranger to be used in integration tests.
 class MiniRanger {
  public:
-  explicit MiniRanger(std::string host)
-    : MiniRanger(GetTestDataDirectory(), std::move(host)) {}
+  explicit MiniRanger(std::string host, 
std::shared_ptr<postgres::MiniPostgres> mini_pg) :
+  MiniRanger(GetTestDataDirectory(), std::move(host), std::move(mini_pg)) {}
 
   ~MiniRanger();
 
-  MiniRanger(std::string data_root, std::string host)
+  MiniRanger(std::string data_root,
+             std::string host,
+             std::shared_ptr<postgres::MiniPostgres> mini_pg)
     : data_root_(std::move(data_root)),
       host_(std::move(host)),
-      mini_pg_(data_root_, host_),
+      mini_pg_(std::move(mini_pg)),
       kerberos_(false),
       env_(Env::Default()) {
         curl_.set_auth(CurlAuthType::BASIC, "admin", "admin");
@@ -115,14 +119,19 @@ class MiniRanger {
     return ranger_admin_url_;
   }
 
- private:
+  bool IsRunning() const { return process_ && process_->IsStarted(); }
+
+  // Sends a POST request to Ranger with 'payload'.
+  Status PostToRanger(std::string url, EasyJson payload) WARN_UNUSED_RESULT;
+
+private:
   // Starts the Ranger service.
   Status StartRanger() WARN_UNUSED_RESULT;
 
   // Initializes Ranger within 'admin_home' (home directory of the Ranger
   // admin). Sets 'fresh_install' to true if 'admin_home' didn't exist before
   // calling InitRanger().
-  Status InitRanger(std::string admin_home, bool* fresh_install)
+  Status InitRanger(const std::string& admin_home, bool* fresh_install)
     WARN_UNUSED_RESULT;
 
   // Creates configuration files.
@@ -135,9 +144,6 @@ class MiniRanger {
   // Creates a Kudu service in Ranger.
   Status CreateKuduService() WARN_UNUSED_RESULT;
 
-  // Sends a POST request to Ranger with 'payload'.
-  Status PostToRanger(std::string url, EasyJson payload) WARN_UNUSED_RESULT;
-
   // Returns Ranger admin's home directory.
   std::string ranger_admin_home() const {
     return JoinPathSegments(data_root_, "ranger-admin");
@@ -162,8 +168,7 @@ class MiniRanger {
   // Directory in which to put all our stuff.
   const std::string data_root_;
   const std::string host_;
-
-  postgres::MiniPostgres mini_pg_;
+  std::shared_ptr<kudu::postgres::MiniPostgres> mini_pg_;
   std::unique_ptr<Subprocess> process_;
 
   // URL of the Ranger admin REST API.
diff --git a/src/kudu/ranger/ranger_client-test.cc 
b/src/kudu/ranger/ranger_client-test.cc
index b3f1c9a31..486ab820d 100644
--- a/src/kudu/ranger/ranger_client-test.cc
+++ b/src/kudu/ranger/ranger_client-test.cc
@@ -25,7 +25,7 @@
 #include <utility>
 #include <vector>
 
-#include <boost/functional/hash/hash.hpp>
+#include <boost/container_hash/extensions.hpp>
 #include <gflags/gflags_declare.h>
 #include <glog/logging.h>
 #include <google/protobuf/any.pb.h>
@@ -36,6 +36,7 @@
 #include "kudu/gutil/strings/join.h"
 #include "kudu/gutil/strings/split.h"
 #include "kudu/gutil/strings/substitute.h"
+#include "kudu/postgres/mini_postgres.h"
 #include "kudu/ranger/mini_ranger.h"
 #include "kudu/ranger/ranger.pb.h"
 #include "kudu/subprocess/server.h"
@@ -70,6 +71,7 @@ using std::vector;
 using strings::SkipEmpty;
 using strings::Split;
 using strings::Substitute;
+using kudu::postgres::MiniPostgres;
 
 namespace kudu {
 namespace ranger {
@@ -353,7 +355,7 @@ class RangerClientTestBase : public KuduTest {
   }
 
   Status InitializeRanger() {
-    ranger_.reset(new MiniRanger("127.0.0.1"));
+    ranger_.reset(new MiniRanger("127.0.0.1", 
std::make_shared<MiniPostgres>("127.0.0.1")));
     RETURN_NOT_OK(ranger_->Start());
     // Create a policy so the Ranger client policy refresher can pick something
     // up. In some environments the absense of policies can cause the plugin to
diff --git a/src/kudu/util/env_util.cc b/src/kudu/util/env_util.cc
index 532277f6f..7dcafe2bb 100644
--- a/src/kudu/util/env_util.cc
+++ b/src/kudu/util/env_util.cc
@@ -248,22 +248,51 @@ Status CopyFile(Env* env, const string& source_path, 
const string& dest_path,
 
   unique_ptr<WritableFile> dest;
   RETURN_NOT_OK(env->NewWritableFile(opts, dest_path, &dest));
-  RETURN_NOT_OK(dest->PreAllocate(size));
-
-  const int32_t kBufferSize = 1024 * 1024;
-  unique_ptr<uint8_t[]> scratch(new uint8_t[kBufferSize]);
-
-  uint64_t bytes_read = 0;
-  while (bytes_read < size) {
-    uint64_t max_bytes_to_read = std::min<uint64_t>(size - bytes_read, 
kBufferSize);
-    Slice data(scratch.get(), max_bytes_to_read);
-    RETURN_NOT_OK(source->Read(&data));
-    RETURN_NOT_OK(dest->Append(data));
-    bytes_read += data.size();
+  if (size > 0) {
+    RETURN_NOT_OK(dest->PreAllocate(size));
+
+    const int32_t kBufferSize = 1024 * 1024;
+    unique_ptr<uint8_t[]> scratch(new uint8_t[kBufferSize]);
+
+    uint64_t bytes_read = 0;
+    while (bytes_read < size) {
+      uint64_t max_bytes_to_read = std::min<uint64_t>(size - bytes_read, 
kBufferSize);
+      Slice data(scratch.get(), max_bytes_to_read);
+      RETURN_NOT_OK(source->Read(&data));
+      RETURN_NOT_OK(dest->Append(data));
+      bytes_read += data.size();
+    }
   }
   return Status::OK();
 }
 
+Status CopyDirectory(Env* env, const std::string& source_path, const 
std::string& dest_path,
+                     WritableFileOptions opts) {
+  bool is_dir;
+  RETURN_NOT_OK(env->IsDirectory(source_path, &is_dir));
+
+  if (!is_dir) {
+    return Status::InvalidArgument("source is not a directory");
+  }
+
+  vector<string> children;
+  RETURN_NOT_OK(ListFilesInDir(env, source_path, &children));
+
+  RETURN_NOT_OK(env->CreateDir(dest_path));
+  for (const auto& c : children) {
+    string source = JoinPathSegments(source_path, c);
+    string dest = JoinPathSegments(dest_path, c);
+    RETURN_NOT_OK(env->IsDirectory(source, &is_dir));
+    if (is_dir) {
+      RETURN_NOT_OK(CopyDirectory(env, source, dest, opts));
+    } else {
+      RETURN_NOT_OK(CopyFile(env, source, dest, opts));
+    }
+  }
+
+  return Status::OK();
+}
+
 Status DeleteExcessFilesByPattern(Env* env, const string& pattern, int 
max_matches) {
   // Negative numbers don't make sense for our interface.
   DCHECK_GE(max_matches, 0);
diff --git a/src/kudu/util/env_util.h b/src/kudu/util/env_util.h
index 39c32015a..35dc6c694 100644
--- a/src/kudu/util/env_util.h
+++ b/src/kudu/util/env_util.h
@@ -82,6 +82,14 @@ Status CreateDirsRecursively(Env* env, const std::string& 
path);
 Status CopyFile(Env* env, const std::string& source_path, const std::string& 
dest_path,
                 WritableFileOptions opts);
 
+// Copy the contents of a directory from source_path to dest_path.
+// This is not atomic, if there is an error while reading or writing,
+// a partial copy of the directory may be left in dest_path.
+// The contents of the directory will be copied recursively. Does not fsync 
the parent
+// directory of dest_path -- if you need durability then do that yourself.
+Status CopyDirectory(Env* env, const std::string& source_path, const 
std::string& dest_path,
+                     WritableFileOptions opts);
+
 // Deletes files matching 'pattern' in excess of 'max_matches' files.
 // 'max_matches' must be greater than or equal to 0.
 // The oldest files are deleted first, as determined by last modified time.

Reply via email to