KUDU-2541: add Kerberos support to Sentry client and mini-cluster More infrastructure work towards KUDU-428.
Change-Id: I3f7e27137135cb9b463a90b98e4aba864cece3c1 Reviewed-on: http://gerrit.cloudera.org:8080/11525 Reviewed-by: Hao Hao <hao....@cloudera.com> Reviewed-by: Adar Dembo <a...@cloudera.com> Tested-by: Kudu Jenkins Project: http://git-wip-us.apache.org/repos/asf/kudu/repo Commit: http://git-wip-us.apache.org/repos/asf/kudu/commit/ecc4998c Tree: http://git-wip-us.apache.org/repos/asf/kudu/tree/ecc4998c Diff: http://git-wip-us.apache.org/repos/asf/kudu/diff/ecc4998c Branch: refs/heads/master Commit: ecc4998cb921b97d919ce6acef2b0f623a75d653 Parents: 14f3e6f Author: Dan Burkert <danburk...@apache.org> Authored: Mon Sep 24 15:47:39 2018 -0700 Committer: Dan Burkert <danburk...@apache.org> Committed: Thu Sep 27 00:34:32 2018 +0000 ---------------------------------------------------------------------- src/kudu/sentry/CMakeLists.txt | 1 + src/kudu/sentry/mini_sentry.cc | 53 +++++++++++++++++++++++++++--- src/kudu/sentry/mini_sentry.h | 10 ++++++ src/kudu/sentry/sentry_client-test.cc | 41 +++++++++++++++++++---- 4 files changed, 95 insertions(+), 10 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/kudu/blob/ecc4998c/src/kudu/sentry/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/src/kudu/sentry/CMakeLists.txt b/src/kudu/sentry/CMakeLists.txt index 910117c..588bf9f 100644 --- a/src/kudu/sentry/CMakeLists.txt +++ b/src/kudu/sentry/CMakeLists.txt @@ -72,6 +72,7 @@ target_link_libraries(mini_sentry if (NOT NO_TESTS) SET_KUDU_TEST_LINK_LIBS( kudu_sentry + mini_kdc mini_sentry) ADD_KUDU_TEST(sentry_client-test) http://git-wip-us.apache.org/repos/asf/kudu/blob/ecc4998c/src/kudu/sentry/mini_sentry.cc ---------------------------------------------------------------------- diff --git a/src/kudu/sentry/mini_sentry.cc b/src/kudu/sentry/mini_sentry.cc index ff54ad8..f50aa5b 100644 --- a/src/kudu/sentry/mini_sentry.cc +++ b/src/kudu/sentry/mini_sentry.cc @@ -53,6 +53,18 @@ MiniSentry::~MiniSentry() { WARN_NOT_OK(Stop(), "Failed to stop MiniSentry"); } +void MiniSentry::EnableKerberos(std::string krb5_conf, + std::string service_principal, + std::string keytab_file) { + CHECK(!sentry_process_); + CHECK(!krb5_conf.empty()); + CHECK(!service_principal.empty()); + CHECK(!keytab_file.empty()); + krb5_conf_ = std::move(krb5_conf); + service_principal_ = std::move(service_principal); + keytab_file_ = std::move(keytab_file); +} + Status MiniSentry::Start() { SCOPED_LOG_SLOW_EXECUTION(WARNING, kSentryStartTimeoutMs / 2, "Starting Sentry"); CHECK(!sentry_process_); @@ -76,9 +88,16 @@ Status MiniSentry::Start() { RETURN_NOT_OK(CreateSentryConfigs(tmp_dir)); + // List of JVM environment options to pass to the Sentry service. + string java_options; + if (!krb5_conf_.empty()) { + java_options += Substitute(" -Djava.security.krb5.conf=$0", krb5_conf_); + } + map<string, string> env_vars { { "JAVA_HOME", java_home }, { "HADOOP_HOME", hadoop_home }, + { "JAVA_TOOL_OPTIONS", java_options }, }; // Start Sentry. @@ -145,17 +164,31 @@ Status MiniSentry::CreateSentryConfigs(const string& tmp_dir) const { // // - sentry.service.admin.group // Set up admin groups which have unrestricted privileges in Sentry. + // + // - sentry.service.allow.connect + // Set of Kerberos principals which is allowed to connect to Sentry when + // the Kerberos security mode is enabled. static const string kFileTemplate = R"( <configuration> <property> <name>sentry.service.security.mode</name> - <value>none</value> + <value>$0</value> + </property> + + <property> + <name>sentry.service.server.principal</name> + <value>$1</value> + </property> + + <property> + <name>sentry.service.server.keytab</name> + <value>$2</value> </property> <property> <name>sentry.store.jdbc.url</name> - <value>jdbc:derby:$0/sentry;create=true</value> + <value>jdbc:derby:$3/sentry;create=true</value> </property> <property> @@ -180,18 +213,30 @@ Status MiniSentry::CreateSentryConfigs(const string& tmp_dir) const { <property> <name>sentry.store.group.mapping.resource</name> - <value>$1</value> + <value>$4</value> </property> <property> <name>sentry.service.admin.group</name> <value>admin</value> </property> + + <property> + <name>sentry.service.allow.connect</name> + <value>kudu</value> + </property> + </configuration> )"; string users_ini_path = JoinPathSegments(tmp_dir, "users.ini"); - string file_contents = Substitute(kFileTemplate, tmp_dir, users_ini_path); + string file_contents = Substitute( + kFileTemplate, + keytab_file_.empty() ? "none" : "kerberos", + service_principal_, + keytab_file_, + tmp_dir, + users_ini_path); RETURN_NOT_OK(WriteStringToFile(Env::Default(), file_contents, JoinPathSegments(tmp_dir, "sentry-site.xml"))); http://git-wip-us.apache.org/repos/asf/kudu/blob/ecc4998c/src/kudu/sentry/mini_sentry.h ---------------------------------------------------------------------- diff --git a/src/kudu/sentry/mini_sentry.h b/src/kudu/sentry/mini_sentry.h index bcf46aa..708ed6b 100644 --- a/src/kudu/sentry/mini_sentry.h +++ b/src/kudu/sentry/mini_sentry.h @@ -38,6 +38,11 @@ class MiniSentry { ~MiniSentry(); + // Configures the mini Sentry service to use Kerberos. + void EnableKerberos(std::string krb5_conf, + std::string service_principal, + std::string keytab_file); + // Starts the mini Sentry service. // // If the MiniSentry has already been started and stopped, it will be restarted @@ -70,6 +75,11 @@ class MiniSentry { std::unique_ptr<Subprocess> sentry_process_; uint16_t port_ = 0; + + // Kerberos configuration + std::string krb5_conf_; + std::string service_principal_; + std::string keytab_file_; }; } // namespace sentry http://git-wip-us.apache.org/repos/asf/kudu/blob/ecc4998c/src/kudu/sentry/sentry_client-test.cc ---------------------------------------------------------------------- diff --git a/src/kudu/sentry/sentry_client-test.cc b/src/kudu/sentry/sentry_client-test.cc index 7e3815b..d738a4b 100644 --- a/src/kudu/sentry/sentry_client-test.cc +++ b/src/kudu/sentry/sentry_client-test.cc @@ -17,10 +17,13 @@ #include "kudu/sentry/sentry_client.h" +#include <map> #include <string> #include <gtest/gtest.h> +#include "kudu/rpc/sasl_common.h" +#include "kudu/security/test/mini_kdc.h" #include "kudu/sentry/mini_sentry.h" #include "kudu/sentry/sentry_policy_service_types.h" #include "kudu/thrift/client.h" @@ -28,14 +31,21 @@ #include "kudu/util/test_macros.h" #include "kudu/util/test_util.h" +using std::string; + namespace kudu { namespace sentry { -class SentryClientTest : public KuduTest { +class SentryClientTest : public KuduTest, + public ::testing::WithParamInterface<bool> { public: + bool KerberosEnabled() const { + return GetParam(); + } }; +INSTANTIATE_TEST_CASE_P(KerberosEnabled, SentryClientTest, ::testing::Bool()); -TEST_F(SentryClientTest, TestMiniSentryLifecycle) { +TEST_P(SentryClientTest, TestMiniSentryLifecycle) { MiniSentry mini_sentry; ASSERT_OK(mini_sentry.Start()); @@ -50,11 +60,30 @@ TEST_F(SentryClientTest, TestMiniSentryLifecycle) { // test of Sentry's role handling, but instead verification that the client can // communicate with the Sentry service, and errors are converted to Status // instances. -TEST_F(SentryClientTest, TestCreateDropRole) { - MiniSentry mini_sentry; - ASSERT_OK(mini_sentry.Start()); +TEST_P(SentryClientTest, TestCreateDropRole) { + MiniKdc kdc; + MiniSentry sentry; + thrift::ClientOptions sentry_client_opts; + + if (KerberosEnabled()) { + ASSERT_OK(kdc.Start()); + + string spn = "sentry/127.0....@krbtest.com"; + string ktpath; + ASSERT_OK(kdc.CreateServiceKeytab("sentry/127.0.0.1", &ktpath)); + + ASSERT_OK(rpc::SaslInit()); + sentry.EnableKerberos(kdc.GetEnvVars()["KRB5_CONFIG"], spn, ktpath); + + ASSERT_OK(kdc.CreateUserPrincipal("kudu")); + ASSERT_OK(kdc.Kinit("kudu")); + ASSERT_OK(kdc.SetKrb5Environment()); + sentry_client_opts.enable_kerberos = true; + sentry_client_opts.service_principal = "sentry"; + } + ASSERT_OK(sentry.Start()); - SentryClient client(mini_sentry.address(), thrift::ClientOptions()); + SentryClient client(sentry.address(), sentry_client_opts); ASSERT_OK(client.Start()); { // Create a role