This is an automated email from the ASF dual-hosted git repository. abukor pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/kudu.git
commit cdbe4577a91a171718ff485acc1dd1261e73a8d2 Author: halim.kim <[email protected]> AuthorDate: Fri Jul 28 16:12:33 2023 +0900 KUDU-3496 support spnego dedicated keytab Add a --spnego_keytab_file flag for seperation of service keytab file and spnego keytab file. If --webserver_require_spnego flag is true and --spnego_keytab_file is not empty but specifies a keytab location, web console gss acceptor registers specified keytab location so that web server is able to find spnego principal from spnego_keytab_file. if --spnego_keytab_file is empty even --webserver_require_spnego=true, web server will use --keytab_file flag as it is. Change-Id: I01646207954854d67308f99e6b71ba32c019ed6a Reviewed-on: http://gerrit.cloudera.org:8080/20278 Tested-by: Kudu Jenkins Reviewed-by: Attila Bukor <[email protected]> --- src/kudu/security/init.cc | 5 +++++ src/kudu/security/test/mini_kdc.cc | 16 ++++++++++++++++ src/kudu/security/test/mini_kdc.h | 6 ++++++ src/kudu/server/webserver-test.cc | 37 +++++++++++++++++++++++++++++++++++++ src/kudu/server/webserver.cc | 14 ++++++++++---- 5 files changed, 74 insertions(+), 4 deletions(-) diff --git a/src/kudu/security/init.cc b/src/kudu/security/init.cc index dfadb4e85..b4cd394d7 100644 --- a/src/kudu/security/init.cc +++ b/src/kudu/security/init.cc @@ -85,6 +85,11 @@ DEFINE_string(keytab_file, "", "to be used to authenticate RPC connections."); TAG_FLAG(keytab_file, stable); +DEFINE_string(spnego_keytab_file, "", + "Absolute path to Kerberos keytab file " + "for HTTP SPNEGO. If it is empty, --keytab_file flag will be used."); +TAG_FLAG(spnego_keytab_file, advanced); + using std::mt19937; using std::nullopt; using std::optional; diff --git a/src/kudu/security/test/mini_kdc.cc b/src/kudu/security/test/mini_kdc.cc index a9cbe5ed4..8b07a992e 100644 --- a/src/kudu/security/test/mini_kdc.cc +++ b/src/kudu/security/test/mini_kdc.cc @@ -273,6 +273,22 @@ Status MiniKdc::CreateServiceKeytab(const string& spn, return Status::OK(); } +Status MiniKdc::CreateServiceKeytabWithName(const string& spn, const string& name, + string* path) { + SCOPED_LOG_SLOW_EXECUTION(WARNING, 100, Substitute("creating service keytab for $0", spn)); + string kt_path = name; + kt_path = JoinPathSegments(options_.data_root, kt_path) + ".keytab"; + + string kadmin; + RETURN_NOT_OK(GetBinaryPath("kadmin.local", &kadmin)); + RETURN_NOT_OK(Subprocess::Call(MakeArgv({ + kadmin, "-q", Substitute("add_principal -randkey $0", spn)}))); + RETURN_NOT_OK(Subprocess::Call(MakeArgv({ + kadmin, "-q", Substitute("ktadd -k $0 $1", kt_path, spn)}))); + *path = kt_path; + return Status::OK(); +} + Status MiniKdc::RandomizePrincipalKey(const string& spn) { SCOPED_LOG_SLOW_EXECUTION(WARNING, 100, Substitute("randomizing key for $0", spn)); string kadmin; diff --git a/src/kudu/security/test/mini_kdc.h b/src/kudu/security/test/mini_kdc.h index ecbabe082..f55d3040a 100644 --- a/src/kudu/security/test/mini_kdc.h +++ b/src/kudu/security/test/mini_kdc.h @@ -92,6 +92,12 @@ class MiniKdc { // will be reset and a new keytab will be generated. Status CreateServiceKeytab(const std::string& spn, std::string* path); + // Creates a new service principal and associated keytab, specifying its + // keytab file name with 'name'. if "test" is assigned, keytab file name + // will be "test.keytab". + Status CreateServiceKeytabWithName(const std::string& spn, const std::string& name, + std::string* path); + // Randomize the key for the given SPN. This invalidates any previously-produced // keytabs. Status RandomizePrincipalKey(const std::string& spn); diff --git a/src/kudu/server/webserver-test.cc b/src/kudu/server/webserver-test.cc index ded5d4ee9..07688ab63 100644 --- a/src/kudu/server/webserver-test.cc +++ b/src/kudu/server/webserver-test.cc @@ -75,6 +75,8 @@ TAG_FLAG(test_sensitive_flag, sensitive); DECLARE_bool(webserver_enable_csp); +DECLARE_string(spnego_keytab_file); + #if OPENSSL_VERSION_NUMBER < 0x30000000L int fips_mode = FIPS_mode(); #else @@ -249,6 +251,39 @@ class SpnegoWebserverTest : public WebserverTest { }; +class SpnegoDedicatedKeytabWebserverTest : public SpnegoWebserverTest { + protected: + void MaybeSetupSpnego(WebserverOptions* opts) override { + kdc_.reset(new MiniKdc(MiniKdcOptions{})); + ASSERT_OK(kdc_->Start()); + kdc_->SetKrb5Environment(); + string kt_path; + ASSERT_OK(kdc_->CreateServiceKeytabWithName("HTTP/127.0.0.1", + "spnego.dedicated", + &kt_path)); + ASSERT_OK(kdc_->CreateUserPrincipal("alice")); + FLAGS_spnego_keytab_file = kt_path; + opts->require_spnego = true; + opts->spnego_post_authn_callback = [&](const string& spn) { + last_authenticated_spn_ = spn; + }; + } + +}; + +TEST_F(SpnegoDedicatedKeytabWebserverTest, TestAuthenticated) { + ASSERT_OK(kdc_->Kinit("alice")); + ASSERT_OK(DoSpnegoCurl()); + EXPECT_EQ("[email protected]", last_authenticated_spn_); + EXPECT_STR_CONTAINS(buf_.ToString(), "Kudu"); +} + +// Tests that execute DoSpnegoCurl() are ignored in MacOS (except the first test case) +// MacOS heimdal kerberos caches kdc port number somewhere so that all the test cases +// executing DoSpnegoCurl() use same kdc port number and it is test defect. +// Please refer to KUDU-3533(https://issues.apache.org/jira/browse/KUDU-3533) +#ifndef __APPLE__ + TEST_F(SpnegoWebserverTest, TestAuthenticated) { ASSERT_OK(kdc_->Kinit("alice")); ASSERT_OK(DoSpnegoCurl()); @@ -290,6 +325,8 @@ TEST_F(SpnegoWebserverTest, TestUnauthenticatedNoClientAuth) { EXPECT_EQ(kNotAuthn, last_authenticated_spn_); } +#endif + // Test some malformed authorization headers. TEST_F(SpnegoWebserverTest, TestInvalidHeaders) { EXPECT_EQ(curl_.FetchURL(url_, &buf_, { "Authorization: blahblah" }).ToString(), diff --git a/src/kudu/server/webserver.cc b/src/kudu/server/webserver.cc index 07dd1f45e..b920f2b95 100644 --- a/src/kudu/server/webserver.cc +++ b/src/kudu/server/webserver.cc @@ -44,6 +44,7 @@ #include <glog/logging.h> #include <mustache.h> #include <squeasel.h> +#include <gssapi/gssapi_krb5.h> #include "kudu/gutil/endian.h" #include "kudu/gutil/macros.h" @@ -135,6 +136,7 @@ DEFINE_string(webserver_x_content_type_options, "nosniff", TAG_FLAG(webserver_x_content_type_options, advanced); TAG_FLAG(webserver_x_content_type_options, runtime); +DECLARE_string(spnego_keytab_file); namespace kudu { @@ -352,14 +354,18 @@ Status Webserver::Start() { } if (opts_.require_spnego) { - // We assume that security::InitKerberosForServer() has already been called, which - // ensures that the keytab path has been propagated into this environment variable - // where the GSSAPI calls will pick it up. - const char* kt_file = getenv("KRB5_KTNAME"); + // If the spnego_keytab_file flag is empty, GSSAPI will find the keytab path from + // KRB5_KTNAME environment variable which is set by InitKerberosForServer(). + // Setting spnego_keytab_file flag will make GSSAPI to use spnego dedicated keytab + // instead of KRB5_KTNAME. + const char* kt_file = FLAGS_spnego_keytab_file.empty() ? + getenv("KRB5_KTNAME") : + FLAGS_spnego_keytab_file.c_str(); if (!kt_file || !Env::Default()->FileExists(kt_file)) { return Status::InvalidArgument("Unable to configure web server for SPNEGO authentication: " "must configure a keytab file for the server"); } + krb5_gss_register_acceptor_identity(kt_file); } options.emplace_back("listening_ports");
