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");

Reply via email to