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 "simple"
+ or "kerberos".
+ </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.