kudu-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jdcry...@apache.org
Subject [2/5] kudu git commit: [security] moved mini_kdc into the test subdir
Date Mon, 07 Nov 2016 20:25:05 GMT
[security] moved mini_kdc into the test subdir

Moved mini_kdc code into the test sub-directory.
The idea of moving is inspired by Todd's comment on
  https://gerrit.cloudera.org/#/c/4926/

There are no functional changes in this patch.

Change-Id: I5f678f59e1911fd89191bd9b5dfb840e20d746a1
Reviewed-on: http://gerrit.cloudera.org:8080/4965
Tested-by: Kudu Jenkins
Reviewed-by: Will Berkeley <wdberkeley@gmail.com>


Project: http://git-wip-us.apache.org/repos/asf/kudu/repo
Commit: http://git-wip-us.apache.org/repos/asf/kudu/commit/c25aea49
Tree: http://git-wip-us.apache.org/repos/asf/kudu/tree/c25aea49
Diff: http://git-wip-us.apache.org/repos/asf/kudu/diff/c25aea49

Branch: refs/heads/master
Commit: c25aea49f2eca58b01c6b5b95bc6830b5c784a44
Parents: 81f645c
Author: Alexey Serbin <aserbin@cloudera.com>
Authored: Fri Nov 4 23:09:04 2016 -0700
Committer: Will Berkeley <wdberkeley@gmail.com>
Committed: Sat Nov 5 14:30:15 2016 +0000

----------------------------------------------------------------------
 .../external_mini_cluster-test.cc               |   2 +-
 .../integration-tests/external_mini_cluster.cc  |   2 +-
 src/kudu/rpc/sasl_rpc-test.cc                   |   2 +-
 src/kudu/security/CMakeLists.txt                |   4 +-
 src/kudu/security/mini_kdc-test.cc              |  69 ----
 src/kudu/security/mini_kdc.cc                   | 363 -------------------
 src/kudu/security/mini_kdc.h                    | 127 -------
 src/kudu/security/test/mini_kdc-test.cc         |  69 ++++
 src/kudu/security/test/mini_kdc.cc              | 363 +++++++++++++++++++
 src/kudu/security/test/mini_kdc.h               | 127 +++++++
 10 files changed, 564 insertions(+), 564 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kudu/blob/c25aea49/src/kudu/integration-tests/external_mini_cluster-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/integration-tests/external_mini_cluster-test.cc b/src/kudu/integration-tests/external_mini_cluster-test.cc
index fbc63ca..f316754 100644
--- a/src/kudu/integration-tests/external_mini_cluster-test.cc
+++ b/src/kudu/integration-tests/external_mini_cluster-test.cc
@@ -27,7 +27,7 @@
 #include "kudu/gutil/stl_util.h"
 #include "kudu/gutil/strings/substitute.h"
 #include "kudu/gutil/strings/util.h"
-#include "kudu/security/mini_kdc.h"
+#include "kudu/security/test/mini_kdc.h"
 #include "kudu/util/metrics.h"
 #include "kudu/util/net/net_util.h"
 #include "kudu/util/test_util.h"

http://git-wip-us.apache.org/repos/asf/kudu/blob/c25aea49/src/kudu/integration-tests/external_mini_cluster.cc
----------------------------------------------------------------------
diff --git a/src/kudu/integration-tests/external_mini_cluster.cc b/src/kudu/integration-tests/external_mini_cluster.cc
index 4e4420a..621c059 100644
--- a/src/kudu/integration-tests/external_mini_cluster.cc
+++ b/src/kudu/integration-tests/external_mini_cluster.cc
@@ -32,7 +32,7 @@
 #include "kudu/gutil/strings/util.h"
 #include "kudu/master/master.proxy.h"
 #include "kudu/master/master_rpc.h"
-#include "kudu/security/mini_kdc.h"
+#include "kudu/security/test/mini_kdc.h"
 #include "kudu/server/server_base.pb.h"
 #include "kudu/server/server_base.proxy.h"
 #include "kudu/tserver/tserver_service.proxy.h"

http://git-wip-us.apache.org/repos/asf/kudu/blob/c25aea49/src/kudu/rpc/sasl_rpc-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/rpc/sasl_rpc-test.cc b/src/kudu/rpc/sasl_rpc-test.cc
index 5861f7b..1a5de40 100644
--- a/src/kudu/rpc/sasl_rpc-test.cc
+++ b/src/kudu/rpc/sasl_rpc-test.cc
@@ -34,7 +34,7 @@
 #include "kudu/rpc/sasl_client.h"
 #include "kudu/rpc/sasl_common.h"
 #include "kudu/rpc/sasl_server.h"
-#include "kudu/security/mini_kdc.h"
+#include "kudu/security/test/mini_kdc.h"
 #include "kudu/util/monotime.h"
 #include "kudu/util/net/sockaddr.h"
 #include "kudu/util/net/socket.h"

http://git-wip-us.apache.org/repos/asf/kudu/blob/c25aea49/src/kudu/security/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/kudu/security/CMakeLists.txt b/src/kudu/security/CMakeLists.txt
index 3386f3e..28a9543 100644
--- a/src/kudu/security/CMakeLists.txt
+++ b/src/kudu/security/CMakeLists.txt
@@ -16,7 +16,7 @@
 # under the License.
 
 set(SECURITY_TEST_SRCS
-  mini_kdc.cc
+  test/mini_kdc.cc
 )
 
 add_library(security-test ${SECURITY_TEST_SRCS})
@@ -30,4 +30,4 @@ set(KUDU_TEST_LINK_LIBS
   security-test
   ${KUDU_MIN_TEST_LIBS})
 
-ADD_KUDU_TEST(mini_kdc-test)
+ADD_KUDU_TEST(test/mini_kdc-test)

http://git-wip-us.apache.org/repos/asf/kudu/blob/c25aea49/src/kudu/security/mini_kdc-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/security/mini_kdc-test.cc b/src/kudu/security/mini_kdc-test.cc
deleted file mode 100644
index 66fcb4c..0000000
--- a/src/kudu/security/mini_kdc-test.cc
+++ /dev/null
@@ -1,69 +0,0 @@
-// 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 <string>
-
-#include <gtest/gtest.h>
-
-#include "kudu/security/mini_kdc.h"
-#include "kudu/util/env.h"
-#include "kudu/util/test_util.h"
-
-using std::string;
-
-namespace kudu {
-
-TEST(MiniKdcTest, TestBasicOperation) {
-  MiniKdcOptions options;
-  MiniKdc kdc(options);
-  ASSERT_OK(kdc.Start());
-  ASSERT_GT(kdc.port(), 0);
-  ASSERT_OK(kdc.CreateUserPrincipal("alice"));
-  ASSERT_OK(kdc.Kinit("alice"));
-
-  ASSERT_OK(kdc.Stop());
-  ASSERT_OK(kdc.Start());
-
-  ASSERT_OK(kdc.CreateUserPrincipal("bob"));
-  ASSERT_OK(kdc.Kinit("bob"));
-
-  string klist;
-  ASSERT_OK(kdc.Klist(&klist));
-  ASSERT_STR_CONTAINS(klist, "alice@KRBTEST.COM");
-  ASSERT_STR_CONTAINS(klist, "bob@KRBTEST.COM");
-  ASSERT_STR_CONTAINS(klist, "krbtgt/KRBTEST.COM@KRBTEST.COM");
-
-  // Drop 'bob' credentials. We'll get a RuntimeError because klist
-  // exits with a non-zero exit code if there are no cached credentials.
-  ASSERT_OK(kdc.Kdestroy());
-  ASSERT_TRUE(kdc.Klist(&klist).IsRuntimeError());
-
-  // Test keytab creation.
-  string kt_path;
-  ASSERT_OK(kdc.CreateServiceKeytab("kudu/foo.example.com", &kt_path));
-  SCOPED_TRACE(kt_path);
-  ASSERT_OK(kdc.KlistKeytab(kt_path, &klist));
-  ASSERT_STR_CONTAINS(klist, "kudu/foo.example.com@KRBTEST.COM");
-}
-
-// Regression test to ensure that dropping a stopped MiniKdc doesn't panic.
-TEST(MiniKdcTest, TestStopDrop) {
-  MiniKdcOptions options;
-  MiniKdc kdc(options);
-}
-
-} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/c25aea49/src/kudu/security/mini_kdc.cc
----------------------------------------------------------------------
diff --git a/src/kudu/security/mini_kdc.cc b/src/kudu/security/mini_kdc.cc
deleted file mode 100644
index 8cb73eb..0000000
--- a/src/kudu/security/mini_kdc.cc
+++ /dev/null
@@ -1,363 +0,0 @@
-// 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/security/mini_kdc.h"
-
-#include <csignal>
-#include <stdlib.h>
-
-#include <limits>
-#include <map>
-#include <memory>
-#include <string>
-
-#include <glog/logging.h>
-
-#include "kudu/gutil/gscoped_ptr.h"
-#include "kudu/gutil/strings/numbers.h"
-#include "kudu/gutil/strings/split.h"
-#include "kudu/gutil/strings/strip.h"
-#include "kudu/gutil/strings/substitute.h"
-#include "kudu/util/env.h"
-#include "kudu/util/monotime.h"
-#include "kudu/util/path_util.h"
-#include "kudu/util/subprocess.h"
-#include "kudu/util/test_util.h"
-
-using std::map;
-using std::string;
-using std::unique_ptr;
-using strings::Substitute;
-
-namespace kudu {
-
-string MiniKdcOptions::ToString() const {
-  return strings::Substitute("{ realm: $0, port: $1, data_root: $2 }", realm, port, data_root);
-}
-
-MiniKdc::MiniKdc()
-    : MiniKdc(MiniKdcOptions()) {
-}
-
-MiniKdc::MiniKdc(const MiniKdcOptions& options)
-    : options_(options) {
-  if (options_.realm.empty()) {
-    options_.realm = "KRBTEST.COM";
-  }
-  if (options_.data_root.empty()) {
-    options_.data_root = JoinPathSegments(GetTestDataDirectory(), "krb5kdc");
-  }
-}
-
-MiniKdc::~MiniKdc() {
-  if (kdc_process_) {
-    WARN_NOT_OK(Stop(), "Unable to stop MiniKdc");
-  }
-}
-
-map<string, string> MiniKdc::GetEnvVars() const {
-  return {
-    {"KRB5_CONFIG", JoinPathSegments(options_.data_root, "krb5.conf")},
-    {"KRB5_KDC_PROFILE", JoinPathSegments(options_.data_root, "kdc.conf")},
-    {"KRB5CCNAME", "DIR:" + JoinPathSegments(options_.data_root, "krb5cc")}
-  };
-}
-
-vector<string> MiniKdc::MakeArgv(const vector<string>& in_argv) {
-  vector<string> real_argv = { "env" };
-  for (const auto& p : GetEnvVars()) {
-    real_argv.push_back(Substitute("$0=$1", p.first, p.second));
-  }
-  for (const string& a : in_argv) {
-    real_argv.push_back(a);
-  }
-  return real_argv;
-}
-
-namespace {
-// Attempts to find the path to the specified Kerberos binary, storing it in 'path'.
-Status GetBinaryPath(const string& binary,
-                     const vector<string>& search,
-                     string* path) {
-  string p;
-
-  // First, check specified locations which are sometimes not on the PATH.
-  // This is necessary to check first so that the system Heimdal kerberos
-  // binaries won't be found first on OS X.
-  for (const auto& location : search) {
-    p = JoinPathSegments(location, binary);
-    if (Env::Default()->FileExists(p)) {
-      *path = p;
-      return Status::OK();
-    }
-  }
-
-  // Next check if the binary is on the PATH.
-  Status s = Subprocess::Call({ "which", binary }, "", &p);
-  if (s.ok()) {
-    StripTrailingNewline(&p);
-    *path = p;
-    return Status::OK();
-  }
-
-  return Status::NotFound("Unable to find binary", binary);
-}
-
-// Attempts to find the path to the specified Kerberos binary, storing it in 'path'.
-Status GetBinaryPath(const string& binary, string* path) {
-  static const vector<string> kCommonLocations = {
-    "/usr/local/opt/krb5/sbin", // Homebrew
-    "/usr/local/opt/krb5/bin", // Homebrew
-    "/opt/local/sbin", // Macports
-    "/opt/local/bin", // Macports
-    "/usr/lib/mit/sbin", // SLES
-    "/usr/sbin", // Linux
-  };
-  return GetBinaryPath(binary, kCommonLocations, path);
-}
-} // namespace
-
-
-Status MiniKdc::Start() {
-  CHECK(!kdc_process_);
-  VLOG(1) << "Starting Kerberos KDC: " << options_.ToString();
-
-  if (!Env::Default()->FileExists(options_.data_root)) {
-    RETURN_NOT_OK(Env::Default()->CreateDir(options_.data_root));
-
-    RETURN_NOT_OK(CreateKdcConf());
-    RETURN_NOT_OK(CreateKrb5Conf());
-
-    // Create the KDC database using the kdb5_util tool.
-    string kdb5_util_bin;
-    RETURN_NOT_OK(GetBinaryPath("kdb5_util", &kdb5_util_bin));
-
-    RETURN_NOT_OK(Subprocess::Call(MakeArgv({
-        kdb5_util_bin, "create",
-        "-s", // Stash the master password.
-        "-P", "masterpw", // Set a password.
-        "-W", // Use weak entropy (since we don't need real security).
-    })));
-  }
-
-  // Start the Kerberos KDC.
-  string krb5kdc_bin;
-  RETURN_NOT_OK(GetBinaryPath("krb5kdc", &krb5kdc_bin));
-
-  kdc_process_.reset(new Subprocess(
-      "env", MakeArgv({
-      krb5kdc_bin,
-      "-n", // Do not daemonize.
-  })));
-
-  RETURN_NOT_OK(kdc_process_->Start());
-
-  // If we asked for an ephemeral port, grab the actual ports and
-  // rewrite the configuration so that clients can connect.
-  if (options_.port == 0) {
-    RETURN_NOT_OK(WaitForKdcPorts());
-    RETURN_NOT_OK(CreateKrb5Conf());
-    RETURN_NOT_OK(CreateKdcConf());
-  }
-
-  return Status::OK();
-}
-
-Status MiniKdc::Stop() {
-  CHECK(kdc_process_);
-  VLOG(1) << "Stopping KDC";
-  unique_ptr<Subprocess> proc(kdc_process_.release());
-  RETURN_NOT_OK(proc->Kill(SIGKILL));
-  RETURN_NOT_OK(proc->Wait());
-
-  return Status::OK();
-}
-
-// Creates a kdc.conf file according to the provided options.
-Status MiniKdc::CreateKdcConf() const {
-  static const string kFileTemplate = R"(
-[kdcdefaults]
-kdc_ports = ""
-kdc_tcp_ports = $2
-
-[realms]
-$1 = {
-        acl_file = $0/kadm5.acl
-        admin_keytab = $0/kadm5.keytab
-        database_name = $0/principal
-        key_stash_file = $0/.k5.$1
-        max_renewable_life = 7d 0h 0m 0s
-}
-  )";
-  string file_contents = strings::Substitute(kFileTemplate, options_.data_root,
-                                             options_.realm, options_.port);
-  return WriteStringToFile(Env::Default(), file_contents,
-                           JoinPathSegments(options_.data_root, "kdc.conf"));
-}
-
-// Creates a krb5.conf file according to the provided options.
-Status MiniKdc::CreateKrb5Conf() const {
-  static const string kFileTemplate = R"(
-[logging]
-    kdc = STDERR
-
-[libdefaults]
-    default_realm = $1
-    dns_lookup_kdc = false
-    dns_lookup_realm = false
-    forwardable = true
-    renew_lifetime = 7d
-    ticket_lifetime = 24h
-
-    # In miniclusters, we start daemons on local loopback IPs that
-    # have no reverse DNS entries. So, disable reverse DNS.
-    rdns = false
-
-    # The server side will start its GSSAPI server using the local FQDN.
-    # However, in tests, we connect to it via a non-matching loopback IP.
-    # This enables us to connect despite that mismatch.
-    ignore_acceptor_hostname = true
-
-    # The KDC is configured to only use TCP, so the client should not prefer UDP.
-    udp_preference_limit = 0
-
-[realms]
-    $1 = {
-        kdc = 127.0.0.1:$0
-    }
-  )";
-  string file_contents = strings::Substitute(kFileTemplate, options_.port, options_.realm);
-  return WriteStringToFile(Env::Default(), file_contents,
-                           JoinPathSegments(options_.data_root, "krb5.conf"));
-}
-
-Status MiniKdc::WaitForKdcPorts() {
-  // We have to use 'lsof' to figure out which ports the KDC bound to if we
-  // requested ephemeral ones. The KDC doesn't log the bound port or expose it
-  // in any other fashion, and re-implementing lsof involves parsing a lot of
-  // files in /proc/. So, requiring lsof for tests and parsing its output seems
-  // more straight-forward. We call lsof in a loop in case the kdc is slow to
-  // bind to the ports.
-
-  string lsof;
-  RETURN_NOT_OK(GetBinaryPath("lsof", {"/sbin", "/usr/sbin"}, &lsof));
-
-  vector<string> cmd = {
-    lsof, "-wbn", "-Ffn",
-    "-p", std::to_string(kdc_process_->pid()),
-    "-a", "-i", "4TCP"};
-
-  string lsof_out;
-  for (int i = 1; ; i++) {
-    lsof_out.clear();
-    Status s = Subprocess::Call(cmd, "", &lsof_out);
-
-    if (s.ok()) {
-      StripTrailingNewline(&lsof_out);
-      break;
-    } else if (i > 10) {
-      return s;
-    }
-
-    SleepFor(MonoDelta::FromMilliseconds(i * i));
-  }
-
-  // The '-Ffn' flag gets lsof to output something like:
-  //   p19730
-  //   f123
-  //   n*:41254
-  // The first line is the pid. We ignore it.
-  // The second line is the file descriptor number. We ignore it.
-  // The third line has the bind address and port.
-  vector<string> lines = strings::Split(lsof_out, "\n");
-  int32_t port = -1;
-  if (lines.size() != 3 ||
-      lines[2].substr(0, 3) != "n*:" ||
-      !safe_strto32(lines[2].substr(3), &port) ||
-      port <= 0) {
-    return Status::RuntimeError("unexpected lsof output", lsof_out);
-  }
-  CHECK(port > 0 && port < std::numeric_limits<uint16_t>::max())
-      << "parsed invalid port: " << port;
-  options_.port = port;
-  VLOG(1) << "Determined bound KDC port: " << options_.port;
-  return Status::OK();
-}
-
-Status MiniKdc::CreateUserPrincipal(const string& username) {
-  string kadmin;
-  RETURN_NOT_OK(GetBinaryPath("kadmin.local", &kadmin));
-  RETURN_NOT_OK(Subprocess::Call(MakeArgv({
-          kadmin, "-q", Substitute("add_principal -pw $0 $0", username)})));
-  return Status::OK();
-}
-
-Status MiniKdc::CreateServiceKeytab(const string& spn,
-                                    string* path) {
-  string kt_path = spn;
-  StripString(&kt_path, "/", '_');
-  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::Kinit(const string& username) {
-  string kinit;
-  RETURN_NOT_OK(GetBinaryPath("kinit", &kinit));
-  Subprocess::Call(MakeArgv({ kinit, username }), username);
-  return Status::OK();
-}
-
-Status MiniKdc::Kdestroy() {
-  string kdestroy;
-  RETURN_NOT_OK(GetBinaryPath("kdestroy", &kdestroy));
-  return Subprocess::Call(MakeArgv({ kdestroy, "-A" }));
-}
-
-Status MiniKdc::Klist(string* output) {
-  string klist;
-  RETURN_NOT_OK(GetBinaryPath("klist", &klist));
-  RETURN_NOT_OK(Subprocess::Call(MakeArgv({ klist, "-A" }), "", output));
-  return Status::OK();
-}
-
-Status MiniKdc::KlistKeytab(const string& keytab_path, string* output) {
-  string klist;
-  RETURN_NOT_OK(GetBinaryPath("klist", &klist));
-  RETURN_NOT_OK(Subprocess::Call(MakeArgv({ klist, "-k", keytab_path }), "", output));
-  return Status::OK();
-}
-
-Status MiniKdc::SetKrb5Environment() const {
-  if (!kdc_process_) {
-    return Status::IllegalState("KDC not started");
-  }
-  for (const auto& p : GetEnvVars()) {
-    CHECK_ERR(setenv(p.first.c_str(), p.second.c_str(), 1 /*overwrite*/));
-  }
-
-  return Status::OK();
-}
-
-} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/c25aea49/src/kudu/security/mini_kdc.h
----------------------------------------------------------------------
diff --git a/src/kudu/security/mini_kdc.h b/src/kudu/security/mini_kdc.h
deleted file mode 100644
index bb5dede..0000000
--- a/src/kudu/security/mini_kdc.h
+++ /dev/null
@@ -1,127 +0,0 @@
-// 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 <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <glog/logging.h>
-
-#include "kudu/util/status.h"
-
-namespace kudu {
-
-class Env;
-class Subprocess;
-
-struct MiniKdcOptions {
-
-  // Kerberos Realm.
-  // Default: "KRBTEST.COM"
-  std::string realm;
-
-  // Directory in which to store data.
-  // Default: "", which auto-generates a unique path for this KDC.
-  // The default may only be used from a gtest unit test.
-  std::string data_root;
-
-  // KDC port.
-  // Default: 0 (ephemeral port).
-  uint16_t port = 0;
-
-  // Returns a string representation of the options suitable for debug printing.
-  std::string ToString() const;
-};
-
-class MiniKdc {
- public:
-  // Creates a new MiniKdc with the default options.
-  MiniKdc();
-
-  // Creates a new MiniKdc with the provided options.
-  explicit MiniKdc(const MiniKdcOptions& options);
-
-  ~MiniKdc();
-
-  // Starts the mini Kerberos KDC.
-  Status Start() WARN_UNUSED_RESULT;
-
-  // Stops the mini Kerberos KDC.
-  Status Stop() WARN_UNUSED_RESULT;
-
-  uint16_t port() const {
-    CHECK(kdc_process_) << "must start first";
-    return options_.port;
-  }
-
-  // Creates a new user with the given username.
-  // The password is the same as the username.
-  Status CreateUserPrincipal(const std::string& username) WARN_UNUSED_RESULT;
-
-  // Creates a new service principal and associated keytab, returning its
-  // path in 'path'. 'spn' is the desired service principal name
-  // (e.g. "kudu/foo.example.com"). If the principal already exists, its key
-  // will be reset and a new keytab will be generated.
-  Status CreateServiceKeytab(const std::string& spn, std::string* path);
-
-  // Kinit a user to the mini KDC.
-  Status Kinit(const std::string& username) WARN_UNUSED_RESULT;
-
-  // Destroy any credentials in the current ticket cache.
-  // Equivalent to 'kdestroy -A'.
-  Status Kdestroy() WARN_UNUSED_RESULT;
-
-  // Call the 'klist' utility.  This is useful for logging the local ticket
-  // cache state.
-  Status Klist(std::string* output) WARN_UNUSED_RESULT;
-
-  // Call the 'klist' utility to list the contents of a specific keytab.
-  Status KlistKeytab(const std::string& keytab_path,
-                     std::string* output) WARN_UNUSED_RESULT;
-
-  // Sets the environment variables used by the krb5 library
-  // in the current process. This points the SASL library at the
-  // configuration associated with this KDC.
-  Status SetKrb5Environment() const;
-
-  // Returns a map of the Kerberos environment variables which configure
-  // a process to use this KDC.
-  std::map<std::string, std::string> GetEnvVars() const;
-
- private:
-
-  // Prepends required Kerberos environment variables to the process arguments.
-  std::vector<std::string> MakeArgv(const std::vector<std::string>& in_argv);
-
-  // Creates a kdc.conf in the data root.
-  Status CreateKrb5Conf() const WARN_UNUSED_RESULT;
-
-  // Creates a krb5.conf in the data root.
-  Status CreateKdcConf() const WARN_UNUSED_RESULT;
-
-  // Determine the ports that the KDC bound to. Will wait for the KDC if it is
-  // still initializing.
-  Status WaitForKdcPorts() WARN_UNUSED_RESULT;
-
-  std::unique_ptr<Subprocess> kdc_process_;
-  MiniKdcOptions options_;
-};
-
-} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/c25aea49/src/kudu/security/test/mini_kdc-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/security/test/mini_kdc-test.cc b/src/kudu/security/test/mini_kdc-test.cc
new file mode 100644
index 0000000..3c57340
--- /dev/null
+++ b/src/kudu/security/test/mini_kdc-test.cc
@@ -0,0 +1,69 @@
+// 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 <string>
+
+#include <gtest/gtest.h>
+
+#include "kudu/security/test/mini_kdc.h"
+#include "kudu/util/env.h"
+#include "kudu/util/test_util.h"
+
+using std::string;
+
+namespace kudu {
+
+TEST(MiniKdcTest, TestBasicOperation) {
+  MiniKdcOptions options;
+  MiniKdc kdc(options);
+  ASSERT_OK(kdc.Start());
+  ASSERT_GT(kdc.port(), 0);
+  ASSERT_OK(kdc.CreateUserPrincipal("alice"));
+  ASSERT_OK(kdc.Kinit("alice"));
+
+  ASSERT_OK(kdc.Stop());
+  ASSERT_OK(kdc.Start());
+
+  ASSERT_OK(kdc.CreateUserPrincipal("bob"));
+  ASSERT_OK(kdc.Kinit("bob"));
+
+  string klist;
+  ASSERT_OK(kdc.Klist(&klist));
+  ASSERT_STR_CONTAINS(klist, "alice@KRBTEST.COM");
+  ASSERT_STR_CONTAINS(klist, "bob@KRBTEST.COM");
+  ASSERT_STR_CONTAINS(klist, "krbtgt/KRBTEST.COM@KRBTEST.COM");
+
+  // Drop 'bob' credentials. We'll get a RuntimeError because klist
+  // exits with a non-zero exit code if there are no cached credentials.
+  ASSERT_OK(kdc.Kdestroy());
+  ASSERT_TRUE(kdc.Klist(&klist).IsRuntimeError());
+
+  // Test keytab creation.
+  string kt_path;
+  ASSERT_OK(kdc.CreateServiceKeytab("kudu/foo.example.com", &kt_path));
+  SCOPED_TRACE(kt_path);
+  ASSERT_OK(kdc.KlistKeytab(kt_path, &klist));
+  ASSERT_STR_CONTAINS(klist, "kudu/foo.example.com@KRBTEST.COM");
+}
+
+// Regression test to ensure that dropping a stopped MiniKdc doesn't panic.
+TEST(MiniKdcTest, TestStopDrop) {
+  MiniKdcOptions options;
+  MiniKdc kdc(options);
+}
+
+} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/c25aea49/src/kudu/security/test/mini_kdc.cc
----------------------------------------------------------------------
diff --git a/src/kudu/security/test/mini_kdc.cc b/src/kudu/security/test/mini_kdc.cc
new file mode 100644
index 0000000..8e92080
--- /dev/null
+++ b/src/kudu/security/test/mini_kdc.cc
@@ -0,0 +1,363 @@
+// 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/security/test/mini_kdc.h"
+
+#include <csignal>
+#include <stdlib.h>
+
+#include <limits>
+#include <map>
+#include <memory>
+#include <string>
+
+#include <glog/logging.h>
+
+#include "kudu/gutil/gscoped_ptr.h"
+#include "kudu/gutil/strings/numbers.h"
+#include "kudu/gutil/strings/split.h"
+#include "kudu/gutil/strings/strip.h"
+#include "kudu/gutil/strings/substitute.h"
+#include "kudu/util/env.h"
+#include "kudu/util/monotime.h"
+#include "kudu/util/path_util.h"
+#include "kudu/util/subprocess.h"
+#include "kudu/util/test_util.h"
+
+using std::map;
+using std::string;
+using std::unique_ptr;
+using strings::Substitute;
+
+namespace kudu {
+
+string MiniKdcOptions::ToString() const {
+  return strings::Substitute("{ realm: $0, port: $1, data_root: $2 }", realm, port, data_root);
+}
+
+MiniKdc::MiniKdc()
+    : MiniKdc(MiniKdcOptions()) {
+}
+
+MiniKdc::MiniKdc(const MiniKdcOptions& options)
+    : options_(options) {
+  if (options_.realm.empty()) {
+    options_.realm = "KRBTEST.COM";
+  }
+  if (options_.data_root.empty()) {
+    options_.data_root = JoinPathSegments(GetTestDataDirectory(), "krb5kdc");
+  }
+}
+
+MiniKdc::~MiniKdc() {
+  if (kdc_process_) {
+    WARN_NOT_OK(Stop(), "Unable to stop MiniKdc");
+  }
+}
+
+map<string, string> MiniKdc::GetEnvVars() const {
+  return {
+    {"KRB5_CONFIG", JoinPathSegments(options_.data_root, "krb5.conf")},
+    {"KRB5_KDC_PROFILE", JoinPathSegments(options_.data_root, "kdc.conf")},
+    {"KRB5CCNAME", "DIR:" + JoinPathSegments(options_.data_root, "krb5cc")}
+  };
+}
+
+vector<string> MiniKdc::MakeArgv(const vector<string>& in_argv) {
+  vector<string> real_argv = { "env" };
+  for (const auto& p : GetEnvVars()) {
+    real_argv.push_back(Substitute("$0=$1", p.first, p.second));
+  }
+  for (const string& a : in_argv) {
+    real_argv.push_back(a);
+  }
+  return real_argv;
+}
+
+namespace {
+// Attempts to find the path to the specified Kerberos binary, storing it in 'path'.
+Status GetBinaryPath(const string& binary,
+                     const vector<string>& search,
+                     string* path) {
+  string p;
+
+  // First, check specified locations which are sometimes not on the PATH.
+  // This is necessary to check first so that the system Heimdal kerberos
+  // binaries won't be found first on OS X.
+  for (const auto& location : search) {
+    p = JoinPathSegments(location, binary);
+    if (Env::Default()->FileExists(p)) {
+      *path = p;
+      return Status::OK();
+    }
+  }
+
+  // Next check if the binary is on the PATH.
+  Status s = Subprocess::Call({ "which", binary }, "", &p);
+  if (s.ok()) {
+    StripTrailingNewline(&p);
+    *path = p;
+    return Status::OK();
+  }
+
+  return Status::NotFound("Unable to find binary", binary);
+}
+
+// Attempts to find the path to the specified Kerberos binary, storing it in 'path'.
+Status GetBinaryPath(const string& binary, string* path) {
+  static const vector<string> kCommonLocations = {
+    "/usr/local/opt/krb5/sbin", // Homebrew
+    "/usr/local/opt/krb5/bin", // Homebrew
+    "/opt/local/sbin", // Macports
+    "/opt/local/bin", // Macports
+    "/usr/lib/mit/sbin", // SLES
+    "/usr/sbin", // Linux
+  };
+  return GetBinaryPath(binary, kCommonLocations, path);
+}
+} // namespace
+
+
+Status MiniKdc::Start() {
+  CHECK(!kdc_process_);
+  VLOG(1) << "Starting Kerberos KDC: " << options_.ToString();
+
+  if (!Env::Default()->FileExists(options_.data_root)) {
+    RETURN_NOT_OK(Env::Default()->CreateDir(options_.data_root));
+
+    RETURN_NOT_OK(CreateKdcConf());
+    RETURN_NOT_OK(CreateKrb5Conf());
+
+    // Create the KDC database using the kdb5_util tool.
+    string kdb5_util_bin;
+    RETURN_NOT_OK(GetBinaryPath("kdb5_util", &kdb5_util_bin));
+
+    RETURN_NOT_OK(Subprocess::Call(MakeArgv({
+        kdb5_util_bin, "create",
+        "-s", // Stash the master password.
+        "-P", "masterpw", // Set a password.
+        "-W", // Use weak entropy (since we don't need real security).
+    })));
+  }
+
+  // Start the Kerberos KDC.
+  string krb5kdc_bin;
+  RETURN_NOT_OK(GetBinaryPath("krb5kdc", &krb5kdc_bin));
+
+  kdc_process_.reset(new Subprocess(
+      "env", MakeArgv({
+      krb5kdc_bin,
+      "-n", // Do not daemonize.
+  })));
+
+  RETURN_NOT_OK(kdc_process_->Start());
+
+  // If we asked for an ephemeral port, grab the actual ports and
+  // rewrite the configuration so that clients can connect.
+  if (options_.port == 0) {
+    RETURN_NOT_OK(WaitForKdcPorts());
+    RETURN_NOT_OK(CreateKrb5Conf());
+    RETURN_NOT_OK(CreateKdcConf());
+  }
+
+  return Status::OK();
+}
+
+Status MiniKdc::Stop() {
+  CHECK(kdc_process_);
+  VLOG(1) << "Stopping KDC";
+  unique_ptr<Subprocess> proc(kdc_process_.release());
+  RETURN_NOT_OK(proc->Kill(SIGKILL));
+  RETURN_NOT_OK(proc->Wait());
+
+  return Status::OK();
+}
+
+// Creates a kdc.conf file according to the provided options.
+Status MiniKdc::CreateKdcConf() const {
+  static const string kFileTemplate = R"(
+[kdcdefaults]
+kdc_ports = ""
+kdc_tcp_ports = $2
+
+[realms]
+$1 = {
+        acl_file = $0/kadm5.acl
+        admin_keytab = $0/kadm5.keytab
+        database_name = $0/principal
+        key_stash_file = $0/.k5.$1
+        max_renewable_life = 7d 0h 0m 0s
+}
+  )";
+  string file_contents = strings::Substitute(kFileTemplate, options_.data_root,
+                                             options_.realm, options_.port);
+  return WriteStringToFile(Env::Default(), file_contents,
+                           JoinPathSegments(options_.data_root, "kdc.conf"));
+}
+
+// Creates a krb5.conf file according to the provided options.
+Status MiniKdc::CreateKrb5Conf() const {
+  static const string kFileTemplate = R"(
+[logging]
+    kdc = STDERR
+
+[libdefaults]
+    default_realm = $1
+    dns_lookup_kdc = false
+    dns_lookup_realm = false
+    forwardable = true
+    renew_lifetime = 7d
+    ticket_lifetime = 24h
+
+    # In miniclusters, we start daemons on local loopback IPs that
+    # have no reverse DNS entries. So, disable reverse DNS.
+    rdns = false
+
+    # The server side will start its GSSAPI server using the local FQDN.
+    # However, in tests, we connect to it via a non-matching loopback IP.
+    # This enables us to connect despite that mismatch.
+    ignore_acceptor_hostname = true
+
+    # The KDC is configured to only use TCP, so the client should not prefer UDP.
+    udp_preference_limit = 0
+
+[realms]
+    $1 = {
+        kdc = 127.0.0.1:$0
+    }
+  )";
+  string file_contents = strings::Substitute(kFileTemplate, options_.port, options_.realm);
+  return WriteStringToFile(Env::Default(), file_contents,
+                           JoinPathSegments(options_.data_root, "krb5.conf"));
+}
+
+Status MiniKdc::WaitForKdcPorts() {
+  // We have to use 'lsof' to figure out which ports the KDC bound to if we
+  // requested ephemeral ones. The KDC doesn't log the bound port or expose it
+  // in any other fashion, and re-implementing lsof involves parsing a lot of
+  // files in /proc/. So, requiring lsof for tests and parsing its output seems
+  // more straight-forward. We call lsof in a loop in case the kdc is slow to
+  // bind to the ports.
+
+  string lsof;
+  RETURN_NOT_OK(GetBinaryPath("lsof", {"/sbin", "/usr/sbin"}, &lsof));
+
+  vector<string> cmd = {
+    lsof, "-wbn", "-Ffn",
+    "-p", std::to_string(kdc_process_->pid()),
+    "-a", "-i", "4TCP"};
+
+  string lsof_out;
+  for (int i = 1; ; i++) {
+    lsof_out.clear();
+    Status s = Subprocess::Call(cmd, "", &lsof_out);
+
+    if (s.ok()) {
+      StripTrailingNewline(&lsof_out);
+      break;
+    } else if (i > 10) {
+      return s;
+    }
+
+    SleepFor(MonoDelta::FromMilliseconds(i * i));
+  }
+
+  // The '-Ffn' flag gets lsof to output something like:
+  //   p19730
+  //   f123
+  //   n*:41254
+  // The first line is the pid. We ignore it.
+  // The second line is the file descriptor number. We ignore it.
+  // The third line has the bind address and port.
+  vector<string> lines = strings::Split(lsof_out, "\n");
+  int32_t port = -1;
+  if (lines.size() != 3 ||
+      lines[2].substr(0, 3) != "n*:" ||
+      !safe_strto32(lines[2].substr(3), &port) ||
+      port <= 0) {
+    return Status::RuntimeError("unexpected lsof output", lsof_out);
+  }
+  CHECK(port > 0 && port < std::numeric_limits<uint16_t>::max())
+      << "parsed invalid port: " << port;
+  options_.port = port;
+  VLOG(1) << "Determined bound KDC port: " << options_.port;
+  return Status::OK();
+}
+
+Status MiniKdc::CreateUserPrincipal(const string& username) {
+  string kadmin;
+  RETURN_NOT_OK(GetBinaryPath("kadmin.local", &kadmin));
+  RETURN_NOT_OK(Subprocess::Call(MakeArgv({
+          kadmin, "-q", Substitute("add_principal -pw $0 $0", username)})));
+  return Status::OK();
+}
+
+Status MiniKdc::CreateServiceKeytab(const string& spn,
+                                    string* path) {
+  string kt_path = spn;
+  StripString(&kt_path, "/", '_');
+  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::Kinit(const string& username) {
+  string kinit;
+  RETURN_NOT_OK(GetBinaryPath("kinit", &kinit));
+  Subprocess::Call(MakeArgv({ kinit, username }), username);
+  return Status::OK();
+}
+
+Status MiniKdc::Kdestroy() {
+  string kdestroy;
+  RETURN_NOT_OK(GetBinaryPath("kdestroy", &kdestroy));
+  return Subprocess::Call(MakeArgv({ kdestroy, "-A" }));
+}
+
+Status MiniKdc::Klist(string* output) {
+  string klist;
+  RETURN_NOT_OK(GetBinaryPath("klist", &klist));
+  RETURN_NOT_OK(Subprocess::Call(MakeArgv({ klist, "-A" }), "", output));
+  return Status::OK();
+}
+
+Status MiniKdc::KlistKeytab(const string& keytab_path, string* output) {
+  string klist;
+  RETURN_NOT_OK(GetBinaryPath("klist", &klist));
+  RETURN_NOT_OK(Subprocess::Call(MakeArgv({ klist, "-k", keytab_path }), "", output));
+  return Status::OK();
+}
+
+Status MiniKdc::SetKrb5Environment() const {
+  if (!kdc_process_) {
+    return Status::IllegalState("KDC not started");
+  }
+  for (const auto& p : GetEnvVars()) {
+    CHECK_ERR(setenv(p.first.c_str(), p.second.c_str(), 1 /*overwrite*/));
+  }
+
+  return Status::OK();
+}
+
+} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/c25aea49/src/kudu/security/test/mini_kdc.h
----------------------------------------------------------------------
diff --git a/src/kudu/security/test/mini_kdc.h b/src/kudu/security/test/mini_kdc.h
new file mode 100644
index 0000000..bb5dede
--- /dev/null
+++ b/src/kudu/security/test/mini_kdc.h
@@ -0,0 +1,127 @@
+// 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 <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <glog/logging.h>
+
+#include "kudu/util/status.h"
+
+namespace kudu {
+
+class Env;
+class Subprocess;
+
+struct MiniKdcOptions {
+
+  // Kerberos Realm.
+  // Default: "KRBTEST.COM"
+  std::string realm;
+
+  // Directory in which to store data.
+  // Default: "", which auto-generates a unique path for this KDC.
+  // The default may only be used from a gtest unit test.
+  std::string data_root;
+
+  // KDC port.
+  // Default: 0 (ephemeral port).
+  uint16_t port = 0;
+
+  // Returns a string representation of the options suitable for debug printing.
+  std::string ToString() const;
+};
+
+class MiniKdc {
+ public:
+  // Creates a new MiniKdc with the default options.
+  MiniKdc();
+
+  // Creates a new MiniKdc with the provided options.
+  explicit MiniKdc(const MiniKdcOptions& options);
+
+  ~MiniKdc();
+
+  // Starts the mini Kerberos KDC.
+  Status Start() WARN_UNUSED_RESULT;
+
+  // Stops the mini Kerberos KDC.
+  Status Stop() WARN_UNUSED_RESULT;
+
+  uint16_t port() const {
+    CHECK(kdc_process_) << "must start first";
+    return options_.port;
+  }
+
+  // Creates a new user with the given username.
+  // The password is the same as the username.
+  Status CreateUserPrincipal(const std::string& username) WARN_UNUSED_RESULT;
+
+  // Creates a new service principal and associated keytab, returning its
+  // path in 'path'. 'spn' is the desired service principal name
+  // (e.g. "kudu/foo.example.com"). If the principal already exists, its key
+  // will be reset and a new keytab will be generated.
+  Status CreateServiceKeytab(const std::string& spn, std::string* path);
+
+  // Kinit a user to the mini KDC.
+  Status Kinit(const std::string& username) WARN_UNUSED_RESULT;
+
+  // Destroy any credentials in the current ticket cache.
+  // Equivalent to 'kdestroy -A'.
+  Status Kdestroy() WARN_UNUSED_RESULT;
+
+  // Call the 'klist' utility.  This is useful for logging the local ticket
+  // cache state.
+  Status Klist(std::string* output) WARN_UNUSED_RESULT;
+
+  // Call the 'klist' utility to list the contents of a specific keytab.
+  Status KlistKeytab(const std::string& keytab_path,
+                     std::string* output) WARN_UNUSED_RESULT;
+
+  // Sets the environment variables used by the krb5 library
+  // in the current process. This points the SASL library at the
+  // configuration associated with this KDC.
+  Status SetKrb5Environment() const;
+
+  // Returns a map of the Kerberos environment variables which configure
+  // a process to use this KDC.
+  std::map<std::string, std::string> GetEnvVars() const;
+
+ private:
+
+  // Prepends required Kerberos environment variables to the process arguments.
+  std::vector<std::string> MakeArgv(const std::vector<std::string>& in_argv);
+
+  // Creates a kdc.conf in the data root.
+  Status CreateKrb5Conf() const WARN_UNUSED_RESULT;
+
+  // Creates a krb5.conf in the data root.
+  Status CreateKdcConf() const WARN_UNUSED_RESULT;
+
+  // Determine the ports that the KDC bound to. Will wait for the KDC if it is
+  // still initializing.
+  Status WaitForKdcPorts() WARN_UNUSED_RESULT;
+
+  std::unique_ptr<Subprocess> kdc_process_;
+  MiniKdcOptions options_;
+};
+
+} // namespace kudu


Mime
View raw message