webserver: improve SSL certificate handling

* Allows users to specify a separate location for PEM private-key file
  using --webserver_private_key_file (previously required private-key
  and cert. to be in same file).

* Allows users to specify a shell command to run to get the password
  for the webserver's private-key file using
  --webserver_private_key_password_cmd

The separate configuration of cert and key is apparently more commonly
used, so this simplifies deployment. The use of a script to provide the
password for the private key makes it easier to integrate with external
credential providers.

This mirrors the change made in Impala by IMPALA-2051 (8bbe45393c7).

Change-Id: I4b508cebbe6f31556e6d5a5fba5e5e9fb44cf1b9
Reviewed-on: http://gerrit.cloudera.org:8080/5015
Tested-by: Kudu Jenkins
Reviewed-by: Dan Burkert <[email protected]>
Reviewed-by: Alexey Serbin <[email protected]>


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

Branch: refs/heads/master
Commit: 2c052fcb4cd66cd901d40cf053be0c01d0ebc84e
Parents: b235487
Author: Todd Lipcon <[email protected]>
Authored: Tue Nov 8 16:32:11 2016 -0800
Committer: Todd Lipcon <[email protected]>
Committed: Thu Jan 26 23:38:29 2017 +0000

----------------------------------------------------------------------
 src/kudu/security/security-test-util.h |  3 ++
 src/kudu/security/test/test_certs.cc   | 79 +++++++++++++++++++++++++++
 src/kudu/security/test/test_certs.h    | 16 ++++--
 src/kudu/server/CMakeLists.txt         |  2 +-
 src/kudu/server/webserver-test.cc      | 84 +++++++++++++++++++++++++++--
 src/kudu/server/webserver.cc           | 51 +++++++++++++-----
 src/kudu/server/webserver_options.cc   | 16 +++++-
 src/kudu/server/webserver_options.h    |  2 +
 src/kudu/util/curl_util.cc             |  3 ++
 src/kudu/util/curl_util.h              | 10 ++++
 10 files changed, 245 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kudu/blob/2c052fcb/src/kudu/security/security-test-util.h
----------------------------------------------------------------------
diff --git a/src/kudu/security/security-test-util.h 
b/src/kudu/security/security-test-util.h
index b9ea0b2..76853d4 100644
--- a/src/kudu/security/security-test-util.h
+++ b/src/kudu/security/security-test-util.h
@@ -25,6 +25,9 @@
 namespace kudu {
 namespace security {
 
+// TODO(todd): consolidate these certs with those in
+// security/test/test_certs.h once we support configuring a password
+// for the RPC cert.
 static Status CreateSSLServerCert(const std::string& file_path) {
   static const char* test_server_cert = R"(
 -----BEGIN CERTIFICATE-----

http://git-wip-us.apache.org/repos/asf/kudu/blob/2c052fcb/src/kudu/security/test/test_certs.cc
----------------------------------------------------------------------
diff --git a/src/kudu/security/test/test_certs.cc 
b/src/kudu/security/test/test_certs.cc
index 15d4519..1f1e5e5 100644
--- a/src/kudu/security/test/test_certs.cc
+++ b/src/kudu/security/test/test_certs.cc
@@ -17,6 +17,14 @@
 
 #include "kudu/security/test/test_certs.h"
 
+#include <string>
+
+#include "kudu/util/env.h"
+#include "kudu/util/path_util.h"
+#include "kudu/util/status.h"
+
+using std::string;
+
 namespace kudu {
 namespace security {
 namespace ca {
@@ -208,5 +216,76 @@ H/sbP2R+P6RvQceLEEtk6ZZLiuScVmLtVOpUoUZb3Rx6a7GKbec7oQ==
 )***";
 
 } // namespace ca
+
+Status CreateTestSSLCerts(const string& dir,
+                          string* cert_file,
+                          string* key_file,
+                          string* key_password) {
+  const char* kCert = R"(
+-----BEGIN CERTIFICATE-----
+MIIDXTCCAkWgAwIBAgIJAOOmFHYkBz4rMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQwHhcNMTYxMTAyMjI0OTQ5WhcNMTcwMjEwMjI0OTQ5WjBF
+MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAppo9GwiDisQVYAF9NXl8ykqo0MIi5rfNwiE9kUWbZ2ejzxs+1Cf7WCn4
+mzbkJx5ZscRjhnNb6dJxtZJeid/qgiNVBcNzh35H8J+ao0tEbHjCs7rKOX0etsFU
+p4GQwYkdfpvVBsU8ciXvkxhvt1XjSU3/YJJRAvCyGVxUQlKiVKGCD4OnFNBwMdNw
+7qI8ryiRv++7I9udfSuM713yMeBtkkV7hWUfxrTgQOLsV/CS+TsSoOJ7JJqHozeZ
++VYom85UqSfpIFJVzM6S7BTb6SX/vwYIoS70gubT3HbHgDRcMvpCye1npHL9fL7B
+87XZn7wnnUem0eeCqWyUjJ82Uj9mQQIDAQABo1AwTjAdBgNVHQ4EFgQUOY7rpWGo
+ZMrmyRZ9RohPWVwyPBowHwYDVR0jBBgwFoAUOY7rpWGoZMrmyRZ9RohPWVwyPBow
+DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEATKh3io8ruqbhmopY3xQW
+A2pEhs4ZSu3H+AfULMruVsXKEZjWp27nTsFaxLZYUlzeZr0EcWwZ79qkcA8Dyj+m
+VHhrCAPpcjsDACh1ZdUQAgASkVS4VQvkukct3DFa3y0lz5VwQIxjoQR5y6dCvxxX
+T9NpRo/Z7pd4MRhEbz3NT6PScQ9f2MTrR0NOikLdB98JlpKQbEKxzbMhWDw4J3mr
+mK6zdemjdCcRDsBVPswKnyAjkibXaZkpNRzjvDNAgO88MKlArCYoyRZqIfkcSXAw
+wTdGQ+5GQLsY9zS49Rrhk9R7eOmDhaHybdRBDqW1JiCSmzURZAxlnrjox4GmC3JJ
+aA==
+-----END CERTIFICATE-----
+)";
+  const char* kKey = R"(
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIA3+5nR+Jr18CAggA
+MBQGCCqGSIb3DQMHBAhCwHFGbZEcBgSCBMjT9vVbrYpd1reGwfLhk8703ihlspZi
+cm4Z2MM+lkJs0Pi8O4n+zNSTfgrEr2XGlKIktBWEBxbhYrdYy1bYm4Fu5Z055JFo
+89L/9zT1Zm/agk3CUW+ljirYZF60t/dDWmzLwt9f4dp8m4etL/UwvMJ1NglyxMkj
+c03+aWWh4wHLRGkGDJFsKEQY87LL+nAOZS7P1qY38HnzQTOgLyNpntXX3SryzvQ6
+CpNIyQFhVGXfGn6DzGJB76heyLpCyAvIiP87vBS3zbnSqDM6v6PTW3SMo8R42RfL
+d0CVmO8Z8NQeX29EkMHSRu7gCwXu1pf40QIog2vJZ7dmUgsU9GbBSg8l3nVWS6sm
+AICNwPvHXRMMGX0wBJyK2ihuC7rVd5aZLgmu1sjlLYaB9KkoETcFFT8KbFpnd6aR
+1whXQ4rPm1WyGtqbVGkZthisvGUeeGnbv2HXUVthSGleD+hQuwFXa8wE/8+Ruq9X
+rNv/WMrf2NLIm+wbr19JzVSvLh+j7mIBMZmIwGQvBPo3/Tuq2zeyZdfSOroFcanq
+Lyoc6yF5rAkU5BLVe36e48MarWICWDCxiz1n6tWdCpcXWfBvlWIkkjP1/rhqnOW3
+DKNjTyGJhaVYydkseoGrrpj4gkyyWtpgw+8c7jdtk+7cmIxpXu3UU6fh+Yt3vEhF
+VqHvCd+YuUgpJ+TW574xiau4xbyib5Qv6JAR1Qp3MtzZ1IngyCU4QqqgxBGMBVqc
+2LI7Romw7icfdzJxMeMp9WXh8D0Bxx5kjDcO0eUnkpVkFyozXZkoLCnoJ8u3yJo6
+yV4RQ4mOAWj7uZYg9KEUywNCHuIVPKG0CEfQkxKiPw4uvmdimKZ6Ij7aqrrc68Vi
+oZnNHJEfJhnG78MKHgxXHNrMLFXBgPpLoBQxUBVhI2nq5D2l7gL5bKc9JZNg+mRJ
+CouijXBHS1nZ/7GwVjLvNIGKWEsuiz1P0SYki1S02/3bBF9ySdeNGl1XTNqK4Xqy
+arK4agJc89wg3N6SOIA+q8kA4LScafMtCkVDChw0CcLUrQERpH1tv1cqCt5zXF9n
+5AnnWmEM2knlkHxXzg7k/1YXUz4JMmAhS4gVHuNU1uZR152lD+kgSy2K4sCyCfx2
+iWFpDGj556AUxDRGrqKU7OLC/64AuNz5IJs8doDa29cGGKFw3/foRoOaya84ISGW
+GTl2qDOHZrJbgR6BUpSh2E2mVyO3GwIBst6yIb5VaTpNuIwS6fhjC4fQZEV6hHcx
+qNvHxTTvz6eag4TeUPR48h/kGsI44DB0r4I79WbTwg5dvdlYbchPIwAs888bxpd6
+7ZxSg7EwuyHqJEL0FkWcDgw89+vLDETQiTwfscDxwm893gTymj5JPSDz35kudPlI
+rsNfABLeXSg8Z8/7LsPP6Q48c1jisLVPPndV80cS791dvyXRxZWvX2z5UFuTDy3K
+PV3L60mdejXudzFPfvovhgJDIWsKMmlxYplRWvG3WUXTck1Kb7KEcZmuo4nJMOID
+6caoDNa5L9p5XH54sBCB7uqTNdqijaqq9iBFx/MqL3LHt6/wVF5J9g6PmuDxuYDX
+tBKU0ns67U6wUxvLGBX/7RnWUibc5JwVGPBGw1E5u6MKWxW9Q6Dk1WakAtsqtDkR
+WEc=
+-----END ENCRYPTED PRIVATE KEY-----
+)";
+  const char* kKeyPassword = "test";
+
+  *cert_file = JoinPathSegments(dir, "test.cert");
+  *key_file = JoinPathSegments(dir, "test.key");
+  *key_password = kKeyPassword;
+
+  RETURN_NOT_OK(WriteStringToFile(Env::Default(), kCert, *cert_file));
+  RETURN_NOT_OK(WriteStringToFile(Env::Default(), kKey, *key_file));
+  return Status::OK();
+}
+
 } // namespace security
 } // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/2c052fcb/src/kudu/security/test/test_certs.h
----------------------------------------------------------------------
diff --git a/src/kudu/security/test/test_certs.h 
b/src/kudu/security/test/test_certs.h
index bafc11b..863c141 100644
--- a/src/kudu/security/test/test_certs.h
+++ b/src/kudu/security/test/test_certs.h
@@ -14,11 +14,13 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-//
-
 #pragma once
 
+#include <string>
+
 namespace kudu {
+class Status;
+
 namespace security {
 namespace ca {
 
@@ -40,6 +42,14 @@ extern const char kCaExpiredCert[];
 extern const char kCaExpiredPrivateKey[];
 
 } // namespace ca
+
+// Creates a matching SSL certificate and private key file in 'dir', returning
+// their paths in '*cert_file' and '*key_file'. The password associated with
+// the private key is stored in '*key_password'.
+Status CreateTestSSLCerts(const std::string& dir,
+                          std::string* cert_file,
+                          std::string* key_file,
+                          std::string* key_password);
+
 } // namespace security
 } // namespace kudu
-

http://git-wip-us.apache.org/repos/asf/kudu/blob/2c052fcb/src/kudu/server/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/kudu/server/CMakeLists.txt b/src/kudu/server/CMakeLists.txt
index 1cdb0f8..25ce3df 100644
--- a/src/kudu/server/CMakeLists.txt
+++ b/src/kudu/server/CMakeLists.txt
@@ -102,5 +102,5 @@ endif()
 # server_process tests
 #########################################
 
-set(KUDU_TEST_LINK_LIBS server_process ${KUDU_MIN_TEST_LIBS})
+set(KUDU_TEST_LINK_LIBS server_process security-test ${KUDU_MIN_TEST_LIBS})
 ADD_KUDU_TEST(webserver-test)

http://git-wip-us.apache.org/repos/asf/kudu/blob/2c052fcb/src/kudu/server/webserver-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/server/webserver-test.cc 
b/src/kudu/server/webserver-test.cc
index 5ad7aa4..28c25ed 100644
--- a/src/kudu/server/webserver-test.cc
+++ b/src/kudu/server/webserver-test.cc
@@ -23,6 +23,7 @@
 #include "kudu/gutil/strings/substitute.h"
 #include "kudu/gutil/strings/util.h"
 #include "kudu/gutil/stringprintf.h"
+#include "kudu/security/test/test_certs.h"
 #include "kudu/server/default-path-handlers.h"
 #include "kudu/server/webserver.h"
 #include "kudu/util/curl_util.h"
@@ -35,20 +36,32 @@ DECLARE_int32(webserver_max_post_length_bytes);
 
 namespace kudu {
 
+namespace {
+void SetSslOptions(WebserverOptions* opts) {
+  string password;
+  CHECK_OK(security::CreateTestSSLCerts(GetTestDataDirectory(),
+                                        &opts->certificate_file,
+                                        &opts->private_key_file,
+                                        &password));
+  opts->private_key_password_cmd = strings::Substitute("echo $0", password);
+}
+} // anonymous namespace
+
 class WebserverTest : public KuduTest {
  public:
   WebserverTest() {
     static_dir_ = GetTestPath("webserver-docroot");
     CHECK_OK(env_->CreateDir(static_dir_));
+  }
+
+  virtual void SetUp() OVERRIDE {
+    KuduTest::SetUp();
 
     WebserverOptions opts;
     opts.port = 0;
     opts.doc_root = static_dir_;
+    if (use_ssl()) SetSslOptions(&opts);
     server_.reset(new Webserver(opts));
-  }
-
-  virtual void SetUp() OVERRIDE {
-    KuduTest::SetUp();
 
     AddDefaultPathHandlers(server_.get());
     ASSERT_OK(server_->Start());
@@ -60,6 +73,9 @@ class WebserverTest : public KuduTest {
   }
 
  protected:
+  // Overridden by subclasses.
+  virtual bool use_ssl() const { return false; }
+
   EasyCurl curl_;
   faststring buf_;
   gscoped_ptr<Webserver> server_;
@@ -68,6 +84,11 @@ class WebserverTest : public KuduTest {
   string static_dir_;
 };
 
+class SslWebserverTest : public WebserverTest {
+ protected:
+  bool use_ssl() const override { return true; }
+};
+
 TEST_F(WebserverTest, TestIndexPage) {
   ASSERT_OK(curl_.FetchURL(strings::Substitute("http://$0/";, addr_.ToString()),
                            &buf_));
@@ -78,6 +99,16 @@ TEST_F(WebserverTest, TestIndexPage) {
   ASSERT_STR_CONTAINS(buf_.ToString(), "memz");
 }
 
+TEST_F(SslWebserverTest, TestSSL) {
+  // We use a self-signed cert, so we need to disable cert verification in 
curl.
+  curl_.set_verify_peer(false);
+
+  ASSERT_OK(curl_.FetchURL(strings::Substitute("https://$0/";, 
addr_.ToString()),
+                           &buf_));
+  // Should have expected title.
+  ASSERT_STR_CONTAINS(buf_.ToString(), "Kudu");
+}
+
 TEST_F(WebserverTest, TestDefaultPaths) {
   // Test memz
   ASSERT_OK(curl_.FetchURL(strings::Substitute("http://$0/memz?raw=1";, 
addr_.ToString()),
@@ -161,4 +192,49 @@ TEST_F(WebserverTest, TestStaticFiles) {
   ASSERT_EQ("Remote error: HTTP 403", s.ToString());
 }
 
+// Various tests for failed webserver startup cases.
+class WebserverNegativeTests : public KuduTest {
+ protected:
+
+  // Tries to start the webserver, expecting it to fail.
+  // 'func' is used to set webserver options before starting it.
+  template<class OptsFunc>
+  void ExpectFailedStartup(const OptsFunc& func) {
+    WebserverOptions opts;
+    opts.port = 0;
+    func(&opts);
+    Webserver server(opts);
+    Status s = server.Start();
+    ASSERT_FALSE(s.ok());
+  }
+};
+
+TEST_F(WebserverNegativeTests, BadCertFile) {
+  ExpectFailedStartup([this](WebserverOptions* opts) {
+      SetSslOptions(opts);
+      opts->certificate_file = "/dev/null";
+    });
+}
+
+TEST_F(WebserverNegativeTests, BadKeyFile) {
+  ExpectFailedStartup([this](WebserverOptions* opts) {
+      SetSslOptions(opts);
+      opts->private_key_file = "/dev/null";
+    });
+}
+
+TEST_F(WebserverNegativeTests, WrongPassword) {
+  ExpectFailedStartup([this](WebserverOptions* opts) {
+      SetSslOptions(opts);
+      opts->private_key_password_cmd = "echo wrong_pass";
+    });
+}
+
+TEST_F(WebserverNegativeTests, BadPasswordCommand) {
+  ExpectFailedStartup([this](WebserverOptions* opts) {
+      SetSslOptions(opts);
+      opts->private_key_password_cmd = "/bin/false";
+    });
+}
+
 } // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/2c052fcb/src/kudu/server/webserver.cc
----------------------------------------------------------------------
diff --git a/src/kudu/server/webserver.cc b/src/kudu/server/webserver.cc
index ceca077..0b8cdfd 100644
--- a/src/kudu/server/webserver.cc
+++ b/src/kudu/server/webserver.cc
@@ -39,11 +39,13 @@
 #include "kudu/gutil/strings/numbers.h"
 #include "kudu/gutil/strings/split.h"
 #include "kudu/gutil/strings/stringpiece.h"
+#include "kudu/gutil/strings/strip.h"
 #include "kudu/util/env.h"
 #include "kudu/util/flag_tags.h"
 #include "kudu/util/locks.h"
 #include "kudu/util/logging.h"
 #include "kudu/util/net/net_util.h"
+#include "kudu/util/subprocess.h"
 #include "kudu/util/url-coding.h"
 #include "kudu/util/version_info.h"
 
@@ -125,12 +127,12 @@ Status Webserver::BuildListenSpec(string* spec) const {
 Status Webserver::Start() {
   LOG(INFO) << "Starting webserver on " << http_address_;
 
-  vector<const char*> options;
+  vector<string> options;
 
   if (static_pages_available()) {
     LOG(INFO) << "Document root: " << opts_.doc_root;
     options.push_back("document_root");
-    options.push_back(opts_.doc_root.c_str());
+    options.push_back(opts_.doc_root);
     options.push_back("enable_directory_listing");
     options.push_back("no");
   } else {
@@ -140,12 +142,34 @@ Status Webserver::Start() {
   if (IsSecure()) {
     LOG(INFO) << "Webserver: Enabling HTTPS support";
     options.push_back("ssl_certificate");
-    options.push_back(opts_.certificate_file.c_str());
+    options.push_back(opts_.certificate_file);
+
+    if (!opts_.private_key_file.empty()) {
+      options.push_back("ssl_private_key");
+      options.push_back(opts_.private_key_file);
+
+      string key_password;
+      if (!opts_.private_key_password_cmd.empty()) {
+        vector<string> argv = strings::Split(opts_.private_key_password_cmd, " 
",
+                                             strings::SkipEmpty());
+        if (argv.empty()) {
+          return Status::RuntimeError("invalid empty private key password 
command");
+        }
+        string stderr;
+        Status s = Subprocess::Call(argv, "" /* stdin */, &key_password, 
&stderr);
+        if (!s.ok()) {
+          return Status::RuntimeError("failed to run private key password 
command", stderr);
+        }
+        StripTrailingWhitespace(&key_password);
+      }
+      options.push_back("ssl_private_key_password");
+      options.push_back(key_password); // maybe empty if not configured.
+    }
   }
 
   if (!opts_.authentication_domain.empty()) {
     options.push_back("authentication_domain");
-    options.push_back(opts_.authentication_domain.c_str());
+    options.push_back(opts_.authentication_domain);
   }
 
   if (!opts_.password_file.empty()) {
@@ -158,21 +182,17 @@ Status Webserver::Start() {
     }
     LOG(INFO) << "Webserver: Password file is " << opts_.password_file;
     options.push_back("global_passwords_file");
-    options.push_back(opts_.password_file.c_str());
+    options.push_back(opts_.password_file);
   }
 
   options.push_back("listening_ports");
   string listening_str;
   RETURN_NOT_OK(BuildListenSpec(&listening_str));
-  options.push_back(listening_str.c_str());
+  options.push_back(listening_str);
 
   // Num threads
   options.push_back("num_threads");
-  string num_threads_str = SimpleItoa(opts_.num_worker_threads);
-  options.push_back(num_threads_str.c_str());
-
-  // Options must be a NULL-terminated list
-  options.push_back(nullptr);
+  options.push_back(std::to_string(opts_.num_worker_threads));
 
   // mongoose ignores SIGCHLD and we need it to run kinit. This means that 
since
   // mongoose does not reap its own children CGI programs must be avoided.
@@ -184,11 +204,18 @@ Status Webserver::Start() {
   callbacks.begin_request = &Webserver::BeginRequestCallbackStatic;
   callbacks.log_message = &Webserver::LogMessageCallbackStatic;
 
+  // Options must be a NULL-terminated list of C strings.
+  vector<const char*> c_options;
+  for (const auto& opt : options) {
+    c_options.push_back(opt.c_str());
+  }
+  c_options.push_back(nullptr);
+
   // To work around not being able to pass member functions as C callbacks, we 
store a
   // pointer to this server in the per-server state, and register a static 
method as the
   // default callback. That method unpacks the pointer to this and calls the 
real
   // callback.
-  context_ = sq_start(&callbacks, reinterpret_cast<void*>(this), &options[0]);
+  context_ = sq_start(&callbacks, reinterpret_cast<void*>(this), 
&c_options[0]);
 
   // Restore the child signal handler so wait() works properly.
   signal(SIGCHLD, sig_chld);

http://git-wip-us.apache.org/repos/asf/kudu/blob/2c052fcb/src/kudu/server/webserver_options.cc
----------------------------------------------------------------------
diff --git a/src/kudu/server/webserver_options.cc 
b/src/kudu/server/webserver_options.cc
index 0730f06..020dc9d 100644
--- a/src/kudu/server/webserver_options.cc
+++ b/src/kudu/server/webserver_options.cc
@@ -51,9 +51,21 @@ DEFINE_bool(webserver_enable_doc_root, true,
     "If true, webserver may serve static files from the webserver_doc_root");
 TAG_FLAG(webserver_enable_doc_root, advanced);
 
+// SSL configuration.
 DEFINE_string(webserver_certificate_file, "",
-    "The location of the debug webserver's SSL certificate file, in .pem 
format. If "
+    "The location of the debug webserver's SSL certificate file, in PEM 
format. If "
     "empty, webserver SSL support is not enabled");
+DEFINE_string(webserver_private_key_file, "", "The full path to the private 
key used as a"
+    " counterpart to the public key contained in --ssl_server_certificate. If "
+    "--ssl_server_certificate is set, this option must be set as well.");
+DEFINE_string(webserver_private_key_password_cmd, "", "A Unix command whose 
output "
+    "returns the password used to decrypt the Webserver's certificate private 
key file "
+    "specified in --webserver_private_key_file. If the PEM key file is not "
+    "password-protected, this command will not be invoked. The output of the 
command "
+    "will be truncated to 1024 bytes, and then all trailing whitespace will be 
trimmed "
+    "before it is used to decrypt the private key");
+
+
 DEFINE_string(webserver_authentication_domain, "",
     "Domain used for debug webserver authentication");
 DEFINE_string(webserver_password_file, "",
@@ -84,6 +96,8 @@ WebserverOptions::WebserverOptions()
     doc_root(FLAGS_webserver_doc_root),
     enable_doc_root(FLAGS_webserver_enable_doc_root),
     certificate_file(FLAGS_webserver_certificate_file),
+    private_key_file(FLAGS_webserver_private_key_file),
+    private_key_password_cmd(FLAGS_webserver_private_key_password_cmd),
     authentication_domain(FLAGS_webserver_authentication_domain),
     password_file(FLAGS_webserver_password_file),
     num_worker_threads(FLAGS_webserver_num_worker_threads) {

http://git-wip-us.apache.org/repos/asf/kudu/blob/2c052fcb/src/kudu/server/webserver_options.h
----------------------------------------------------------------------
diff --git a/src/kudu/server/webserver_options.h 
b/src/kudu/server/webserver_options.h
index ddc5079..d192637 100644
--- a/src/kudu/server/webserver_options.h
+++ b/src/kudu/server/webserver_options.h
@@ -33,6 +33,8 @@ struct WebserverOptions {
   std::string doc_root;
   bool enable_doc_root;
   std::string certificate_file;
+  std::string private_key_file;
+  std::string private_key_password_cmd;
   std::string authentication_domain;
   std::string password_file;
   uint32_t num_worker_threads;

http://git-wip-us.apache.org/repos/asf/kudu/blob/2c052fcb/src/kudu/util/curl_util.cc
----------------------------------------------------------------------
diff --git a/src/kudu/util/curl_util.cc b/src/kudu/util/curl_util.cc
index d3265b8..c3bfdb6 100644
--- a/src/kudu/util/curl_util.cc
+++ b/src/kudu/util/curl_util.cc
@@ -68,6 +68,9 @@ Status EasyCurl::DoRequest(const std::string& url,
                            faststring* dst) {
   CHECK_NOTNULL(dst)->clear();
 
+  RETURN_NOT_OK(TranslateError(curl_easy_setopt(
+      curl_, CURLOPT_SSL_VERIFYPEER,
+      static_cast<long>(verify_peer_)))); // NOLINT(runtime/int)
   RETURN_NOT_OK(TranslateError(curl_easy_setopt(curl_, CURLOPT_URL, 
url.c_str())));
   RETURN_NOT_OK(TranslateError(curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, 
WriteCallback)));
   RETURN_NOT_OK(TranslateError(curl_easy_setopt(curl_, CURLOPT_WRITEDATA,

http://git-wip-us.apache.org/repos/asf/kudu/blob/2c052fcb/src/kudu/util/curl_util.h
----------------------------------------------------------------------
diff --git a/src/kudu/util/curl_util.h b/src/kudu/util/curl_util.h
index b7e2da9..d758975 100644
--- a/src/kudu/util/curl_util.h
+++ b/src/kudu/util/curl_util.h
@@ -48,6 +48,12 @@ class EasyCurl {
                    const std::string& post_data,
                    faststring* dst);
 
+  // Set whether to verify the server's SSL certificate in the case of an HTTPS
+  // connection.
+  void set_verify_peer(bool verify) {
+    verify_peer_ = verify;
+  }
+
  private:
   // Do a request. If 'post_data' is non-NULL, does a POST.
   // Otherwise, does a GET.
@@ -55,6 +61,10 @@ class EasyCurl {
                    const std::string* post_data,
                    faststring* dst);
   CURL* curl_;
+
+  // Whether to verify the server certificate.
+  bool verify_peer_ = true;
+
   DISALLOW_COPY_AND_ASSIGN(EasyCurl);
 };
 

Reply via email to